Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Colliders

Colliders define the physical shape of objects for collision detection.

Collider Shapes

Ball (Sphere)

#![allow(unused)]
fn main() {
world.core.set_collider(entity, ColliderComponent::new_ball(0.5));
}

Cuboid (Box)

#![allow(unused)]
fn main() {
world.core.set_collider(entity, ColliderComponent::new_cuboid(1.0, 0.5, 1.0));
}

Capsule

Perfect for characters:

#![allow(unused)]
fn main() {
world.core.set_collider(entity, ColliderComponent::new_capsule(1.0, 0.3));
}

Cylinder

#![allow(unused)]
fn main() {
world.core.set_collider(entity, ColliderComponent {
    shape: ColliderShape::Cylinder {
        half_height: 1.0,
        radius: 0.5,
    },
    ..Default::default()
});
}

Cone

#![allow(unused)]
fn main() {
world.core.set_collider(entity, ColliderComponent {
    shape: ColliderShape::Cone {
        half_height: 1.0,
        radius: 0.5,
    },
    ..Default::default()
});
}

Triangle Mesh

For complex static geometry:

#![allow(unused)]
fn main() {
let vertices: Vec<[f32; 3]> = mesh.vertices.iter()
    .map(|v| v.position)
    .collect();

let indices: Vec<[u32; 3]> = mesh.indices
    .chunks(3)
    .map(|c| [c[0], c[1], c[2]])
    .collect();

world.core.set_collider(entity, ColliderComponent {
    shape: ColliderShape::TriMesh { vertices, indices },
    ..Default::default()
});
}

Heightfield

For terrain:

#![allow(unused)]
fn main() {
let heights: Vec<f32> = generate_height_grid(64, 64);

world.core.set_collider(entity, ColliderComponent {
    shape: ColliderShape::HeightField {
        nrows: 64,
        ncols: 64,
        heights,
        scale: [100.0, 50.0, 100.0],
    },
    ..Default::default()
});
}

Compound

Multiple shapes combined:

#![allow(unused)]
fn main() {
let body_collider = ColliderComponent::new_cuboid(0.5, 0.1, 0.5);
let head_collider = ColliderComponent::new_ball(0.3);
}

Physics Materials

Friction, restitution, and density are fields directly on ColliderComponent:

#![allow(unused)]
fn main() {
world.core.set_collider(entity, ColliderComponent::new_cuboid(0.5, 0.5, 0.5)
    .with_friction(0.5)       // Sliding resistance (0 = ice, 1 = rubber)
    .with_restitution(0.3)    // Bounciness (0 = no bounce, 1 = perfect bounce)
    .with_density(1.0));      // Mass per unit volume
}

Material Examples

#![allow(unused)]
fn main() {
// Ice
let ice = ColliderComponent::new_cuboid(0.5, 0.5, 0.5)
    .with_friction(0.05)
    .with_restitution(0.1)
    .with_density(0.9);

// Rubber
let rubber = ColliderComponent::new_ball(0.5)
    .with_friction(0.9)
    .with_restitution(0.8)
    .with_density(1.1);

// Metal
let metal = ColliderComponent::new_cuboid(0.5, 0.5, 0.5)
    .with_friction(0.4)
    .with_restitution(0.2)
    .with_density(7.8);
}

Collision Groups

Filter which objects collide:

#![allow(unused)]
fn main() {
use rapier3d::prelude::*;

// Define groups
const GROUP_PLAYER: Group = Group::GROUP_1;
const GROUP_ENEMY: Group = Group::GROUP_2;
const GROUP_PROJECTILE: Group = Group::GROUP_3;
const GROUP_WORLD: Group = Group::GROUP_4;

// Player collides with enemies and world, not own projectiles
let player_filter = CollisionGroups::new(
    GROUP_PLAYER,
    GROUP_ENEMY | GROUP_WORLD,
);
}

Sensor Colliders

Detect overlaps without physical response:

#![allow(unused)]
fn main() {
// Create a trigger zone
if let Some(collider) = world.resources.physics.collider_set.get_mut(handle.into()) {
    collider.set_sensor(true);
}
}

Sensor collisions are reported through the collision events system (see below).

Collision Events

Query collision pairs each frame. collision_events() returns &[CollisionEvent]:

#![allow(unused)]
fn main() {
fn run_systems(&mut self, world: &mut World) {
    for event in world.resources.physics.collision_events() {
        match event.kind {
            CollisionEventKind::Started => {
                handle_collision_start(event.entity_a, event.entity_b);
            }
            CollisionEventKind::Stopped => {
                handle_collision_end(event.entity_a, event.entity_b);
            }
        }
    }
}
}

Each CollisionEvent has:

#![allow(unused)]
fn main() {
pub struct CollisionEvent {
    pub entity_a: Entity,
    pub entity_b: Entity,
    pub kind: CollisionEventKind,
    pub is_sensor: bool,
}
}

Convex Decomposition

For complex shapes on dynamic bodies, use convex decomposition:

#![allow(unused)]
fn main() {
use rapier3d::prelude::*;

// This creates multiple convex pieces from a concave mesh
let decomposed = SharedShape::convex_decomposition(&vertices, &indices);
}

Performance Tips

ShapePerformanceUse Case
BallFastestRolling objects
CuboidFastCrates, buildings
CapsuleFastCharacters
CylinderMediumBarrels, pillars
ConvexMediumSimple props
TrimeshSlowStatic terrain only
CompoundVariesComplex dynamic objects
  • Prefer primitive shapes over meshes
  • Use trimesh only for static geometry
  • Compound colliders are better than multiple entities
  • Simplify collision geometry vs visual geometry