Garry's Mod Wiki

Revision Difference

Entity_Driving#514594

<cat>Dev.GameModes</cat>⤶ Entity driving allows you to control other entities. This isn't particularly only cars - but any move type you want. It could be a simple ball that rolls around, noclip, a helicopter, a dog. It doesn't have to even move. Anything you can control.⤶ ⤶ ## The Drive Class⤶ ⤶ A drive class is simply a table of functions. Drive classes exist on both the client and the server - and are fully predicted - which means that driving feels the same in multiplayer as in singleplayer. Nice and responsive.⤶ ⤶ ## A simple Drive Class⤶ ⤶ Here's a simple drive class⤶ ⤶ ⤶ ```⤶ -- Derive from drive_base (see lua/drive/drive_base.lua )⤶ DEFINE_BASECLASS( "drive_base" )⤶ ⤶ drive.Register( "drive_example", ⤶ {⤶ --⤶ -- Calculates the view when driving the entity⤶ --⤶ CalcView = function( self, view )⤶ ⤶ --⤶ -- Use the utility method on drive_base.lua to give us a 3rd person view⤶ --⤶ self:CalcView_ThirdPerson( view, 100, 2, { self.Entity } )⤶ view.angles.roll = 0⤶ ⤶ end,⤶ ⤶ --⤶ -- Called before each move. You should use your entity and cmd to ⤶ -- fill mv with information you need for your move. ⤶ --⤶ StartMove = function( self, mv, cmd )⤶ -- Set observer mode to chase, so the entity will be drawn.⤶ self.Player:SetObserverMode( OBS_MODE_CHASE )⤶ --⤶ -- Update move position and velocity from our entity⤶ --⤶ mv:SetOrigin( self.Entity:GetNetworkOrigin() )⤶ mv:SetVelocity( self.Entity:GetAbsVelocity() )⤶ ⤶ end,⤶ ⤶ --⤶ -- Runs the actual move. On the client when there's ⤶ -- prediction errors this can be run multiple times.⤶ -- You should try to only change mv.⤶ --⤶ Move = function( self, mv )⤶ ⤶ --⤶ -- Set up a speed, go faster if shift is held down⤶ --⤶ local speed = 0.0005 * FrameTime()⤶ if ( mv:KeyDown( IN_SPEED ) ) then speed = 0.005 * FrameTime() end⤶ ⤶ --⤶ -- Get information from the movedata⤶ --⤶ local ang = mv:GetMoveAngles()⤶ local pos = mv:GetOrigin()⤶ local vel = mv:GetVelocity()⤶ ⤶ --⤶ -- Add velocities. This can seem complicated. On the first line⤶ -- we're basically saying get the forward vector, then multiply it⤶ -- by our forward speed (which will be > 0 if we're holding W, < 0 if we're⤶ -- holding S and 0 if we're holding neither) - and add that to velocity.⤶ -- We do that for right and up too, which gives us our free movement.⤶ --⤶ vel = vel + ang:Forward() * mv:GetForwardSpeed() * speed⤶ vel = vel + ang:Right() * mv:GetSideSpeed() * speed⤶ vel = vel + ang:Up() * mv:GetUpSpeed() * speed⤶ ⤶ --⤶ -- We don't want our velocity to get out of hand so we apply⤶ -- a little bit of air resistance. If no keys are down we apply⤶ -- more resistance so we slow down more.⤶ --⤶ if ( math.abs(mv:GetForwardSpeed()) + math.abs(mv:GetSideSpeed()) + math.abs(mv:GetUpSpeed()) < 0.1 ) then⤶ vel = vel * 0.90⤶ else⤶ vel = vel * 0.99⤶ end⤶ ⤶ --⤶ -- Add the velocity to the position (this is the movement)⤶ --⤶ pos = pos + vel⤶ ⤶ --⤶ -- We don't set the newly calculated values on the entity itself⤶ -- we instead store them in the movedata. These get applied in F inishMove.⤶ --⤶ mv:SetVelocity( vel )⤶ mv:SetOrigin( pos )⤶ ⤶ end,⤶ ⤶ --⤶ -- The move is finished. Use mv to set the new positions⤶ -- on your entities/players.⤶ --⤶ FinishMove = function( self, mv )⤶ ⤶ --⤶ -- Update our entity!⤶ --⤶ self.Entity:SetNetworkOrigin( mv:GetOrigin() )⤶ self.Entity:SetAbsVelocity( mv:GetVelocity() )⤶ self.Entity:SetAngles( mv:GetMoveAngles() )⤶ ⤶ --⤶ -- If we have a physics object update that too. But only on the server.⤶ --⤶ if ( SERVER && IsValid( self.Entity:GetPhysicsObject() ) ) then⤶ ⤶ self.Entity:GetPhysicsObject():EnableMotion( true )⤶ self.Entity:GetPhysicsObject():SetPos( mv:GetOrigin() )⤶ self.Entity:GetPhysicsObject():Wake()⤶ self.Entity:GetPhysicsObject():EnableMotion( false )⤶ ⤶ end⤶ ⤶ end,⤶ ⤶ }, "drive_base" )⤶ ```⤶ ⤶ This might look complicated but it's actually pretty simple. It gives you a simple velocity based noclip mode.⤶ ⤶ Some things to note. When you register your drive mode you should register them in order. What this means is that if you're deriving your mode from a class you should register that class first.⤶ ⤶ ## Making the player drive⤶ ⤶ To make the player start driving, on the server call⤶ ⤶ ⤶ ```⤶ drive.PlayerStartDriving( player, ent, drivemode )⤶ ```⤶ ⤶ ⤶ for example⤶ ⤶ ⤶ ```⤶ drive.PlayerStartDriving( player, ent, "drive_mydriveclass" )⤶ ```⤶ ⤶ ⤶ To stop driving you can call⤶ ⤶ ⤶ ```⤶ drive.PlayerStopDriving( player )⤶ ```⤶ ⤶ ⤶ Or if you're inside the actual drive class you can call⤶ ⤶ ⤶ ```⤶ self:Stop()⤶ ```⤶ ⤶ ⤶ ## Example⤶ ⤶ For a more in depth example you can look at **lua/drive/drive_sandbox.lua**. This is the drive mode used to drive around entities in Sandbox mode.⤶ ⤶ It's worth checking out **lua/drive/drive_base.lua** too - as this is the common base shared by all of the default drive modes and displays all the hookable functions.⤶ ⤶ ## Internals⤶ ⤶ The class is created once and cached. On server it's pretty much guaranteed that this object will stick around for the duration of the drive - so it's safe to store variables on it. But on the client if there's lag - or prediction errors - it's possible that the object will be re-created.⤶ ⤶ The controlling player and entity aren't passed to each function because they're saved on the object itself. They're accessible via:⤶ ⤶ ⤶ ```⤶ self.Player⤶ self.Entity⤶ ```⤶ ⤶ ⤶ ⤶ ⤶