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:
platform_handle = world.add_body(
rp.RigidBody.kinematic_position_based(translation=(0.0, 1.0, 0.0)),
colliders=[rp.Collider.cuboid(2.0, 0.1, 2.0)],
)
for step in range(200):
# Setting the next position of the platform, once per timestep.
time = step * world.integration_parameters.dt
platform = world.rigid_bodies[platform_handle]
platform.set_next_kinematic_translation((math.sin(time) * 2.0, 1.0, 0.0))
world.step()
Alternately, if the platform's rigid-body was created with the RigidBodyType.KINEMATIC_VELOCITY_BASED type, then it
needs to be controlled by setting its velocity (with its linvel and angvel properties) directly instead of a target position.
Don't move a kinematic body by setting its position directly (e.g. with its translation property): 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 (ContactModificationContext.update_as_oneway_platform):
class OneWayPlatform:
def __init__(self, platform):
self.platform = platform
def modify_solver_contacts(self, context):
# 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.
if context.collider1 == self.platform:
allowed_local_n1 = (0.0, 1.0, 0.0)
else:
allowed_local_n1 = (0.0, -1.0, 0.0)
context.update_as_oneway_platform(allowed_local_n1, 0.1)
The hooks are then given to the world, by assigning them to its physics_hooks property, and the platform's collider is flagged as asking for them:
# The hooks are only called for the colliders asking for them.
platform_collider = world.rigid_bodies[platform_handle].colliders[0]
world.colliders[platform_collider].active_hooks = rp.ActiveHooks.MODIFY_SOLVER_CONTACTS
world.physics_hooks = OneWayPlatform(platform_collider)
world.step()
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 (with ContactModificationContext.set_tangent_velocity).
class ConveyorBelt:
def __init__(self, belt):
self.belt = belt
def modify_solver_contacts(self, context):
# The belt drags the objects along the world-space z axis at 12 m/s. The tangent
# velocity is the one of the surface of the second collider relative to the first
# one, hence the flip when the belt is the second collider.
if context.collider1 == self.belt:
context.set_tangent_velocity((0.0, 0.0, 12.0))
else:
context.set_tangent_velocity((0.0, 0.0, -12.0))
The tangent velocity given to set_tangent_velocity is the velocity of the surface of the second collider of the pair
relative to the surface of the first one, therefore it must be flipped when the belt happens to be the second one.
Like the one-way platform, the belt's collider must be given the ActiveHooks.MODIFY_SOLVER_CONTACTS active hooks.
Keep in mind as well that the hooks aren't called for the contacts of sleeping rigid-bodies: an object put to sleep
on the belt before it started moving must be woken up, e.g., with RigidBody.wake_up.