MODDING TUTORIALS
Entity Driving
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 )
--
--
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
No comments:
Post a Comment