The real-time simulation of rigid-bodies subjected to forces and contacts is the main feature of a physics engine for video-games, robotics, or animation. Rigid-bodies are typically used to simulate the dynamics of non-deformable solids as well as to integrate the trajectory of solids which velocities are controlled by the user (e.g. moving platforms). On the other hand, rigid-bodies are not enough to simulate, e.g., cars, ragdolls, or robotic systems, as those use-cases require adding restrictions on the relative motion between their parts with joints.
In this chapter, we first show how to initialize a physics world which will construct all that is to be physically simulated and will drive the physics simulation.
Then we introduce colliders which are geometric shapes responsible for generating contacts or simulating sensors. Colliders are attached to rigid-bodies which are responsible for the simulation of the object trajectory under various forces including gravity.
First, let's see what is the minimum set of elements that must be setup to have an empty physical simulation up and running:
- Example 2D
- Example 3D
PhysicsPipeline is responsible for executing the whole simulation: collision detection as well as the simulation of
physical phenomena: gravity, contact forces, joints, etc. All the state contained by the
PhysicsPipeline are workspace
buffers so you are free to discard it at any time without breaking the simulation. For example, it is perfectly fine
to re-instantiate a new
PhysicsPipeline at each game loop iteration. However this will be less efficient due to
repeated allocations of the workspace buffers. All the actually useful simulation-related data are stored in the arguments
- The gravity is a global force affecting all dynamic rigid-bodies with non-zero mass.
IntegrationParameterscontains elements to configure the constraint solver.
BroadPhaseperforms coarse AABB-based collision detections.
NarrowPhaseperforms precise contact point determination based on the collision pairs generated by the broad-phase.
RigidBodySetcontains all the rigid-bodies part of the simulation.
ColliderSetcontains all the colliders part of the simulation.
JointSetcontains all the joint constraints part of the simulation.
event_handlerwill collect all the events generated by the physics pipeline. Setting this to
()simply ignores these events.
All the mentioned sets associate a unique handle to each element they contain. These handles are generational indices so they don't suffer from the ABA problem.
Each call to
pipeline.step(...) will advance the simulation by a time equal to seconds, which is a good default if your
application has a refresh rate of 60Hz. The length of this timestep can be retrieved by
integration_parameters.dt() and modified
integration_parameters.set_dt(value). Note that if you use the
recommanded International System of Units (SI units), this
timestep is to be given in seconds. Keep in mind that the timestep length strongly affects the accuracy of the simulation:
the smaller the timestep, the more accurate the simulation will be.
Changes of timesteps during the simulation should be avoided as much as possible since they may introduce numerical instabilities or reduce the constraints solvers convergence.
Note that we are free to replace the
pipeline by a fresh instance of
PhysicsPipeline whenever we want. This is not the
case for the other mutable components. Once at least one
pipeline.step has been called with these components, they will be
inter-dependent. Replacing any of them (in subsequent calls to
.step) with new/other instances may cause unexpected
behaviors. We are working to ensure this is no longer an issue in the future.
A rigid-body is the simplest type of body supported by rapier. It can be seen as the aggregation of a position, orientation, and mass properties (rotational inertia tensor, mass, and center of mass). It does not hold any information regarding its shape which can optionally be specified by attaching one or multiple colliders to it. A rigid-body with no collider, but with a non-zero mass, will be affected by all the forces the world is aware of, including joints attached to it, but not by contacts (because it does not have any shape that can be collided to).
Creating a rigid-body
A rigid-body can only be created by a
RigidBodyBuilder structure that is based on the builder pattern:
The following example shows all the possible setters that can be called to customize the rigid-body being built. The input values are just random so using this example as-is will not lead to a useful result.
- Example 2D
- Example 3D
All the properties are optional. The only calls that are required are
initialize the builder, and
.build() to actually build the rigid body.
Typically, the inertia and center of mass are automatically set to the inertia and center of mass resulting from the shapes of the colliders attached to the rigid-body.
The rigid-body handle
Once the rigid body is created it must be added to a
RigidBodySet. A body set is a container that maps a unique handle to
Upon insertion, the body set will return a handle to the rigid body. This will allow us to get a reference to this body later if needed:
No two bodies in the same body set will share the same handle. This handle is what you should store for future addressing of this rigid body as it is required by various operations including: attaching colliders or joints to the rigid-body.
Any body can have one of three different statuses identified by the
BodyStatus::Dynamic: It indicates the body is affected by external forces, inertial forces (gyroscopic, coriolis, etc.) and contacts.
BodyStatus::Static: Indicates the body cannot move. It acts as if it has an infinite mass and will not be affected by any force. It will continue to collide with dynamic bodies but not with static nor with kinematic bodies. This is typically used for the ground or for temporarily freezing a body.
BodyStatus::Kinematic: Indicates the body position must not be altered by the physics engine. The user is free to set any position and the body velocity will be deduced at each update accordingly to ensure a realistic behavior of dynamic bodies in contact with it. This is typically used for moving platforms, elevators, etc.
Colliders represent the geometric shapes that generate contacts and proximity events when they touch.
Creating a collider
A collider is built by a
ColliderBuilder structure following the builder pattern:
The following example shows all the possible setters that can be called to customize the collider being built. The input values are just random so using this example as-is will not lead to a useful result.
- Example 2D
- Example 3D
The collider handle
Once the collider is created it must be added to a collider set. A collider set is a container that maps a unique handle to a collider:
When inserting the collider, it will be attached to the rigid-body specified by is handle
Upon insertion, the collider set will return a handle to the collider. This will allow you to get a reference to this
collider if needed later:
No two colliders in the same collider set will share the same handle. This handle is what you should store for future addressing of this collider.