Getting started
Install the package, run a simulation, get the results back into your game. Five minutes.
Install
dotnet add package Box3D.NET
The native Box3D binary for your platform comes with it. Nothing else to install. .NET 8 or later, on Windows, Linux and macOS, x64 and arm64.
Android and iOS are supported too, with two caveats worth knowing up front:
- Android works exactly like the desktop platforms — add the package and
the right
libbox3d.sois packed into your.apk. Only 64-bit ABIs are shipped (arm64-v8aandx86_64), which covers every publishable device and the emulator. - iOS requires .NET 10 or later. Apple does not allow an application to load a dynamic library that is not a signed framework, so Box3D is linked into your application instead of loaded from a file, and that needs a target framework the .NET 8 iOS workload can no longer provide.
Neither is exercised on a real device in CI — see Platforms for exactly what is and is not verified. If you ship on a phone, test on a phone.
Your first simulation
using System.Numerics;
using Box3D;
using var world = new PhysicsWorld();
Body ground = world.CreateStaticBody(new Vector3(0.0f, -0.5f, 0.0f));
ground.AddBox(new Box(new Vector3(50.0f, 0.5f, 50.0f)));
Body ball = world.CreateDynamicBody(new Vector3(0.0f, 10.0f, 0.0f));
ball.AddSphere(new Sphere(0.5f));
for (int frame = 0; frame < 120; frame++)
{
world.Step(1.0f / 60.0f);
}
Console.WriteLine(ball.Position); // resting on the ground
That is the whole model:
PhysicsWorld |
Contains everything and is the thing you step. Dispose it when finished. |
Body |
Position, orientation, velocity. No shape of its own. |
Shape |
Collision geometry attached to a body. A body can carry several. |
Step |
Advances time. Everything happens here. |
A body with no shapes has no mass and no geometry, so create the body and then attach to it.
Three kinds of body
world.CreateStaticBody(position); // never moves. Level geometry.
world.CreateDynamicBody(position); // falls, collides, responds to forces.
world.CreateKinematicBody(position); // you move it; it pushes others aside.
Static bodies are effectively free, so use them for everything that does not move. See Bodies.
Step at a fixed rate
world.Step(1.0f / 60.0f); // yes
world.Step(deltaTime); // no
A varying step makes the simulation irreproducible and hurts stability. Decouple physics from your frame rate with an accumulator:
const float FixedStep = 1.0f / 60.0f;
float accumulator = 0.0f;
void Update(float deltaTime)
{
// Clamp, or a long frame spirals into a hundred catch-up steps.
accumulator += MathF.Min(deltaTime, 0.25f);
while (accumulator >= FixedStep)
{
world.Step(FixedStep);
accumulator -= FixedStep;
}
}
The simulation step covers sub-steps, sleeping and continuous collision.
Get the results back
Every body, shape and joint carries a ulong that Box3D stores and never reads.
Put an entity id or an array index in it, then read results back through it:
Body body = world.CreateDynamicBody(spawn);
body.AddBox(Box.Cube(0.5f));
body.UserData = entityId;
world.Step(FixedStep);
foreach (BodyMoveEvent moved in world.Events.BodyMoves)
{
ref Transform transform = ref transforms[moved.Body.UserData];
transform.Position = moved.Position;
transform.Rotation = moved.Rotation;
}
BodyMoves is one contiguous list of what actually moved, which beats asking
every body every frame. See Events.
Where next
| Bodies · Shapes | The two types you will use most |
| Queries | Ray casts, overlaps, shape casts |
| Events | Contacts, sensors, what moved |
| Collision filtering | What collides with what |
| Joints | Hinges, sliders, wheels, and six more |
| Memory and ownership | What to dispose, and in which order |
| Examples | Sixteen runnable samples |
Three things that catch people out
Non-finite values throw. body.LinearVelocity = new Vector3(float.NaN, 0, 0)
raises an ArgumentException instead of being accepted. Box3D validates with
assertions that release builds compile out, so without the check one NaN spreads
until every body in the world reads NaN.
Create bodies where they belong. Creating at the origin and moving afterwards costs nearly twice as much, and more once shapes are attached.
Meshes, height fields and baked compounds are static only. Box3D only generates their contacts against static bodies. See Terrain and meshes.