Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/facepunch/sbox-public/llms.txt

Use this file to discover all available pages before exploring further.

CharacterController provides collision-constrained movement without a full physics simulation. Unlike Rigidbody, it is not affected by forces — it only moves when you explicitly call Move() or MoveTo(). This makes it ideal for player characters where you want precise, predictable motion. This page covers all properties and methods available on CharacterController.
CharacterController requires no collider sibling — it builds its own capsule trace shape internally using Radius and Height.

Adding a character controller

var go = new GameObject( "Player" );
var cc = go.Components.Create<CharacterController>();
cc.Radius = 16;
cc.Height = 64;
cc.StepHeight = 18;

Properties

Capsule shape

Radius
float
default:"16"
The radius of the controller’s capsule in world units. Range 0–200.
Height
float
default:"64"
The total height of the controller’s capsule in world units. Range 0–200.
BoundingBox
BBox
Read-only. The local bounding box of the capsule derived from Radius and Height. Spans (-Radius, -Radius, 0) to (Radius, Radius, Height).

Movement parameters

StepHeight
float
default:"18"
Maximum height of a step or ledge the controller can climb automatically. Range 0–50.
GroundAngle
float
default:"45"
Maximum surface angle (in degrees) that is considered walkable ground. Surfaces steeper than this are treated as walls. Range 0–90.
Acceleration
float
default:"10"
The acceleration rate used by Accelerate(). Higher values make the character reach target velocity faster. Range 0–64.
Bounciness
float
default:"0.3"
How much velocity is reflected when the controller hits a wall mid-air. 0 stops dead on impact; 1 is a full elastic bounce. Range 0–1.

State

Velocity
Vector3
Current velocity of the character in world units per second. Synchronized over the network via [Sync]. Set this directly to override movement.
IsOnGround
bool
true when the controller is standing on a walkable surface. Synchronized via [Sync]. Updated every time Move() is called.
GroundObject
GameObject
The GameObject the controller is currently standing on, or null if airborne.
GroundCollider
Collider
The specific Collider the controller is standing on, or null if airborne.

Collision filtering

UseCollisionRules
bool
default:"false"
When true, collision filtering uses the project’s collision rules applied to the GameObject’s tags. When false, IgnoreLayers is used instead.
IgnoreLayers
TagSet
Tags of objects the controller should pass through. Only active when UseCollisionRules is false.

Methods

Movement

Move()
void
Advances the character by the current Velocity over Time.Delta, resolving collisions and stepping up surfaces. Calls CategorizePosition() internally to update IsOnGround. Call this once per OnFixedUpdate.
protected override void OnFixedUpdate()
{
    Controller.Velocity += Vector3.Down * 800 * Time.Delta; // gravity
    Controller.Move();
}
MoveTo( Vector3 targetPosition, bool useStep )
void
Slides from the current position toward targetPosition using trace-based movement. Useful for scripted motion such as ladder climbing. Pass useStep = true to apply step climbing.
Accelerate( Vector3 vector )
void
Adds velocity toward vector, capped by the current Acceleration rate scaled by Time.Delta. You do not need to multiply by delta time yourself.
// Move forward at up to 200 u/s
Controller.Accelerate( wishDir * 200 );
ApplyFriction( float frictionAmount, float stopSpeed = 140 )
void
Decelerates the controller. frictionAmount scales how quickly speed bleeds off. stopSpeed sets the minimum effective speed used in the friction calculation, which prevents the character from skating at very low speeds.
if ( Controller.IsOnGround )
{
    Controller.ApplyFriction( 6.0f );
}
Punch( in Vector3 amount )
void
Disconnects the controller from the ground and adds amount directly to Velocity. Use this for jumping.
if ( Controller.IsOnGround && Input.Pressed( "Jump" ) )
{
    Controller.Punch( Vector3.Up * 300 );
}

Tracing

TraceDirection( Vector3 direction )
SceneTraceResult
Runs a trace from the controller’s current position in direction using the controller’s capsule shape and collision filters. Useful for checking clearance before moving.
var result = Controller.TraceDirection( Vector3.Forward * 64 );
if ( result.Hit )
{
    Log.Info( "Wall ahead" );
}

Ground detection

After every call to Move(), the controller runs an internal ground detection step (CategorizePosition). It traces a short distance downward from the controller’s position:
  • If IsOnGround is already true, the trace extends downward by StepHeight to handle step-down while walking.
  • If IsOnGround is false, the trace extends only 0.1 units (enough to detect a surface but not snap down from the air).
A surface is classified as ground only when the trace hits and the hit normal is within GroundAngle degrees of straight up. If the controller is moving upward faster than 40 u/s, no ground detection runs that tick.

A minimal player controller

1

Create the component

Add CharacterController to your player GameObject and set capsule dimensions in the editor or on OnAwake.
2

Apply input in OnFixedUpdate

Read input, compute a wish direction, and call Accelerate and ApplyFriction. Then add gravity to Velocity. Finally call Move().
[RequireComponent] CharacterController Controller { get; set; }

protected override void OnFixedUpdate()
{
    // Gravity
    if ( !Controller.IsOnGround )
        Controller.Velocity += Vector3.Down * 800 * Time.Delta;

    // Jump
    if ( Controller.IsOnGround && Input.Pressed( "Jump" ) )
        Controller.Punch( Vector3.Up * 320 );

    // Horizontal movement
    var wishDir = Input.AnalogMove.WithZ( 0 ).Normal;
    Controller.Accelerate( wishDir * 200 );

    if ( Controller.IsOnGround )
        Controller.ApplyFriction( 6 );

    Controller.Move();
}
3

Rotate the camera separately

The controller only moves the GameObject — rotate the camera GameObject based on mouse input independently.
Call ApplyFriction only when IsOnGround is true. Applying friction in the air will kill velocity after a jump.
CharacterController uses [Sync] on Velocity and IsOnGround. In a networked game, only the owner should write these properties.

Character controller guide

Step-by-step walkthrough for building a complete player movement system.

Physics components

Rigidbody, colliders, and joints for physics-simulated objects.

Input API

Reading keyboard, mouse, and controller input.

Networking overview

How ownership and sync variables work in multiplayer.

Build docs developers (and LLMs) love