Caution: The character mover API is experimental.
Box3D provides a geometric character mover: a capsule that exists outside the rigid body simulation and is driven entirely by application code. Because it is not a simulated body, you have full control over movement without fighting the solver, at the cost of having to resolve collisions yourself. This is the style of mover common in first-person shooters and games with precise platforming.
Character Mover
The Mover Capsule
The mover is represented by a b3Capsule in world space. A capsule is a good shape for movement because its round profile slides smoothly along edges and corners without snagging. Give the capsule a meaningful radius — a very thin capsule behaves poorly with the encroachment handling described below.
float radius
The radius of the hemispheres.
Definition types.h:1913
b3Vec3 center1
Local center of the first hemisphere.
Definition types.h:1907
b3Vec3 center2
Local center of the second hemisphere.
Definition types.h:1910
A solid capsule can be viewed as two hemispheres connected by a rectangle.
Definition types.h:1905
A 3D vector.
Definition math_functions.h:41
The mover has no explicit rotation handling. Slow rotation can be made to work by updating the capsule each frame, but rapid spinning is not supported.
Workflow
Each frame:
- Compute a desired translation from input and physics (gravity, velocity).
- Call b3World_CastMover to find how far the mover can actually travel.
- Move the capsule by the returned fraction of the desired translation.
- Call b3World_CollideMover to gather all contact planes at the new position.
- Filter and assemble the planes into a b3CollisionPlane array.
- Call b3SolvePlanes to compute a corrected position delta.
- Apply the delta and call b3ClipVector to remove velocity components that push into surfaces.
Swept Motion: b3World_CastMover
void* context
);
bool b3MoverFilterFcn(b3ShapeId shapeId, void *context)
Used to filter shapes for shape casting character movers.
Definition types.h:1853
World id references a world instance. This should be treated as an opaque handle.
Definition id.h:38
b3Vec3 b3Pos
In single precision mode these types are the same.
Definition math_functions.h:90
The query filter is used to filter collisions between queries and shapes.
Definition types.h:1284
float b3World_CastMover(b3WorldId worldId, b3Pos origin, const b3Capsule *mover, b3Vec3 translation, b3QueryFilter filter, b3MoverFilterFcn *fcn, void *context)
Cast a capsule mover through the world.
This casts the capsule through the world and returns the fraction [0, 1] of translation that can be traveled before hitting something. Multiply the translation by this fraction to get the safe displacement.
The cast handles encroachment: when the mover starts out touching a surface, the inner line segment of the capsule may move slightly into that surface without the full capsule generating an overlap. This lets the mover slide along walls and floors it is already resting against without stopping dead at the first frame.
The optional b3MoverFilterFcn callback lets you exclude specific shapes from the cast (e.g., ignore triggers, teammates, or one-way platforms):
Shape id references a shape instance. This should be treated as an opaque handle.
Definition id.h:53
b3World_CastMover is intended for movement, not for gathering contact information. Use b3World_CollideMover for that.
Contact Planes: b3World_CollideMover
void* context
);
bool b3PlaneResultFcn(b3ShapeId shapeId, const b3PlaneResult *plane, int planeCount, void *context)
Used to collect collision planes for character movers.
Definition types.h:1849
void b3World_CollideMover(b3WorldId worldId, b3Pos origin, const b3Capsule *mover, b3QueryFilter filter, b3PlaneResultFcn *fcn, void *context)
Collide a capsule mover with the world, gathering collision planes that can be fed to b3SolvePlanes.
This gathers all surfaces the mover is touching or overlapping and delivers them via the callback:
int planeCount,
void* context
);
The plane between a character mover and a shape.
Definition types.h:1800
b3PlaneResult carries the contact plane and a world-space contact point:
b3Plane plane
Outward pointing plane.
Definition types.h:1802
b3Vec3 point
Closest point on the shape. May not be unique.
Definition types.h:1805
A plane.
Definition math_functions.h:113
The mover is treated as having fixed rotation, so only planes are needed — no full contact manifolds.
Per-Body Query: b3Body_CollideMover
When you need to test the mover against a single specific body (useful for moving platforms or elevators):
int planeCapacity,
);
int b3Body_CollideMover(b3BodyId bodyId, b3BodyPlaneResult *bodyPlanes, int planeCapacity, b3Pos origin, const b3Capsule *mover, b3QueryFilter filter, b3WorldTransform bodyTransform)
Collide a character mover with a specific body using a specified body transform.
Body plane result for movers.
Definition types.h:1839
Body id references a body instance. This should be treated as an opaque handle.
Definition id.h:45
b3Transform b3WorldTransform
In single precision mode these types are the same.
Definition math_functions.h:93
Returns the number of planes written into bodyPlanes. Each entry pairs the originating b3ShapeId with a b3PlaneResult.
Resolving Overlap: b3SolvePlanes
Convert the raw b3PlaneResult values from b3World_CollideMover into b3CollisionPlane entries and call:
int count
);
float pushLimit
Setting this to FLT_MAX makes the plane as rigid as possible.
Definition types.h:1818
bool clipVelocity
Indicates if b3ClipVector should clip against this plane. Should be false for soft collision.
Definition types.h:1824
b3Plane plane
The collision plane between the mover and some shape.
Definition types.h:1814
float push
The push on the mover determined by b3SolvePlanes. Usually in meters.
Definition types.h:1821
b3PlaneSolverResult b3SolvePlanes(b3Vec3 targetDelta, b3CollisionPlane *planes, int count)
Solves the position of a mover that satisfies the given collision planes.
These are collision planes that can be fed to b3SolvePlanes.
Definition types.h:1812
Result returned by b3SolvePlanes.
Definition types.h:1829
b3SolvePlanes finds the position delta closest to targetDelta that satisfies all planes. The result contains the corrected delta and an iterationCount.
pushLimit controls softness. FLT_MAX gives a rigid surface. A smaller value allows the mover to push through — useful for other players, enemies, or doors that should yield but not fully block.
Velocity Clipping: b3ClipVector
After resolving position, clip the mover's velocity so it does not keep accelerating into blocked directions:
b3Vec3 b3ClipVector(b3Vec3 vector, const b3CollisionPlane *planes, int count)
Clips the velocity against the given collision planes.
This removes the components of vector that push into any plane where clipVelocity is true. Without this, velocity accumulates every frame the mover is pressed against a surface.
Putting It Together
b3Pos origin = b3Pos_zero;
float fraction =
b3World_CastMover(worldId, origin, &mover, translation, filter, NULL, NULL);
#define MAX_PLANES 16
int planeCount = 0;
velocity =
b3ClipVector(velocity, collisionPlanes, planeCount);
b3Vec3 delta
The final relative translation.
Definition types.h:1831
b3Vec3 b3Add(b3Vec3 a, b3Vec3 b)
Vector addition.
Definition math_functions.h:223
b3Vec3 b3MulSV(float s, b3Vec3 a)
s * a
Definition math_functions.h:347
The Mover sample in the samples application shows a complete implementation including acceleration, friction, jumping, and a pogo stick.