menu_bookDocumentation
PlayerController: Architecture, Ground Detection, Jump System, WishVelocity Sync, and Physics Step Integration
PlayerController: Architecture, Ground Detection, and Jump System
PlayerController is the built-in s&box character movement component. It uses a Rigidbody internally and implements IScenePhysicsEvents to hook into the physics step.Key Properties
CSHARP
// Body configuration
[Property] float BodyRadius = 16f;
[Property] float BodyHeight = 72f;
[Property] float BodyMass = 500f;
// Movement speeds
[Property] float WalkSpeed = 110f;
[Property] float RunSpeed = 320f;
[Property] float DuckedSpeed = 70f;
[Property] float JumpSpeed = 300f;
[Property] float DuckedHeight = 36f;
// Physics tuning
[Property] float BrakePower = 1f; // friction when slowing down on ground
[Property] float AirFriction = 0.1f; // friction when airborne
// State
bool IsOnGround // true if standing on something
bool IsAirborne // !IsOnGround && !IsSwimming && !IsClimbing
bool IsClimbing
bool IsSwimming
bool IsDucking
Vector3 Velocity // actual velocity minus ground velocity
Vector3 WishVelocity // [Sync] desired velocity
Vector3 GroundVelocity // velocity of the ground beneath usGround Detection
Ground detection runs in PostPhysicsStep() via CategorizeGround(). It traces a small capsule downward from the player's position. The trace uses a radius scale that shrinks from 1.0 to 0.7 to handle edge cases.
CSHARP
// Ground state
GameObject GroundObject // the object we're standing on (null if airborne)
Component GroundComponent // the collider/rigidbody we're standing on
Surface GroundSurface // physics surface material
float GroundFriction // friction of the ground surface
bool GroundIsDynamic // is the ground a dynamic physics object?
TimeSince TimeSinceGrounded // time since last on ground
TimeSince TimeSinceUngrounded // time since last off groundJump System
CSHARP
// Built-in jump (called from InputJump)
controller.Jump( Vector3.Up * JumpSpeed );Physics Step Integration
CSHARP
void IScenePhysicsEvents.PrePhysicsStep()
{
UpdateBody();
if ( !IsProxy )
{
Mode.AddVelocity();
Mode.PrePhysicsStep();
}
}
void IScenePhysicsEvents.PostPhysicsStep()
{
Velocity = Body.Velocity - GroundVelocity;
UpdateGroundVelocity();
RestoreStep();
Mode?.PostPhysicsStep();
CategorizeGround();
ChooseBestMoveMode();
}WishVelocity is Synced
WishVelocity is marked [Sync], so it's automatically replicated from the owner to all clients. This allows proxies to see the player's intended movement direction for animation purposes.OnJumped RPC
CSHARP
[Rpc.Broadcast( NetFlags.OwnerOnly | NetFlags.Unreliable )]
public void OnJumped()
{
// Triggers jump animation on all clients
// OwnerOnly = only the owner can call this
// Unreliable = fire-and-forget, may drop
}
Was this helpful?