Revision Difference
Entity_Driving#561118
<cat>Dev.GameModes</cat>⤶
<cat>Dev.Game</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
```