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⤶
```⤶
⤶
⤶
⤶
⤶