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.
s&box uses a scene-level PhysicsWorld accessible via Scene.PhysicsWorld. You create dynamic objects by attaching a Rigidbody component alongside one or more collider components. For spatial queries you use the fluent trace builder returned by Scene.PhysicsWorld.Trace.
Physics traces
A trace (or raycast) sweeps a shape through the physics world and returns everything it hits. The builder pattern lets you compose exactly the query you need.
Ray trace
The simplest trace casts an infinitely thin ray from one point to another:
var tr = Scene.PhysicsWorld.Trace
.Ray( WorldPosition, WorldPosition + WorldRotation.Forward * 1000f )
.Run();
if ( tr.Hit )
{
Log.Info( $"Hit {tr.Body?.GameObject?.Name} at {tr.HitPosition}" );
Log.Info( $"Surface: {tr.Surface?.ResourceName}" );
Log.Info( $"Normal: {tr.Normal}" );
}
Sphere sweep
var tr = Scene.PhysicsWorld.Trace
.Sphere( radius: 16f, from: start, to: end )
.Run();
Box sweep
var extents = new Vector3( 32, 32, 64 );
var tr = Scene.PhysicsWorld.Trace
.Box( extents, from: start, to: end )
.Run();
Capsule sweep
var capsule = new Capsule( Vector3.Zero, Vector3.Up * 72f, 16f );
var tr = Scene.PhysicsWorld.Trace
.Capsule( capsule, from: start, to: end )
.Run();
RunAll — multiple hits
var hits = Scene.PhysicsWorld.Trace
.Ray( start, end )
.RunAll();
foreach ( var hit in hits )
{
Log.Info( $"Hit {hit.Body?.GameObject?.Name} at fraction {hit.Fraction}" );
}
Filtering the trace
Chain filter methods before calling Run():
var tr = Scene.PhysicsWorld.Trace
.Ray( start, end )
.IgnoreStatic() // Skip static world geometry
.HitTriggers() // Include trigger volumes
.Run();
| Method | Effect |
|---|
IgnoreStatic() | Exclude static bodies |
IgnoreDynamic() | Exclude dynamic bodies |
IgnoreKeyframed() | Exclude keyframed bodies |
HitTriggers() | Include trigger shapes in results |
HitTriggersOnly() | Hit only trigger shapes |
PhysicsTraceResult fields
| Field | Type | Description |
|---|
Hit | bool | Whether the trace hit anything |
StartedSolid | bool | The trace started inside a solid |
HitPosition | Vector3 | World position of the hit |
EndPosition | Vector3 | Final position after travel |
Normal | Vector3 | Surface normal at the hit point |
Fraction | float | How far [0..1] the trace travelled |
Body | PhysicsBody | The physics body that was hit |
Shape | PhysicsShape | The specific shape that was hit |
Surface | Surface | Material/surface properties |
Tags | string[] | Tags on the hit shape |
Distance | float | Distance between start and end |
Rigidbody component
Add a Rigidbody component to give a GameObject physics simulation. It requires at least one collider component (e.g. BoxCollider, SphereCollider, CapsuleCollider) on the same or a child GameObject.
Velocity and forces
var rb = Components.Get<Rigidbody>();
// Read or set linear velocity directly
rb.Velocity = Vector3.Up * 500f;
// Read or set angular velocity (degrees per second around each axis)
rb.AngularVelocity = new Vector3( 0, 0, 90f );
// Apply a continuous force (scaled by physics delta time)
rb.ApplyForce( Vector3.Up * 9800f );
// Apply force at a world-space position (creates torque)
rb.ApplyForceAt( worldPoint, Vector3.Right * 500f );
// Apply angular force (torque)
rb.ApplyTorque( new Vector3( 0, 0, 1000f ) );
// Apply an instantaneous impulse (not scaled by delta time)
rb.ApplyImpulse( Vector3.Up * 300f );
// Impulse at a world position
rb.ApplyImpulseAt( worldPoint, Vector3.Forward * 200f );
Mass and damping
// Override mass (0 = calculate from shapes)
rb.MassOverride = 50f;
// How quickly linear motion decays
rb.LinearDamping = 0.1f;
// How quickly rotation decays
rb.AngularDamping = 0.5f;
// Enable or disable gravity for this body
rb.Gravity = true;
rb.GravityScale = 2.0f; // double gravity
Sleeping
Physics bodies automatically sleep after being still to save performance:
// Force wake-up
rb.Sleeping = false;
// Force to sleep immediately
rb.Sleeping = true;
Accessing the underlying PhysicsBody
var body = rb.PhysicsBody;
if ( body.IsValid() )
{
var velocity = body.Velocity;
var mass = body.Mass;
var bounds = body.GetBounds(); // BBox in world space
}
PhysicsBody API
You can also work with PhysicsBody directly for lower-level control:
var body = rb.PhysicsBody;
// Position and rotation
body.Position = new Vector3( 0, 0, 100f );
body.Rotation = Rotation.FromYaw( 45f );
// Velocity
body.Velocity = Vector3.Forward * 200f;
body.AngularVelocity = Vector3.Up * 180f;
// Forces and impulses
body.ApplyForce( Vector3.Up * 9800f );
body.ApplyImpulse( Vector3.Forward * 500f );
body.ApplyTorque( Vector3.Up * 100f );
// Gravity
body.GravityEnabled = false;
body.GravityScale = 0.5f;
// Damping
body.LinearDamping = 0.05f;
body.AngularDamping = 0.1f;
// CCD — enable for fast-moving objects like bullets
body.EnhancedCcd = true;
Collision detection
Implement ICollisionListener on a component to receive collision callbacks:
public sealed class DamageOnCollision : Component, Component.ICollisionListener
{
void ICollisionListener.OnCollisionStart( Collision collision )
{
var speed = collision.Contact.NormalSpeed;
var other = collision.Other.GameObject;
var point = collision.Contact.Point;
var normal = collision.Contact.Normal;
if ( speed > 300f )
{
Log.Info( $"Hard collision with {other?.Name} at {speed:0}u/s" );
}
}
void ICollisionListener.OnCollisionUpdate( Collision collision ) { }
void ICollisionListener.OnCollisionEnd( CollisionStop stop ) { }
}
Shape management
Shapes define the collision geometry of a PhysicsBody. You can add shapes at runtime:
var body = new PhysicsBody( Scene.PhysicsWorld );
// Sphere
body.AddSphereShape( center: Vector3.Zero, radius: 16f );
// Box
body.AddBoxShape( position: Vector3.Zero, rotation: Rotation.Identity, extent: new Vector3( 32, 32, 32 ) );
// Capsule
body.AddCapsuleShape( center: Vector3.Zero, center2: Vector3.Up * 72f, radius: 16f );
// Remove all shapes
body.ClearShapes();
Practical example — shoot and detect
protected override void OnUpdate()
{
if ( !Input.Pressed( "Attack1" ) ) return;
var ray = Scene.Camera.ScreenNormalToRay( 0.5f, 0.5f );
var tr = Scene.PhysicsWorld.Trace
.Ray( ray, distance: 2000f )
.IgnoreDynamic()
.Run();
if ( !tr.Hit ) return;
Log.Info( $"Hit {tr.Body?.GameObject?.Name}" );
// Push the hit object
var rb = tr.Body?.Component as Rigidbody;
if ( rb is not null )
{
rb.ApplyImpulseAt( tr.HitPosition, ray.Forward * 2000f );
}
}