Common recipes
This page provides code snippets that might be relevant for typical usages of the libraries in games.
Making a moving platform
A moving platform must push the objects resting on it without being pushed back by them, which is exactly what a kinematic rigid-body does: it is moved by your own code, and the solver treats it as if it is immune to gravity and external forces.
A platform following a path is generally position-based: you give it the position it must reach at the end of the next timestep, and the engine derives the velocity needed to get there, so the objects on top of it are pushed with the right velocity:
let (platform_handle, _) = world.insert(
RigidBodyBuilder::kinematic_position_based().translation(Vector::new(0.0, 1.0, 0.0)),
ColliderBuilder::cuboid(2.0, 0.1, 2.0),
);
for step in 0..200 {
// Setting the next position of the platform, once per timestep.
let time = step as f32 * world.integration_parameters.dt;
let platform = &mut world.bodies[platform_handle];
platform.set_next_kinematic_translation(Vector::new(time.sin() * 2.0, 1.0, 0.0));
world.step();
}
Alternately, if the platform's rigid-body was created with the RigidBodyType::KinematicVelocityBased type, then it
needs to be controlled by setting its velocity directly instead of a target position.
Don't move a kinematic body by setting its position directly: this teleports it, so it goes through whatever is in the way instead of pushing it. Note as well that two kinematic bodies never collide with each other, and that a kinematic body pushing another kinematic body has no effect.
Making a one-way platform
A one-way platform lets the character pass through it from below and holds it from
above. This can be done by looking at the contacts before they reach the solver, with the
contact modification hook, and by discarding those whose
normal isn't the one the platform accepts. Rapier provides a helper function update_as_oneway_platform for that:
struct OneWayPlatform {
platform: ColliderHandle,
}
impl PhysicsHooks for OneWayPlatform {
fn modify_solver_contacts(&self, context: &mut ContactModificationContext) {
// Keep only the contacts pushing along the local +y axis of the platform; the other
// ones (the character arriving from below) are discarded. The normal is expressed in
// the frame of the first collider of the pair, hence the flip.
let allowed_local_n1 = if context.collider1 == self.platform {
Vector::Y
} else {
-Vector::Y
};
context.update_as_oneway_platform(allowed_local_n1, 0.1);
}
}
The hooks are then given to the world at each timestep, and the platform's collider is flagged as asking for them:
// The hooks are only called for the colliders asking for them.
let platform_collider = world.bodies[platform_handle].colliders()[0];
world.colliders[platform_collider].set_active_hooks(ActiveHooks::MODIFY_SOLVER_CONTACTS);
let hooks = OneWayPlatform {
platform: platform_collider,
};
world.step_with_events(&hooks, &());
The normal given to update_as_oneway_platform is expressed in the local frame of the first collider of the pair,
therefore it must be flipped when the platform happens to be the second one. Don't forget to give the platform's
collider the ActiveHooks::MODIFY_SOLVER_CONTACTS active hooks, otherwise the hook is
never called for it.
Simulating a conveyor belt
A conveyor belt is a surface that drags what rests on it without moving itself. This is modeled by an artificial surface velocity, which is set on the solver contacts using a contact modification hook.
struct ConveyorBelt;
impl PhysicsHooks for ConveyorBelt {
fn modify_solver_contacts(&self, context: &mut ContactModificationContext) {
if let Some(rigid) = context.rigid_mut() {
for contact in rigid.solver_contacts.iter_mut() {
// The belt drags the objects along the world-space z axis at 12 m/s.
contact.tangent_velocity.z = 12.0;
}
}
}
}