# How Box3D Implements Capsule-Based Character Collision Response

> Learn how Box3D's CharacterMover implements capsule-based collision response using iterative plane solving and impulse exchange for penetration-free character movement and stable dynamic interactions.

- Repository: [Erin Catto/box3d](https://github.com/erincatto/box3d)
- Tags: deep-dive
- Published: 2026-08-01

---

**Box3D's `CharacterMover` uses an iterative plane-solving algorithm that queries the physics world for capsule collisions, solves up to 8 contact planes to compute penetration-free displacement, and exchanges impulses with dynamic bodies while clipping velocity to prevent sliding into surfaces.**

The `CharacterMover` class in Erin Catto's Box3D physics engine provides robust **capsule-based character collision response** for navigating complex game environments without making the character a traditional rigid body. Found in [`samples/mover.h`](https://github.com/erincatto/box3d/blob/main/samples/mover.h) and [`samples/mover.cpp`](https://github.com/erincatto/box3d/blob/main/samples/mover.cpp), this system handles ground adherence, obstacle sliding, and dynamic object interaction through a multi-step constraint solving approach that runs independently of the main physics simulation step.

## Capsule Geometry and World Representation

The mover represents the player character as a **capsule** defined by the `b3Capsule m_capsule` member variable. This shape stores two endpoint positions and a radius, providing smooth collision detection that handles stairs and slopes better than box primitives.

The capsule's current world position is tracked through the `m_transform.p` field, which updates each frame as the solver computes safe displacement vectors. Unlike standard rigid bodies, the character exists as a kinematic probe that queries the world rather than existing as a simulated body within the broad-phase structure.

## The Five-Step Collision Response Pipeline

Each simulation frame, the `CharacterMover::Step` method executes a sophisticated collision resolution sequence that iteratively resolves contacts while respecting geometric constraints.

### 1. Gathering Collision Planes

The system begins by collecting collision data using `b3World_CollideMover`, passing the capsule geometry and a `moverFilter` callback to exclude self-collisions or specific shape categories. The engine invokes `PlaneResultFcn` for each detected contact, storing up to **8 contact planes** in the `b3CollisionPlane m_planes[]` array.

Each plane stores:
- A surface normal vector
- A penetration offset
- Push limits that constrain how far the solver can move the character along that normal

### 2. Solving Penetration Constraints

The collected planes feed into `b3SolvePlanes`, which computes a displacement vector (`b3PlaneSolverResult::delta`) that resolves all penetrations simultaneously while respecting each plane's push limit. This constraint solver ensures the character doesn't tunnel through thin geometry or over-correct into adjacent surfaces.

### 3. Iterative Movement Casting

Rather than applying the full displacement immediately, the mover performs conservative advancement using `b3World_CastMover`. The returned collision fraction scales the delta vector, allowing the character to slide along surfaces. This process repeats up to **five iterations**, re-collecting planes each time to handle new contacts that emerge during the movement.

```cpp
// Simplified iteration logic from samples/mover.cpp
for (int i = 0; i < 5; ++i) {
    // Collect planes at current position
    b3World_CollideMover(world, m_capsule, moverFilter, this);
    
    // Solve for penetration-free displacement
    b3PlaneSolverResult result = b3SolvePlanes(m_planes, planeCount);
    
    // Cast to find safe movement fraction
    float fraction = b3World_CastMover(world, m_capsule, result.delta, moverFilter);
    
    // Update position
    m_transform.p += fraction * result.delta;
    
    if (fraction >= 1.0f) break; // No collision, movement complete
}

```

### 4. Impulse Exchange with Dynamic Bodies

When the capsule contacts dynamic rigid bodies, the mover calculates response impulses based on relative normal velocity at the contact point. The system calls `b3Body_ApplyLinearImpulse` to push the struck object, while simultaneously adjusting the character's own velocity to conserve momentum. This allows players to push crates, affect ragdolls, or ride moving platforms while maintaining kinematic control.

### 5. Velocity Clipping for Surface Stability

After position resolution completes, the mover optionally performs **velocity clipping** using `b3ClipVector`. This removes any velocity component that would push the character into a collision surface within the next frame, eliminating jitter and preventing the character from sliding down gentle slopes when standing still. Pass `true` to the `clipVelocity` parameter in `Step()` to enable this stabilization.

## Practical Implementation Example

Integrating the `CharacterMover` requires initialization with a starting position and per-frame stepping with input vectors. The following pattern from [`samples/sample_character.cpp`](https://github.com/erincatto/box3d/blob/main/samples/sample_character.cpp) demonstrates typical usage:

```cpp
void SampleCharacter::Step()
{
    // Calculate world-space movement vectors from camera orientation
    b3Vec3 forward = -m_camera->GetForward();
    b3Vec3 right = m_camera->GetRight();
    forward.y = 0.0f;
    forward = b3Normalize(forward);
    
    // Convert WASD input to throttle values
    b3Vec2 throttle = {0.0f, 0.0f};
    if (IsKeyDown(KEY_W)) throttle.x += 1.0f;
    if (IsKeyDown(KEY_S)) throttle.x -= 1.0f;
    if (IsKeyDown(KEY_A)) throttle.y -= 1.0f;
    if (IsKeyDown(KEY_D)) throttle.y += 1.0f;
    
    // Step the mover with collision response enabled
    m_mover.Step(nullptr, 0, true);
}

// Initialization elsewhere
void SampleCharacter::Initialize()
{
    b3Pos startPos = b3MakePos(0.0f, 1.0f, 0.0f);
    m_mover.Initialize(this, startPos);
}

```

The `Step` method signature accepts an optional array of shapes to ignore (for custom collision filtering), a count, and the `clipVelocity` boolean that toggles post-solve velocity clipping.

## Summary

- **Capsule Geometry**: The `CharacterMover` uses `b3Capsule` for smooth collision detection against world geometry, stored in [`samples/mover.h`](https://github.com/erincatto/box3d/blob/main/samples/mover.h).
- **Plane-Based Solving**: Up to 8 collision planes are collected via `b3World_CollideMover` and resolved using `b3SolvePlanes` to compute penetration-free displacement.
- **Iterative Casting**: The solver runs up to 5 iterations, using `b3World_CastMover` to slide along surfaces and handle complex contact manifolds.
- **Dynamic Interaction**: Impulses are exchanged with hit bodies through `b3Body_ApplyLinearImpulse`, allowing the character to affect the physics world.
- **Velocity Stability**: Optional `b3ClipVector` pass removes residual penetration velocity, preventing jitter on slopes and against walls.

## Frequently Asked Questions

### Why does Box3D use a capsule shape for character collision?

The **capsule** provides mathematically smooth collision detection that handles edge cases better than boxes or cylinders. As implemented in [`samples/mover.cpp`](https://github.com/erincatto/box3d/blob/main/samples/mover.cpp), the rounded ends prevent catching on terrain seams, allow natural sliding along walls, and provide consistent normal vectors for the plane solver when navigating stairs or slopes.

### How does the CharacterMover prevent tunneling through thin geometry?

The system uses **iterative conservative advancement** through `b3World_CastMover`. Rather than teleporting the full computed displacement, the mover casts the capsule along the delta vector and scales the movement by the collision fraction. If the cast hits a surface at 20% of the distance, the mover only moves 20% and re-collects planes, ensuring the capsule never jumps through thin collision surfaces.

### What limits how many surfaces the character can collide with simultaneously?

The implementation stores contact planes in a fixed-size array `b3CollisionPlane m_planes[8]`, limiting the solver to **8 simultaneous collision constraints**. This balances performance and stability; in practice, this handles standing in corners or sliding along complex geometry without requiring dynamic memory allocation during the physics step.

### How does the mover handle standing on moving platforms?

When `b3World_CollideMover` detects contact with a dynamic body, the system calculates an impulse proportional to the relative velocity at the contact point. The mover applies this impulse to the body via `b3Body_ApplyLinearImpulse` while adjusting its own velocity to match the platform's movement. This implicit coupling allows the character to ride elevators and moving objects while maintaining independent kinematic control.