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.

The s&box physics system gives you direct access to rigid-body simulation through PhysicsBody, world-level configuration via PhysicsWorld, geometry queries through PhysicsTrace, collision shapes with PhysicsShape, and constraints using PhysicsJoint and PhysicsSpring. This page covers how to create and work with each of these types in your C# components.
The scene automatically creates and manages a PhysicsWorld for you. You only need to instantiate one yourself if you want to run a secondary, isolated simulation.

PhysicsWorld

PhysicsWorld is the container for all physics simulation. Every active scene owns one. You can access it via a component’s Scene.PhysicsWorld property.
// Adjust gravity for a low-gravity level
Scene.PhysicsWorld.Gravity = new Vector3( 0, 0, -200 );

// Enable continuous collision detection (default)
Scene.PhysicsWorld.SimulationMode = PhysicsSimulationMode.Continuous;

// Increase sub-steps for better stacking stability at low tick rates
Scene.PhysicsWorld.SubSteps = 2;

Gravity

Set Gravity as a Vector3. The default points downward along Z.

SimulationMode

Continuous (default) prevents fast objects tunnelling through thin walls. Discrete is cheaper but less accurate.

SubSteps

Break each tick into multiple steps. Higher values improve stacking stability at the cost of CPU.

SleepingEnabled

Bodies automatically sleep after a period of inactivity. Disable if you need every body to tick every frame.

Collision callbacks

Subscribe to world-level intersection events to react when any two bodies meet.
protected override void OnStart()
{
    Scene.PhysicsWorld.OnIntersectionStart = OnCollide;
}

void OnCollide( PhysicsIntersection intersection )
{
    Log.Info( $"{intersection.Self.Body.GameObject?.Name} hit {intersection.Other.Body.GameObject?.Name}" );
    Log.Info( $"Contact point: {intersection.Contact.Point}, speed: {intersection.Contact.NormalSpeed}" );
}

PhysicsBody

A PhysicsBody is a single rigid body in the simulation. Each body is made from one or more PhysicsShape primitives.

Body types

TypeBehaviour
DynamicFully simulated — responds to forces, gravity, and collisions.
KeyframedMoved programmatically; pushes dynamic objects but ignores forces.
StaticNever moves; used for world geometry.
var body = new PhysicsBody( Scene.PhysicsWorld );
body.BodyType = PhysicsBodyType.Dynamic;
body.Position = WorldPosition;
body.GravityEnabled = true;
body.Mass = 10.0f;
body.LinearDamping = 0.1f;
body.AngularDamping = 0.1f;

Adding shapes

Shapes define the collision geometry. A body supports any combination of primitives.
// Sphere
body.AddSphereShape( Vector3.Zero, radius: 16.0f );

// Box — position and rotation are relative to the body origin
body.AddBoxShape( Vector3.Zero, Rotation.Identity, new Vector3( 32, 32, 32 ) );

// Capsule — two end-cap centres plus radius
body.AddCapsuleShape( Vector3.Down * 32, Vector3.Up * 32, radius: 16.0f );

// Convex hull from point cloud
body.AddHullShape( Vector3.Zero, Rotation.Identity, points );

// Mesh — static collision only; cannot be simulated
body.AddMeshShape( vertices, indices );

Applying forces and impulses

Use impulses for discrete events like bullet impacts or explosions.
// Apply at centre of mass
body.ApplyImpulse( Vector3.Up * 500 );

// Apply at a world-space point (adds torque)
body.ApplyImpulseAt( hitPosition, Vector3.Up * 500 );

// Angular impulse (spin)
body.ApplyAngularImpulse( new Vector3( 0, 0, 100 ) );

Sleeping

Bodies automatically sleep when their speed drops below SleepThreshold. Wake a body to force it to re-simulate.
body.AutoSleep = true;
body.SleepThreshold = 5.0f; // units/sec
body.Sleeping = false;      // wake it up

Collision events per-body

You can listen for intersections on a specific body rather than the whole world.
body.EnableTouch = true;
body.OnIntersectionStart = c =>
{
    Log.Info( $"Touched {c.Other.Body.GameObject?.Name}" );
};
body.OnIntersectionEnd = c =>
{
    Log.Info( "Contact ended" );
};

Enhanced CCD and precise contacts

For fast-moving projectiles or player-like bodies enable these flags:
body.EnhancedCcd = true;      // sub-step CCD against dynamic bodies
body.PreciseContacts = true;  // recompute contacts every frame — avoids ghost collisions

PhysicsShape

A PhysicsShape represents one collision primitive attached to a PhysicsBody. Retrieve all shapes on a body via body.Shapes.
foreach ( var shape in body.Shapes )
{
    Log.Info( $"Type: {shape.ShapeType}, Trigger: {shape.IsTrigger}" );

    // Read the surface material controlling friction and collision sounds
    Log.Info( $"Surface: {shape.SurfaceMaterial}" );
}

Triggers

Set IsTrigger = true to make a shape detect overlaps without producing solid collisions.
var shape = body.AddSphereShape( Vector3.Zero, 64.0f );
shape.IsTrigger = true;
shape.EnableTouch = true;

Friction and surface velocity

shape.Friction = 0.8f;

// Conveyor belt — set a local surface velocity
shape.SurfaceVelocity = Vector3.Forward * 100.0f;

Removing a shape

shape.Remove(); // releases native resources; do not use the reference afterward

PhysicsTrace and raycasting

PhysicsWorld.Trace returns a PhysicsTraceBuilder you chain to describe the query, then call Run() or RunAll().

Ray queries

var result = Scene.PhysicsWorld.Trace
    .Ray( WorldPosition, WorldPosition + Vector3.Forward * 1000 )
    .Run();

if ( result.Hit )
{
    Log.Info( $"Hit {result.Body?.GameObject?.Name}" );
    Log.Info( $"Normal: {result.Normal}" );
    Log.Info( $"Surface: {result.Surface?.ResourceName}" );
    Log.Info( $"Fraction: {result.Fraction}" ); // 0..1 along the ray
}

Shape-cast queries

Instead of a zero-radius ray, cast a volume along a path.
var result = Scene.PhysicsWorld.Trace
    .Sphere( radius: 16.0f, from: start, to: end )
    .Run();

Filtering

// Ignore a specific tag
var result = Scene.PhysicsWorld.Trace
    .Ray( start, end )
    .IgnoreStatic()    // skip world geometry
    .Run();

// Custom per-shape callback
var result = Scene.PhysicsWorld.Trace
    .Ray( start, end )
    .Filter( shape => shape.Body != myOwnBody )
    .Run();

// Include trigger volumes
var result = Scene.PhysicsWorld.Trace
    .Ray( start, end )
    .HitTriggers()
    .Run();

PhysicsTraceResult fields

FieldTypeDescription
HitboolWhether the trace struck anything.
StartedSolidboolThe trace began inside a solid.
HitPositionVector3World position of the first contact.
EndPositionVector3Where the trace ended (same as HitPosition on a hit).
NormalVector3Surface normal at the contact point.
Fractionfloat0–1 distance along the trace.
BodyPhysicsBodyThe body that was hit.
ShapePhysicsShapeThe specific shape that was hit.
SurfaceSurfacePhysical surface properties.
Tagsstring[]Tags on the hit shape.
DistancefloatLength between start and end positions.

Returning all hits

var hits = Scene.PhysicsWorld.Trace
    .Sphere( 32.0f, start, end )
    .RunAll();

foreach ( var hit in hits )
{
    Log.Info( $"Hit at fraction {hit.Fraction}" );
}

PhysicsJoint

Joints constrain two bodies relative to each other. All joints are created via static factory methods on PhysicsJoint.

Fixed joint

Rigidly welds two bodies together, optionally with spring softness.

Spring / length joint

Keeps two bodies within a min/max distance, like a rope or elastic band.

Hinge joint

Allows rotation around a single axis, like a door or wheel axle.

Ball socket joint

Allows free rotation in any direction from a shared anchor point.

Slider joint

Constrains movement to a single linear axis within min/max limits.

Upright joint

Keeps a body aligned to a reference orientation — useful for ragdolls.

Creating a fixed joint

using Sandbox.Physics;

var joint = PhysicsJoint.CreateFixed(
    bodyA.WorldPoint( attachA ),
    bodyB.WorldPoint( attachB )
);

// Limit the force before the joint breaks
joint.Strength = 5000.0f;
joint.AngularStrength = 3000.0f;

// React when it breaks
joint.OnBreak += () => Log.Info( "Joint broke!" );

Spring joint (rope / elastic)

// Rope — no minimum, enforces maximum length
var rope = PhysicsJoint.CreateLength(
    bodyA.LocalPoint( Vector3.Zero ),
    bodyB.LocalPoint( Vector3.Zero ),
    maxLength: 200.0f
);

// Spring — bounces between min and max like a bungee cord
var spring = PhysicsJoint.CreateSpring(
    bodyA.LocalPoint( Vector3.Zero ),
    bodyB.LocalPoint( Vector3.Zero ),
    minLength: 50.0f,
    maxLength: 150.0f
);

Hinge joint

// Bodies rotate around their local X axis
var hinge = PhysicsJoint.CreateHinge(
    bodyA.LocalPoint( pivotLocal ),
    bodyB.LocalPoint( pivotLocal )
);

hinge.Collisions = false; // prevent the two bodies colliding with each other

Ball socket joint

// Anchor in world space
var ballSocket = PhysicsJoint.CreateBallSocket(
    bodyA, bodyB,
    origin: worldAnchorPoint
);

PhysicsSpring settings

PhysicsSpring configures the softness and damping of a joint. It applies to FixedJoint and can be used wherever spring parameters are accepted.
var spring = new PhysicsSpring(
    frequency: 5.0f,  // stiffness — oscillations per second
    damping:   0.7f,  // 0 = no damping, 1 = critically damped
    maximum:   1000.0f // maximum force (for weld joints)
);

Removing a joint

joint.Remove(); // releases the constraint; bodies simulate independently afterward

Complete example: physics prop

The following component creates a dynamic physics prop on OnStart, applies an explosion impulse, and logs when it collides with something.
using Sandbox;
using Sandbox.Physics;

public class PhysicsProp : Component
{
    PhysicsBody _body;

    protected override void OnStart()
    {
        _body = new PhysicsBody( Scene.PhysicsWorld );
        _body.BodyType = PhysicsBodyType.Dynamic;
        _body.Position = WorldPosition;
        _body.Component = this;
        _body.Mass = 5.0f;
        _body.AddBoxShape( Vector3.Zero, Rotation.Identity, new Vector3( 24, 24, 24 ) );
        _body.EnableTouch = true;
        _body.OnIntersectionStart = OnHit;

        // Throw upward
        _body.ApplyImpulse( Vector3.Up * 800 );
    }

    protected override void OnFixedUpdate()
    {
        // Keep the visual object in sync with the physics body
        WorldPosition = _body.GetLerpedTransform( Time.Now ).Position;
        WorldRotation = _body.Rotation;
    }

    void OnHit( PhysicsIntersection c )
    {
        Log.Info( $"Hit at {c.Contact.Point} with speed {c.Contact.NormalSpeed}" );
    }

    protected override void OnDestroy()
    {
        _body?.Remove();
    }
}
Call body.GetLerpedTransform( Time.Now ) to get a smoothly interpolated transform between physics ticks. This prevents visible jitter when the render rate is higher than the physics tick rate.

Build docs developers (and LLMs) love