Rigid-bodies
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 using joints.
Note that rigid-bodies are only responsible for the dynamics and kinematics of the solid. Colliders can be attached to a rigid-body to specify its shape and enable collision-detection. A rigid-body without collider attached to it will not be affected by contacts (because there is no shape to compute contact against).
Creation and insertion
A rigid-body is created by a RigidBodyBuilder structure that is based on the builder pattern. Then it needs
to be inserted into the physics world, i.e., into its RigidBodySet,
which is processed by the physics-pipeline and the query-pipeline.
The following example shows several 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
use rapier2d::prelude::*;
// The world that will contain our rigid-bodies.
let mut world = PhysicsWorld::new();
// Builder for a fixed rigid-body.
let _ = RigidBodyBuilder::fixed();
// Builder for a dynamic rigid-body.
let _ = RigidBodyBuilder::dynamic();
// Builder for a kinematic rigid-body controlled at the velocity level.
let _ = RigidBodyBuilder::kinematic_velocity_based();
// Builder for a kinematic rigid-body controlled at the position level.
let _ = RigidBodyBuilder::kinematic_position_based();
// Builder for a body with a status specified by an enum.
let rigid_body = RigidBodyBuilder::new(RigidBodyType::Dynamic)
// The rigid body translation.
// Default: zero vector.
.translation(Vector::new(0.0, 5.0))
// The rigid body rotation.
// Default: no rotation.
.rotation(5.0)
// The rigid body position. Will override `.translation(...)` and `.rotation(...)`.
// Default: the identity isometry.
.pose(Pose::new(Vector::new(1.0, 2.0), 0.4))
// The linear velocity of this body.
// Default: zero velocity.
.linvel(Vector::new(1.0, 2.0))
// The angular velocity of this body.
// Default: zero velocity.
.angvel(2.0)
// The scaling factor applied to the gravity affecting the rigid-body.
// Default: 1.0
.gravity_scale(0.5)
// Whether or not this body can sleep.
// Default: true
.can_sleep(true)
// Whether or not CCD is enabled for this rigid-body.
// Default: false
.ccd_enabled(false)
// All done, actually build the rigid-body.
.build();
// Insert the rigid-body into the world.
let rigid_body_handle = world.insert_body(rigid_body);
use rapier3d::prelude::*;
// The world that will contain our rigid-bodies.
let mut world = PhysicsWorld::new();
// Builder for a fixed rigid-body.
let _ = RigidBodyBuilder::fixed();
// Builder for a dynamic rigid-body.
let _ = RigidBodyBuilder::dynamic();
// Builder for a kinematic rigid-body controlled at the velocity level.
let _ = RigidBodyBuilder::kinematic_velocity_based();
// Builder for a kinematic rigid-body controlled at the position level.
let _ = RigidBodyBuilder::kinematic_position_based();
// Builder for a body with a status specified by an enum.
let rigid_body = RigidBodyBuilder::new(RigidBodyType::Dynamic)
// The rigid body translation.
// Default: zero vector.
.translation(Vector::new(0.0, 5.0, 1.0))
// The rigid body rotation.
// Default: no rotation.
.rotation(Vector::new(0.0, 0.0, 5.0))
// The rigid body position. Will override `.translation(...)` and `.rotation(...)`.
// Default: the identity isometry.
.pose(Pose::new(
Vector::new(1.0, 3.0, 2.0),
Vector::new(0.0, 0.0, 0.4),
))
// The linear velocity of this body.
// Default: zero velocity.
.linvel(Vector::new(1.0, 3.0, 4.0))
// The angular velocity of this body.
// Default: zero velocity.
.angvel(Vector::new(3.0, 0.0, 1.0))
// The scaling factor applied to the gravity affecting the rigid-body.
// Default: 1.0
.gravity_scale(0.5)
// Whether or not this body can sleep.
// Default: true
.can_sleep(true)
// Whether or not CCD is enabled for this rigid-body.
// Default: false
.ccd_enabled(false)
// All done, actually build the rigid-body.
.build();
// Insert the rigid-body into the world.
let rigid_body_handle = world.insert_body(rigid_body);
All the properties are optional. The only calls that are required are RigidBodyBuilder::new(status),
RigidBodyBuilder::fixed(), RigidBodyBuilder::dynamic(), RigidBodyBuilder::kinematic_velocity_based(),
or RigidBodyBuilder::kinematic_position_based(), to
initialize the builder, and .build() to actually build the rigid-body.
A rigid-body is created by adding the RigidBody component to an entity. Other components
like Transform, Velocity, Ccd, etc. can be added for further customization of the
rigid-body. Removing one of these optional components afterwards resets the corresponding property of the
rigid-body to its default value.
The following example shows several initialization of components to customize 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
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
commands
.spawn(RigidBody::Dynamic)
.insert(Transform::from_xyz(0.0, 5.0, 0.0))
.insert(Velocity {
linear: Vec2::new(1.0, 2.0),
angular: 0.2,
})
.insert(GravityScale(0.5))
.insert(Sleeping::disabled())
.insert(Ccd::enabled());
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
commands
.spawn(RigidBody::Dynamic)
.insert(Transform::from_xyz(0.0, 0.0, 0.0))
.insert(Velocity {
linear: Vec3::new(0.0, 2.0, 0.0),
angular: Vec3::new(0.2, 0.0, 0.0),
})
.insert(GravityScale(0.5))
.insert(Sleeping::disabled())
.insert(Ccd::enabled());
A rigid-body is created by a World.createRigidBody method. The initial state of
the rigid-body to create is described by an instance of the RigidBodyDesc class.
Each rigid-body create by the physics world is given an integer identifier rigidBody.handle. This identifier
is guaranteed to the different from any identifier of rigid-bodies still existing (or that existed) in the physics
world.
The following example shows several 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
// The world that will contain our rigid-bodies.
let world = new RAPIER.World({ x: 0.0, y: -9.81 });
// Builder for a fixed rigid-body.
let example1 = RAPIER.RigidBodyDesc.fixed();
// Builder for a dynamic rigid-body.
let example2 = RAPIER.RigidBodyDesc.dynamic();
// Builder for a kinematic rigid-body controlled at the velocity level.
let example3 = RAPIER.RigidBodyDesc.kinematicVelocityBased();
// Builder for a kinematic rigid-body controlled at the position level.
let example4 = RAPIER.RigidBodyDesc.kinematicPositionBased();
// Builder for a body with a status specified by an enum.
let rigidBodyDesc = new RAPIER.RigidBodyDesc(RAPIER.RigidBodyType.Dynamic)
// The rigid body translation.
// Default: zero vector.
.setTranslation(0.0, 5.0)
// The rigid body rotation.
// Default: no rotation.
.setRotation(5.0)
// The linear velocity of this body.
// Default: zero velocity.
.setLinvel(1.0, 2.0)
// The angular velocity of this body.
// Default: zero velocity.
.setAngvel(2.0)
// The scaling factor applied to the gravity affecting the rigid-body.
// Default: 1.0
.setGravityScale(0.5)
// Whether or not this body can sleep.
// Default: true
.setCanSleep(true)
// Whether or not CCD is enabled for this rigid-body.
// Default: false
.setCcdEnabled(false);
// All done, actually build the rigid-body.
let rigidBody = world.createRigidBody(rigidBodyDesc);
// The integer handle of the rigid-body can be read from the `handle` field.
let rigidBodyHandle = rigidBody.handle;
// The world that will contain our rigid-bodies.
let world = new RAPIER.World({ x: 0.0, y: -9.81, z: 0.0 });
// Builder for a fixed rigid-body.
let example1 = RAPIER.RigidBodyDesc.fixed();
// Builder for a dynamic rigid-body.
let example2 = RAPIER.RigidBodyDesc.dynamic();
// Builder for a kinematic rigid-body controlled at the velocity level.
let example3 = RAPIER.RigidBodyDesc.kinematicVelocityBased();
// Builder for a kinematic rigid-body controlled at the position level.
let example4 = RAPIER.RigidBodyDesc.kinematicPositionBased();
// Builder for a body with a status specified by an enum.
let rigidBodyDesc = new RAPIER.RigidBodyDesc(RAPIER.RigidBodyType.Dynamic)
// The rigid body translation.
// Default: zero vector.
.setTranslation(0.0, 5.0, 1.0)
// The rigid body rotation, given as a quaternion.
// Default: no rotation.
.setRotation({ w: 1.0, x: 0.0, y: 0.0, z: 0.0 })
// The linear velocity of this body.
// Default: zero velocity.
.setLinvel(1.0, 3.0, 4.0)
// The angular velocity of this body.
// Default: zero velocity.
.setAngvel({ x: 3.0, y: 0.0, z: 1.0 })
// The scaling factor applied to the gravity affecting the rigid-body.
// Default: 1.0
.setGravityScale(0.5)
// Whether or not this body can sleep.
// Default: true
.setCanSleep(true)
// Whether or not CCD is enabled for this rigid-body.
// Default: false
.setCcdEnabled(false);
// All done, actually build the rigid-body.
let rigidBody = world.createRigidBody(rigidBodyDesc);
// The integer handle of the rigid-body can be read from the `handle` field.
let rigidBodyHandle = rigidBody.handle;
A rigid-body is created from a R3RigidBodyDesc description. This plain structure must first be initialized by one of
its constructors (which set the default value of every field), then any of its fields can be modified before it is
inserted into the physics world with r3InsertRigidBody. The world copies
the description and returns the R3RigidBodyHandle identifying the new rigid-body, which is then given to every
function reading or modifying it.
The following example shows several fields that can be set 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
// The world that will contain our rigid-bodies.
R2World *world = r2NewWorld();
// Description of a fixed rigid-body.
R2RigidBodyDesc fixed_desc = r2FixedRigidBodyDesc();
// Description of a dynamic rigid-body.
R2RigidBodyDesc dynamic_desc = r2DynamicRigidBodyDesc();
// Description of a kinematic rigid-body controlled at the velocity level.
R2RigidBodyDesc kinematic_velocity_desc = r2KinematicVelocityBasedRigidBodyDesc();
// Description of a kinematic rigid-body controlled at the position level.
R2RigidBodyDesc kinematic_position_desc = r2KinematicPositionBasedRigidBodyDesc();
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
// The body type: R2_DYNAMIC, R2_FIXED, R2_KINEMATIC_VELOCITY_BASED, or R2_KINEMATIC_POSITION_BASED.
// Default: the type of the constructor used to initialize the description.
rigid_body.bodyType = R2_DYNAMIC;
// The rigid body translation.
// Default: zero vector.
rigid_body.position.translation = r2Vector(0.0, 5.0);
// The rigid body rotation.
// Default: no rotation.
rigid_body.position.rotation = r2Rotation(5.0);
// The rigid body position. Will override the translation and rotation set above.
// Default: the identity pose.
rigid_body.position = r2Pose(r2Vector(1.0, 2.0), r2Rotation(0.4));
// The linear velocity of this body.
// Default: zero velocity.
rigid_body.linvel = r2Vector(1.0, 2.0);
// The angular velocity of this body.
// Default: zero velocity.
rigid_body.angvel = 2.0;
// The scaling factor applied to the gravity affecting the rigid-body.
// Default: 1.0
rigid_body.gravityScale = 0.5;
// Whether or not this body can sleep.
// Default: 1
rigid_body.canSleep = 1;
// Whether or not CCD is enabled for this rigid-body.
// Default: 0
rigid_body.ccdEnabled = 0;
// All done, actually create the rigid-body and insert it into the world.
R2RigidBodyHandle rigid_body_handle = r2InsertRigidBody(world, &rigid_body);
// The world that will contain our rigid-bodies.
R3World *world = r3NewWorld();
// Description of a fixed rigid-body.
R3RigidBodyDesc fixed_desc = r3FixedRigidBodyDesc();
// Description of a dynamic rigid-body.
R3RigidBodyDesc dynamic_desc = r3DynamicRigidBodyDesc();
// Description of a kinematic rigid-body controlled at the velocity level.
R3RigidBodyDesc kinematic_velocity_desc = r3KinematicVelocityBasedRigidBodyDesc();
// Description of a kinematic rigid-body controlled at the position level.
R3RigidBodyDesc kinematic_position_desc = r3KinematicPositionBasedRigidBodyDesc();
R3RigidBodyDesc rigid_body = r3DynamicRigidBodyDesc();
// The body type: R3_DYNAMIC, R3_FIXED, R3_KINEMATIC_VELOCITY_BASED, or R3_KINEMATIC_POSITION_BASED.
// Default: the type of the constructor used to initialize the description.
rigid_body.bodyType = R3_DYNAMIC;
// The rigid body translation.
// Default: zero vector.
rigid_body.position.translation = r3Vector(0.0, 5.0, 1.0);
// The rigid body rotation.
// Default: no rotation.
rigid_body.position.rotation = r3RotationFromAxisAngle(r3Vector(0.0, 0.0, 1.0), 5.0);
// The rigid body position. Will override the translation and rotation set above.
// Default: the identity pose.
rigid_body.position = r3Pose(r3Vector(1.0, 3.0, 2.0), r3RotationFromAxisAngle(r3Vector(0.0, 0.0, 1.0), 0.4));
// The linear velocity of this body.
// Default: zero velocity.
rigid_body.linvel = r3Vector(1.0, 3.0, 4.0);
// The angular velocity of this body.
// Default: zero velocity.
rigid_body.angvel = r3Vector(3.0, 0.0, 1.0);
// The scaling factor applied to the gravity affecting the rigid-body.
// Default: 1.0
rigid_body.gravityScale = 0.5;
// Whether or not this body can sleep.
// Default: 1
rigid_body.canSleep = 1;
// Whether or not CCD is enabled for this rigid-body.
// Default: 0
rigid_body.ccdEnabled = 0;
// All done, actually create the rigid-body and insert it into the world.
R3RigidBodyHandle rigid_body_handle = r3InsertRigidBody(world, &rigid_body);
All the fields are optional. The only calls that are required are r3DynamicRigidBodyDesc(),
r3FixedRigidBodyDesc(), r3KinematicVelocityBasedRigidBodyDesc(), or r3KinematicPositionBasedRigidBodyDesc(), to
initialize the description, and r3InsertRigidBody to actually create the rigid-body. The rigid-body is removed
from the world with r3RemoveRigidBody: its last argument indicates if its colliders must be removed as well (if it
is 0, they are kept as colliders without parent).
A rigid-body is created by a RigidBodyBuilder that is based on the builder pattern: each of its methods returns a
new builder with the corresponding property set, so they can be chained. The builder is obtained from one of the
static methods of the RigidBody class. Then it needs to be inserted into the
physics world with PhysicsWorld.add_body (or directly into its
RigidBodySet with world.rigid_bodies.insert), which returns the RigidBodyHandle identifying the new rigid-body.
The following example shows several 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.
import rapier3d as rp
# The world that will contain our rigid-bodies.
world = rp.PhysicsWorld()
# Builder for a fixed rigid-body.
_ = rp.RigidBody.fixed()
# Builder for a dynamic rigid-body.
_ = rp.RigidBody.dynamic()
# Builder for a kinematic rigid-body controlled at the velocity level.
_ = rp.RigidBody.kinematic_velocity_based()
# Builder for a kinematic rigid-body controlled at the position level.
_ = rp.RigidBody.kinematic_position_based()
# The properties of the builder can also be given as keyword arguments.
_ = rp.RigidBody.dynamic(translation=(0.0, 5.0, 1.0), gravity_scale=0.5)
# Builder for a body with a status specified by an enum.
rigid_body = (
rp.RigidBody.new_body(rp.RigidBodyType.DYNAMIC)
# The rigid body translation.
# Default: zero vector.
.translation((0.0, 5.0, 1.0))
# The rigid body rotation, as a scaled rotation axis.
# Default: no rotation.
.rotation((0.0, 0.0, 5.0))
# The rigid body position. Will override `.translation(...)` and `.rotation(...)`.
# Default: the identity isometry.
.position(rp.Isometry3((1.0, 3.0, 2.0), rp.Rotation3.from_scaled_axis((0.0, 0.0, 0.4))))
# The linear velocity of this body.
# Default: zero velocity.
.linvel((1.0, 3.0, 4.0))
# The angular velocity of this body.
# Default: zero velocity.
.angvel((3.0, 0.0, 1.0))
# The scaling factor applied to the gravity affecting the rigid-body.
# Default: 1.0
.gravity_scale(0.5)
# Whether or not this body can sleep.
# Default: True
.can_sleep(True)
# Whether or not CCD is enabled for this rigid-body.
# Default: False
.ccd_enabled(False)
# All done, actually build the rigid-body.
.build()
)
# Insert the rigid-body into the world.
rigid_body_handle = world.add_body(rigid_body)
All the properties are optional. The only calls that are required are RigidBody.fixed(), RigidBody.dynamic(),
RigidBody.kinematic_velocity_based(), RigidBody.kinematic_position_based(), or
RigidBody.new_body(body_type), to initialize the builder. Each of them also accepts the builder properties as
keyword arguments. Calling .build() to actually build the RigidBody is optional: PhysicsWorld.add_body accepts
the builder as well, and its optional colliders argument attaches a list of colliders to the new
rigid-body in the same call.
Once inserted, the rigid-body is accessed with world.rigid_bodies[handle]. The returned RigidBody is a view of the
rigid-body stored in the world: modifying its properties (e.g. rigid_body.linvel = (1.0, 0.0, 0.0)) modifies the
simulated rigid-body directly, and a RigidBody built but not inserted yet is copied by the insertion (so modifying it
afterwards has no effect on the world). The rigid-body is removed from the world, together with its colliders and the
joints attached to it, with PhysicsWorld.remove_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. But they can also be set manually.
Rigid-body type
There are four types of rigid-bodies, identified by the RigidBodyType enumerationRigidBody componentRigidBodyType enumerationbodyType field of R3RigidBodyDescRigidBodyType enumeration
RigidBodyType::DynamicRigidBody::DynamicRigidBodyType.DynamicR3_DYNAMIC : Indicates that the body is affected by external forces and contacts.RigidBodyType.DYNAMICRigidBodyType::FixedRigidBody::FixedRigidBodyType.FixedR3_FIXED : 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 fixed nor with kinematic bodies. This is typically used for the ground or for temporarily freezing a body.RigidBodyType.FIXEDRigidBodyType::KinematicPositionBasedRigidBody::KinematicPositionBasedRigidBodyType.KinematicPositionBasedR3_KINEMATIC_POSITION_BASED : Indicates that the body position must not be altered by the physics engine. The user is free to set its next 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.RigidBodyType.KINEMATIC_POSITION_BASEDRigidBodyType::KinematicVelocityBasedRigidBody::KinematicVelocityBasedRigidBodyType.KinematicVelocityBasedR3_KINEMATIC_VELOCITY_BASED : Indicates that the body velocity must not be altered by the physics engine. The user is free to set its velocity and the next body position 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.RigidBodyType.KINEMATIC_VELOCITY_BASED
Both position-based and velocity-based kinematic bodies are mostly the same. Choosing between both is mostly a matter of preference between position-based control and velocity-based control.
Note that a fifth type exists, the soft frame, which is reserved to the rigid-bodies Rapier creates to give a frame to the soft-bodies: their pose is computed from the particles of the soft-body, and they aren't meant to be created by hand.
The type of a rigid-body can be read with r3RigidBody_BodyType (or tested with r3RigidBody_IsDynamic,
r3RigidBody_IsFixed, and r3RigidBody_IsKinematic), and modified after its creation with
r3RigidBody_SetBodyType.
The type of a rigid-body can be read, or modified after its creation, with the RigidBody.body_type property (it can
also be tested with the is_dynamic, is_fixed, and is_kinematic properties).
The whole point of kinematic bodies is to let the user have total control over their trajectory. This means that kinematic bodies will simply ignore any contact force and go through walls and the ground. In other words: if you tell the kinematic to go somewhere, it will go there, no questions asked.
Taking obstacles into account needs to be done manually either by using scene queries to detect nearby obstacles, or by using the built-in character controller.
Position
The position of a rigid-body represents its location (translation) in 2D or 3D world-space, as well as its orientation (rotation).
Pose type)Transform componentR3Vector) and its rotational part as a unit quaternion (R3Rotation) in 3D, or as an angle in radians (R2Rotation) in 2D. Both are combined into a pose (the R3Pose structure)Vec3, though a tuple of three floats is accepted as well) and its rotational part as a unit quaternion (Rotation3). Both are combined into a pose (the Isometry3 type)
The position of a rigid-body can be set when creating it. It can also be set after its creation as illustrated below.
Directly changing the position of a rigid-body is equivalent to teleporting it: this is a not a physically realistic action! Teleporting a dynamic or kinematic bodies may result in odd behaviors especially if it teleports into a space occupied by other objects. For dynamic bodies, forces, impulses, or velocity modification should be preferred. For kinematic bodies, see the discussion after the examples below.
- Example 2D
- Example 3D
/* Set the position when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
// The rigid body translation.
// Default: zero vector.
.translation(Vector::new(0.0, 5.0))
// The rigid body rotation.
// Default: no rotation.
.rotation(5.0)
// The rigid body position. Will override `.translation(...)` and `.rotation(...)`.
// Default: the identity isometry.
.pose(Pose::new(Vector::new(1.0, 2.0), 0.4))
// All done, actually build the rigid-body.
.build();
/* Set the position after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_translation(Vector::new(0.0, 5.0), true);
rigid_body.set_rotation(Rotation::new(0.2), true);
assert_eq!(rigid_body.translation(), Vector::new(0.0, 5.0));
assert_eq!(rigid_body.rotation().angle(), 0.2);
rigid_body.set_position(Pose::new(Vector::new(1.0, 2.0), 0.4), true);
assert_eq!(*rigid_body.position(), Pose::new(Vector::new(1.0, 2.0), 0.4));
/* Set the position when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
// The rigid body translation.
// Default: zero vector.
.translation(Vector::new(0.0, 5.0, 1.0))
// The rigid body rotation.
// Default: no rotation.
.rotation(Vector::new(0.2, 0.0, 0.0))
// The rigid body position. Will override `.translation(...)` and `.rotation(...)`.
// Default: the identity isometry.
.pose(Pose::new(
Vector::new(1.0, 2.0, 3.0),
Vector::new(0.2, 0.0, 0.0),
))
// All done, actually build the rigid-body.
.build();
/* Set the position after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_translation(Vector::new(0.0, 5.0, 1.0), true);
rigid_body.set_rotation(Rotation::from_scaled_axis(Vector::new(0.2, 0.0, 0.0)), true);
assert_eq!(rigid_body.translation(), Vector::new(0.0, 5.0, 1.0));
assert_eq!(
rigid_body.rotation().to_scaled_axis(),
Vector::new(0.2, 0.0, 0.0)
);
rigid_body.set_position(
Pose::new(Vector::new(1.0, 2.0, 3.0), Vector::new(0.0, 0.4, 0.0)),
true,
);
assert_eq!(
*rigid_body.position(),
Pose::new(Vector::new(1.0, 2.0, 3.0), Vector::new(0.0, 0.4, 0.0))
);
- Example 2D
- Example 3D
commands
.spawn(RigidBody::Dynamic)
.insert(Transform::from_xyz(0.0, 5.0, 0.0))
/* Change the position inside of a system. */
fn modify_body_translation(mut positions: Query<&mut Transform, With<RigidBody>>) {
for mut position in positions.iter_mut() {
position.translation.y += 0.1;
}
}
commands
.spawn(RigidBody::Dynamic)
.insert(Transform::from_xyz(0.0, 0.0, 0.0))
/* Change the position inside of a system. */
fn modify_body_translation(mut positions: Query<&mut Transform, With<RigidBody>>) {
for mut position in positions.iter_mut() {
position.translation.y += 0.1;
}
}
- Example 2D
- Example 3D
/* Set the position when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
// The rigid body translation.
// Default: zero vector.
.setTranslation(0.0, 5.0)
// The rigid body rotation.
// Default: no rotation.
.setRotation(5.0);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the position after the rigid-body creation. */
// The `true` argument makes sure the rigid-body is awake.
rigidBody.setTranslation({ x: 0.0, y: 5.0 }, true);
rigidBody.setRotation(0.2, true);
/* Set the position when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
// The rigid body translation.
// Default: zero vector.
.setTranslation(0.0, 5.0, 1.0)
// The rigid body rotation, given as a quaternion.
// Default: no rotation.
.setRotation({ w: 1.0, x: 0.0, y: 0.0, z: 0.0 });
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the position after the rigid-body creation. */
// The `true` argument makes sure the rigid-body is awake.
rigidBody.setTranslation({ x: 0.0, y: 5.0, z: 1.0 }, true);
rigidBody.setRotation({ w: 1.0, x: 0.0, y: 0.0, z: 0.0 }, true);
- Example 2D
- Example 3D
/* Set the position when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
// The rigid body translation.
// Default: zero vector.
rigid_body.position.translation = r2Vector(0.0, 5.0);
// The rigid body rotation.
// Default: no rotation.
rigid_body.position.rotation = r2Rotation(5.0);
// The rigid body position. Will override the translation and rotation set above.
// Default: the identity pose.
rigid_body.position = r2Pose(r2Vector(1.0, 2.0), r2Rotation(0.4));
/* Set the position after the rigid-body creation. */
// The last `1` argument makes sure the rigid-body is awake.
r2RigidBody_SetTranslation(rigid_body_handle, r2Vector(0.0, 5.0), 1);
r2RigidBody_SetRotation(rigid_body_handle, r2Rotation(0.2), 1);
R2Vector translation = r2RigidBody_Translation(rigid_body_handle);
R2Rotation rotation = r2RigidBody_Rotation(rigid_body_handle);
assert(translation.x == 0.0 && translation.y == 5.0);
assert(rotation.angle == (R2Real)0.2);
r2RigidBody_SetPosition(rigid_body_handle, r2Pose(r2Vector(1.0, 2.0), r2Rotation(0.4)), 1);
R2Pose position = r2RigidBody_Position(rigid_body_handle);
assert(position.translation.x == 1.0 && position.translation.y == 2.0);
assert(position.rotation.angle == (R2Real)0.4);
/* Set the position when the rigid-body is created. */
R3RigidBodyDesc rigid_body = r3DynamicRigidBodyDesc();
// The rigid body translation.
// Default: zero vector.
rigid_body.position.translation = r3Vector(0.0, 5.0, 1.0);
// The rigid body rotation.
// Default: no rotation.
rigid_body.position.rotation = r3RotationFromAxisAngle(r3Vector(1.0, 0.0, 0.0), 0.2);
// The rigid body position. Will override the translation and rotation set above.
// Default: the identity pose.
rigid_body.position = r3Pose(r3Vector(1.0, 2.0, 3.0), r3RotationFromAxisAngle(r3Vector(1.0, 0.0, 0.0), 0.2));
/* Set the position after the rigid-body creation. */
R3Rotation rotation = r3RotationFromAxisAngle(r3Vector(1.0, 0.0, 0.0), 0.2);
// The last `1` argument makes sure the rigid-body is awake.
r3RigidBody_SetTranslation(rigid_body_handle, r3Vector(0.0, 5.0, 1.0), 1);
r3RigidBody_SetRotation(rigid_body_handle, rotation, 1);
R3Vector current_translation = r3RigidBody_Translation(rigid_body_handle);
R3Rotation current_rotation = r3RigidBody_Rotation(rigid_body_handle);
assert(current_translation.x == 0.0 && current_translation.y == 5.0 && current_translation.z == 1.0);
assert(current_rotation.x == rotation.x && current_rotation.y == rotation.y &&
current_rotation.z == rotation.z && current_rotation.w == rotation.w);
R3Pose pose = r3Pose(r3Vector(1.0, 2.0, 3.0), r3RotationFromAxisAngle(r3Vector(0.0, 1.0, 0.0), 0.4));
r3RigidBody_SetPosition(rigid_body_handle, pose, 1);
R3Pose current_pose = r3RigidBody_Position(rigid_body_handle);
assert(current_pose.translation.x == 1.0 && current_pose.translation.y == 2.0 &&
current_pose.translation.z == 3.0);
assert(current_pose.rotation.x == pose.rotation.x && current_pose.rotation.y == pose.rotation.y &&
current_pose.rotation.z == pose.rotation.z && current_pose.rotation.w == pose.rotation.w);
# Set the position when the rigid-body is created.
rigid_body = (
rp.RigidBody.dynamic()
# The rigid body translation.
# Default: zero vector.
.translation((0.0, 5.0, 1.0))
# The rigid body rotation, as a scaled rotation axis.
# Default: no rotation.
.rotation((0.2, 0.0, 0.0))
# The rigid body position. Will override `.translation(...)` and `.rotation(...)`.
# Default: the identity isometry.
.position(rp.Isometry3((1.0, 2.0, 3.0), rp.Rotation3.from_scaled_axis((0.2, 0.0, 0.0))))
# All done, actually build the rigid-body.
.build()
)
# Set the position after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
# Setting these properties automatically wakes the rigid-body up.
rigid_body.translation = (0.0, 5.0, 1.0)
rigid_body.rotation = rp.Rotation3.from_scaled_axis((0.2, 0.0, 0.0))
assert rigid_body.translation == rp.Vec3(0.0, 5.0, 1.0)
assert rigid_body.rotation.scaled_axis == rp.Vec3(0.2, 0.0, 0.0)
rigid_body.position = rp.Isometry3((1.0, 2.0, 3.0), rp.Rotation3.from_scaled_axis((0.0, 0.4, 0.0)))
assert rigid_body.position == rp.Isometry3(
(1.0, 2.0, 3.0), rp.Rotation3.from_scaled_axis((0.0, 0.4, 0.0))
)
In order to move a dynamic rigid-body it is strongly discouraged to set its position directly as it may results in weird behaviors: it's as if the rigid-body teleports itself, which is a non-physical behavior. For dynamic bodies, it is recommended to either set its velocity or to apply forces or impulses.
For velocity-based kinematic bodies, it is recommended to set its velocity instead of setting its position directly. For position-based kinematic bodies, it is recommended to use the special methods:
RigidBody::set_next_kinematic_rotationRigidBody::set_next_kinematic_translation
These methods will let the physics pipeline compute the fictitious velocity of the position-based kinematic body for more realistic interactions with other rigid-bodies. These methods won't immediately modify the position of the kinematic body itself. The position of the kinematic body will be automatically set to these values during the next physics pipeline update.
For velocity-based kinematic bodies, it is recommended to set its velocity instead of setting its position directly.
For position-based kinematic bodies, it is recommended to modify its Transform (changing its velocity won’t have
any effect). This won't teleport the kinematic body immediately: the modified Transform is used as its next
kinematic position, which lets the physics engine compute the fictitious velocity of the kinematic body for more
realistic interactions with other rigid-bodies.
For velocity-based kinematic bodies, it is recommended to set its velocity instead of setting its position directly. For position-based kinematic bodies, it is recommended to use the special methods:
RigidBody.setNextKinematicRotationRigidBody.setNextKinematicTranslation
These methods will let the physics pipeline compute the fictitious velocity of the position-based kinematic body for more realistic interactions with other rigid-bodies. These methods won't immediately modify the position of the kinematic body itself. The position of the kinematic body will be automatically set to these values during the next physics pipeline update.
For velocity-based kinematic bodies, it is recommended to set its velocity instead of setting its position directly. For position-based kinematic bodies, it is recommended to use the special functions:
r3RigidBody_SetNextKinematicRotationr3RigidBody_SetNextKinematicTranslationr3RigidBody_SetNextKinematicPosition(for both at once)
These functions will let the physics pipeline compute the fictitious velocity of the position-based kinematic body for
more realistic interactions with other rigid-bodies. These functions won't immediately modify the position of the
kinematic body itself. The position of the kinematic body will be automatically set to these values during the next
physics pipeline update (the pending pose can be read with r3RigidBody_NextPosition).
For velocity-based kinematic bodies, it is recommended to set its velocity instead of setting its position directly. For position-based kinematic bodies, it is recommended to use the special methods:
RigidBody.set_next_kinematic_rotationRigidBody.set_next_kinematic_translationRigidBody.set_next_kinematic_position(for both at once)
These methods will let the physics pipeline compute the fictitious velocity of the position-based kinematic body for
more realistic interactions with other rigid-bodies. These methods won't immediately modify the position of the
kinematic body itself. The position of the kinematic body will be automatically set to these values during the next
physics pipeline update (the pending pose can be read with the RigidBody.next_position property).
platform_handle = world.add_body(rp.RigidBody.kinematic_position_based(translation=(0.0, 1.0, 0.0)))
platform = world.rigid_bodies[platform_handle]
# Move the platform up by 0.01 at each step.
for _ in range(10):
next_translation = platform.translation + rp.Vec3(0.0, 0.01, 0.0)
platform.set_next_kinematic_translation(next_translation)
# The position isn't modified until the next step.
assert platform.next_position.translation == next_translation
world.step()
Velocity
The velocity of a dynamic rigid-body controls how fast it is moving in time. The velocity is applied at the center-of-mass of the rigid-body, and is composed of two independent parts:
- The linear velocity is specified as a vector representing the direction and magnitude of the movement.
- In 3D, the angular velocity is given as a vector representing the rotation
axis multiplied by the rotation angular speed in
rad/s(axis-angle representation). In 2D, the angular velocity is given as a real representing the angular speed inrad/s.
The velocity is only relevant to dynamic rigid-bodies. It has no effect on fixed rigid-bodies, and the velocity of kinematic rigid-bodies are automatically computed at each timestep based on their next kinematic positions.
The velocity of a rigid-body is automatically updated by the physics pipeline after taking forces, contacts, and joints into account. It can be set when the rigid-body is created or after its creation:
- Example 2D
- Example 3D
/* Set the velocities when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
// The linear velocity of this body.
// Default: zero velocity.
.linvel(Vector::new(1.0, 3.0))
// The angular velocity of this body.
// Default: zero velocity.
.angvel(3.0)
// All done, actually build the rigid-body.
.build();
/* Set the velocities after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_linvel(Vector::new(1.0, 3.0), true);
rigid_body.set_angvel(3.0, true);
assert_eq!(rigid_body.linvel(), Vector::new(1.0, 3.0));
assert_eq!(rigid_body.angvel(), 3.0);
/* Set the velocities when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
// The linear velocity of this body.
// Default: zero velocity.
.linvel(Vector::new(1.0, 3.0, 4.0))
// The angular velocity of this body.
// Default: zero velocity.
.angvel(Vector::new(3.0, 0.0, 0.0))
// All done, actually build the rigid-body.
.build();
/* Set the velocities after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_linvel(Vector::new(1.0, 3.0, 4.0), true);
rigid_body.set_angvel(Vector::new(3.0, 0.0, 0.0), true);
assert_eq!(rigid_body.linvel(), Vector::new(1.0, 3.0, 4.0));
assert_eq!(rigid_body.angvel(), Vector::new(3.0, 0.0, 0.0));
- Example 2D
- Example 3D
/* Set the velocities when the rigid-body is created. */
commands.spawn(RigidBody::Dynamic).insert(Velocity {
linear: Vec2::new(0.0, 2.0),
angular: 0.4,
});
/* Set the velocities inside of a system. */
fn modify_body_velocity(mut velocities: Query<&mut Velocity>) {
for mut vel in velocities.iter_mut() {
vel.linear = Vec2::new(0.0, 2.0);
vel.angular = 0.4;
}
}
commands.spawn(RigidBody::Dynamic).insert(Velocity {
linear: Vec3::new(0.0, 2.0, 0.0),
angular: Vec3::new(0.2, 0.4, 0.8),
});
/* Set the velocities inside of a system. */
fn modify_body_velocity(mut velocities: Query<&mut Velocity>) {
for mut vel in velocities.iter_mut() {
vel.linear = Vec3::new(0.0, 2.0, 0.0);
vel.angular = Vec3::new(3.2, 0.4, 0.8);
}
}
- Example 2D
- Example 3D
/* Set the velocities when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
// The linear velocity of this body.
// Default: zero velocity.
.setLinvel(1.0, 3.0)
// The angular velocity of this body.
// Default: zero velocity.
.setAngvel(3.0);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the velocities after the rigid-body creation. */
// The `true` argument makes sure the rigid-body is awake.
rigidBody.setLinvel({ x: 1.0, y: 3.0 }, true);
rigidBody.setAngvel(3.0, true);
/* Set the velocities when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
// The linear velocity of this body.
// Default: zero velocity.
.setLinvel(1.0, 3.0, 4.0)
// The angular velocity of this body.
// Default: zero velocity.
.setAngvel({ x: 3.0, y: 0.0, z: 0.0 });
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the velocities after the rigid-body creation. */
// The `true` argument makes sure the rigid-body is awake.
rigidBody.setLinvel({ x: 1.0, y: 3.0, z: 4.0 }, true);
rigidBody.setAngvel({ x: 3.0, y: 0.0, z: 0.0 }, true);
- Example 2D
- Example 3D
/* Set the velocities when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
// The linear velocity of this body.
// Default: zero velocity.
rigid_body.linvel = r2Vector(1.0, 3.0);
// The angular velocity of this body.
// Default: zero velocity.
rigid_body.angvel = 3.0;
/* Set the velocities after the rigid-body creation. */
// The last `1` argument makes sure the rigid-body is awake.
r2RigidBody_SetLinvel(rigid_body_handle, r2Vector(1.0, 3.0), 1);
r2RigidBody_SetAngvel(rigid_body_handle, 3.0, 1);
R2Vector linvel = r2RigidBody_Linvel(rigid_body_handle);
assert(linvel.x == 1.0 && linvel.y == 3.0);
assert(r2RigidBody_Angvel(rigid_body_handle) == 3.0);
/* Set the velocities when the rigid-body is created. */
R3RigidBodyDesc rigid_body = r3DynamicRigidBodyDesc();
// The linear velocity of this body.
// Default: zero velocity.
rigid_body.linvel = r3Vector(1.0, 3.0, 4.0);
// The angular velocity of this body.
// Default: zero velocity.
rigid_body.angvel = r3Vector(3.0, 0.0, 0.0);
/* Set the velocities after the rigid-body creation. */
// The last `1` argument makes sure the rigid-body is awake.
r3RigidBody_SetLinvel(rigid_body_handle, r3Vector(1.0, 3.0, 4.0), 1);
r3RigidBody_SetAngvel(rigid_body_handle, r3Vector(3.0, 0.0, 0.0), 1);
R3Vector linvel = r3RigidBody_Linvel(rigid_body_handle);
R3AngVector angvel = r3RigidBody_Angvel(rigid_body_handle);
assert(linvel.x == 1.0 && linvel.y == 3.0 && linvel.z == 4.0);
assert(angvel.x == 3.0 && angvel.y == 0.0 && angvel.z == 0.0);
# Set the velocities when the rigid-body is created.
rigid_body = (
rp.RigidBody.dynamic()
# The linear velocity of this body.
# Default: zero velocity.
.linvel((1.0, 3.0, 4.0))
# The angular velocity of this body.
# Default: zero velocity.
.angvel((3.0, 0.0, 0.0))
# All done, actually build the rigid-body.
.build()
)
# Set the velocities after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
# Setting these properties automatically wakes the rigid-body up.
rigid_body.linvel = (1.0, 3.0, 4.0)
rigid_body.angvel = (3.0, 0.0, 0.0)
assert rigid_body.linvel == rp.Vec3(1.0, 3.0, 4.0)
assert rigid_body.angvel == rp.Vec3(3.0, 0.0, 0.0)
Alternatively, the velocity of a dynamic rigid-body can be altered indirectly by applying a force or an impulse.
Gravity
Gravity is such a common force that it is implemented as a special case (even if it could easily be implemented
by the user using force application). PhysicsWorld::gravity (or as an argument to the PhysicsPipeline::step method) and can be modified at will.RapierConfiguration::gravity of the component RapierConfiguration and can be modified at
will.World. It can be modified by modifying the
field World.gravity.r3SetGravity (and read with r3Gravity) and can be modified at will.PhysicsWorld.gravity property (initialized by the gravity argument of the PhysicsWorld constructor, and zero by default) or as an argument to the PhysicsPipeline.step method, and can be modified at will.PhysicsWorld.wake_up_all.
Because fixed and kinematic bodies are immune to forces, they are not affected by gravity.
A rigid-body with no mass will not be affected by gravity either. So if your rigid-body doesn't fall when you expected it to, make sure it has a mass set explicitly, or has at least one collider with non-zero density attached to it.
It is possible to change the way gravity affects a specific rigid-body by setting the rigid-body's gravity scale
to a value other than 1.0. The magnitude of the gravity applied to this body will be multiplied by this scaling
factor. Therefore, a gravity scale set to 0.0 will disable gravity for the rigid-body whereas a gravity scale set to
2.0 will make it twice as strong. A negative value will flip the direction of the gravity for this rigid-body.
This gravity scale factor can be set when the rigid-body is created or after its creation:
/* Set the gravity scale when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
// Divide by 2 the strength of gravity for this rigid-body.
.gravity_scale(0.5)
.build();
/* Set the gravity scale after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_gravity_scale(0.5, true);
assert_eq!(rigid_body.gravity_scale(), 0.5);
/* Set the gravity scale when the rigid-body is created. */
commands.spawn(RigidBody::Dynamic).insert(GravityScale(2.0));
/* Set the gravity scale inside of a system. */
fn modify_body_gravity_scale(mut grav_scale: Query<&mut GravityScale>) {
for mut grav_scale in grav_scale.iter_mut() {
grav_scale.0 = 2.0;
}
}
/* Set the gravity scale when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setGravityScale(2.0);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the gravity scale after the rigid-body creation. */
rigidBody.setGravityScale(2.0, true);
/* Set the gravity scale when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
// Divide by 2 the strength of gravity for this rigid-body.
rigid_body.gravityScale = 0.5;
/* Set the gravity scale after the rigid-body creation. */
// The last `1` argument makes sure the rigid-body is awake.
r2RigidBody_SetGravityScale(rigid_body_handle, 0.5, 1);
assert(r2RigidBody_GravityScale(rigid_body_handle) == 0.5);
# Set the gravity scale when the rigid-body is created.
rigid_body = (
rp.RigidBody.dynamic()
# Divide by 2 the strength of gravity for this rigid-body.
.gravity_scale(0.5)
.build()
)
# Set the gravity scale after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
# Setting this property automatically wakes the rigid-body up.
rigid_body.gravity_scale = 0.5
assert rigid_body.gravity_scale == 0.5
Forces and impulses
In addition to gravity, it is possible to add custom forces (or torques) or apply impulses (or torque impulses) to dynamic rigid-bodies in order to make them move in specific ways. Forces affect the rigid-body's acceleration whereas impulses affect the rigid-body's velocity. They are both based on the familiar equations:
- Forces: the acceleration change is equal to the force divided by the mass:
- Impulses: the velocity change is equal to the impulse divided by the mass:
Forces can be added, and impulses can be applied, to a rigid-body
- Example 2D
- Example 3D
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.reset_forces(true); // Reset the forces to zero.
rigid_body.reset_torques(true); // Reset the torques to zero.
rigid_body.add_force(Vector::new(0.0, 1000.0), true);
rigid_body.add_torque(100.0, true);
rigid_body.add_force_at_point(Vector::new(0.0, 1000.0), Vector::new(1.0, 2.0), true);
rigid_body.apply_impulse(Vector::new(0.0, 1000.0), true);
rigid_body.apply_torque_impulse(100.0, true);
rigid_body.apply_impulse_at_point(Vector::new(0.0, 1000.0), Vector::new(1.0, 2.0), true);
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.reset_forces(true); // Reset the forces to zero.
rigid_body.reset_torques(true); // Reset the torques to zero.
rigid_body.add_force(Vector::new(0.0, 1000.0, 0.0), true);
rigid_body.add_torque(Vector::new(100.0, 0.0, 0.0), true);
rigid_body.add_force_at_point(
Vector::new(0.0, 1000.0, 0.0),
Vector::new(1.0, 2.0, 3.0),
true,
);
rigid_body.apply_impulse(Vector::new(0.0, 1000.0, 0.0), true);
rigid_body.apply_torque_impulse(Vector::new(100.0, 0.0, 0.0), true);
rigid_body.apply_impulse_at_point(
Vector::new(0.0, 1000.0, 0.0),
Vector::new(1.0, 2.0, 3.0),
true,
);
- Example 2D
- Example 3D
commands
.spawn(RigidBody::Dynamic)
.insert(ExternalForce {
force: Vec2::new(1000.0, 2000.0),
torque: 140.0,
})
.insert(ExternalImpulse {
impulse: Vec2::new(100.0, 200.0),
torque_impulse: 14.0,
})
// Needed to read the world-space center-of-mass from `apply_impulse_at_point`.
.insert(ReadWorldMassProperties::default());
/* Apply forces and impulses inside of a system. */
fn apply_forces(
mut ext_forces: Query<&mut ExternalForce>,
mut ext_impulses: Query<&mut ExternalImpulse>,
) {
// Apply forces.
for mut ext_force in ext_forces.iter_mut() {
ext_force.force = Vec2::new(1000.0, 2000.0);
ext_force.torque = 0.4;
}
// Apply impulses.
for mut ext_impulse in ext_impulses.iter_mut() {
ext_impulse.impulse = Vec2::new(100.0, 200.0);
ext_impulse.torque_impulse = 0.4;
}
}
/* Apply an impulse at a world-space point inside of a system. */
fn apply_impulse_at_point(mut bodies: Query<(&mut ExternalImpulse, &ReadWorldMassProperties)>) {
for (mut ext_impulse, mprops) in bodies.iter_mut() {
// The torque impulse is deduced from the world-space center-of-mass of the rigid-body.
*ext_impulse += ExternalImpulse::at_point(
Vec2::new(0.0, 100.0),
Vec2::new(1.0, 2.0),
mprops.center_of_mass,
);
}
}
/* Apply forces when the rigid-body is created. */
commands
.spawn(RigidBody::Dynamic)
.insert(ExternalForce {
force: Vec3::new(10.0, 20.0, 30.0),
torque: Vec3::new(1.0, 2.0, 3.0),
})
.insert(ExternalImpulse {
impulse: Vec3::new(1.0, 2.0, 3.0),
torque_impulse: Vec3::new(0.1, 0.2, 0.3),
})
// Needed to read the world-space center-of-mass from `apply_impulse_at_point`.
.insert(ReadWorldMassProperties::default());
/* Apply forces and impulses inside of a system. */
fn apply_forces(
mut ext_forces: Query<&mut ExternalForce>,
mut ext_impulses: Query<&mut ExternalImpulse>,
) {
// Apply forces.
for mut ext_force in ext_forces.iter_mut() {
ext_force.force = Vec3::new(1000.0, 2000.0, 3000.0);
ext_force.torque = Vec3::new(0.4, 0.5, 0.6);
}
// Apply impulses.
for mut ext_impulse in ext_impulses.iter_mut() {
ext_impulse.impulse = Vec3::new(100.0, 200.0, 300.0);
ext_impulse.torque_impulse = Vec3::new(0.4, 0.5, 0.6);
}
}
/* Apply an impulse at a world-space point inside of a system. */
fn apply_impulse_at_point(mut bodies: Query<(&mut ExternalImpulse, &ReadWorldMassProperties)>) {
for (mut ext_impulse, mprops) in bodies.iter_mut() {
// The torque impulse is deduced from the world-space center-of-mass of the rigid-body.
*ext_impulse += ExternalImpulse::at_point(
Vec3::new(0.0, 100.0, 0.0),
Vec3::new(1.0, 2.0, 3.0),
mprops.center_of_mass,
);
}
}
The ExternalForce component is applied at each timestep until it is modified or removed, whereas the
ExternalImpulse component is applied only once and then automatically reset to zero. A force or impulse applied
at a specific world-space point can be built with ExternalForce::at_point or ExternalImpulse::at_point: these
take the world-space center-of-mass of the rigid-body, which can be read, e.g., from its ReadWorldMassProperties
component.
- Example 2D
- Example 3D
// The `true` argument makes sure the rigid-body is awake.
rigidBody.resetForces(true); // Reset the forces to zero.
rigidBody.resetTorques(true); // Reset the torques to zero.
rigidBody.addForce({ x: 0.0, y: 1000.0 }, true);
rigidBody.addTorque(100.0, true);
rigidBody.addForceAtPoint({ x: 0.0, y: 1000.0 }, { x: 1.0, y: 2.0 }, true);
rigidBody.applyImpulse({ x: 0.0, y: 1000.0 }, true);
rigidBody.applyTorqueImpulse(100.0, true);
rigidBody.applyImpulseAtPoint({ x: 0.0, y: 1000.0 }, { x: 1.0, y: 2.0 }, true);
// The `true` argument makes sure the rigid-body is awake.
rigidBody.resetForces(true); // Reset the forces to zero.
rigidBody.resetTorques(true); // Reset the torques to zero.
rigidBody.addForce({ x: 0.0, y: 1000.0, z: 0.0 }, true);
rigidBody.addTorque({ x: 100.0, y: 0.0, z: 0.0 }, true);
rigidBody.addForceAtPoint({ x: 0.0, y: 1000.0, z: 0.0 }, { x: 1.0, y: 2.0, z: 3.0 }, true);
rigidBody.applyImpulse({ x: 0.0, y: 1000.0, z: 0.0 }, true);
rigidBody.applyTorqueImpulse({ x: 100.0, y: 0.0, z: 0.0 }, true);
rigidBody.applyImpulseAtPoint({ x: 0.0, y: 1000.0, z: 0.0 }, { x: 1.0, y: 2.0, z: 3.0 }, true);
- Example 2D
- Example 3D
// The last `1` argument makes sure the rigid-body is awake.
r2RigidBody_ResetForces(rigid_body_handle, 1); // Reset the forces to zero.
r2RigidBody_ResetTorques(rigid_body_handle, 1); // Reset the torques to zero.
r2RigidBody_AddForce(rigid_body_handle, r2Vector(0.0, 1000.0), 1);
r2RigidBody_AddTorque(rigid_body_handle, 100.0, 1);
r2RigidBody_AddForceAtPoint(rigid_body_handle, r2Vector(0.0, 1000.0), r2Vector(1.0, 2.0), 1);
r2RigidBody_ApplyImpulse(rigid_body_handle, r2Vector(0.0, 1000.0), 1);
r2RigidBody_ApplyTorqueImpulse(rigid_body_handle, 100.0, 1);
r2RigidBody_ApplyImpulseAtPoint(rigid_body_handle, r2Vector(0.0, 1000.0), r2Vector(1.0, 2.0), 1);
// The last `1` argument makes sure the rigid-body is awake.
r3RigidBody_ResetForces(rigid_body_handle, 1); // Reset the forces to zero.
r3RigidBody_ResetTorques(rigid_body_handle, 1); // Reset the torques to zero.
r3RigidBody_AddForce(rigid_body_handle, r3Vector(0.0, 1000.0, 0.0), 1);
r3RigidBody_AddTorque(rigid_body_handle, r3Vector(100.0, 0.0, 0.0), 1);
r3RigidBody_AddForceAtPoint(rigid_body_handle, r3Vector(0.0, 1000.0, 0.0), r3Vector(1.0, 2.0, 3.0), 1);
r3RigidBody_ApplyImpulse(rigid_body_handle, r3Vector(0.0, 1000.0, 0.0), 1);
r3RigidBody_ApplyTorqueImpulse(rigid_body_handle, r3Vector(100.0, 0.0, 0.0), 1);
r3RigidBody_ApplyImpulseAtPoint(rigid_body_handle, r3Vector(0.0, 1000.0, 0.0), r3Vector(1.0, 2.0, 3.0), 1);
The forces and torques added with r3RigidBody_AddForce, r3RigidBody_AddTorque, and r3RigidBody_AddForceAtPoint
are accumulated until they are reset with r3RigidBody_ResetForces and r3RigidBody_ResetTorques. Their current sum
can be read with r3RigidBody_UserForce and r3RigidBody_UserTorque. The impulses, on the other hand, modify the
velocity of the rigid-body immediately. The points given to r3RigidBody_AddForceAtPoint and
r3RigidBody_ApplyImpulseAtPoint are expressed in world-space.
rigid_body = world.rigid_bodies[rigid_body_handle]
# The rigid-body is woken up, unless `wake_up=False` is given.
rigid_body.reset_forces() # Reset the forces to zero.
rigid_body.reset_torques() # Reset the torques to zero.
rigid_body.add_force((0.0, 1000.0, 0.0))
rigid_body.add_torque((100.0, 0.0, 0.0))
rigid_body.add_force_at_point((0.0, 1000.0, 0.0), (1.0, 2.0, 3.0))
rigid_body.apply_impulse((0.0, 1000.0, 0.0))
rigid_body.apply_torque_impulse((100.0, 0.0, 0.0))
rigid_body.apply_impulse_at_point((0.0, 1000.0, 0.0), (1.0, 2.0, 3.0))
The forces and torques added with RigidBody.add_force, RigidBody.add_torque, and RigidBody.add_force_at_point
are accumulated until they are reset with RigidBody.reset_forces and RigidBody.reset_torques. Their current sum is
given by the RigidBody.user_force and RigidBody.user_torque properties. The impulses, on the other hand, modify the
velocity of the rigid-body immediately. The points given to add_force_at_point and apply_impulse_at_point are
expressed in world-space.
Keep in mind that a dynamic rigid-body with a zero mass won't be affected by a linear force/impulse, and a rigid-body with a zero angular inertia won't be affected by torques/torque impulses. So if your force doesn't appear to do anything, make sure that:
- The rigid-body is dynamic.
- It is strong enough to make the rigid-body move (try a very large value and see if it does something).
- The rigid-body has a non-zero mass or angular inertia either because they were set explicitly, or because they were computed automatically from colliders with non-zero densities.
wake_up parameter to true).r3RigidBody_WakeUp or setting the last wake_up argument to 1).RigidBody.wake_up or keeping the wake_up argument to its default value True).Mass properties
The mass properties of a rigid-body is composed of three parts:
- The mass which determines the resistance of the rigid-body wrt. linear movements. A high mass implies that larger forces are needed to make the rigid-body translate.
- The angular inertia determines the resistance of the rigid-body wrt. the angular movements. A high angular inertia implies that larger torques are needed to make the rigid-body rotate.
- The center-of-mass determines relative to what points torques are applied to the rigid-body.
Zero is a special value for masses and angular inertia. A mass equal to zero is interpreted as an infinite mass. An angular inertia equal to zero is interpreted as an infinite angular inertia. Therefore, a rigid-body with a mass equal to zero will not be affected by any force, and a rigid-body with an angular inertia equal to zero will not be affected by any torque.
Computing the mass and angular-inertia can often be difficult because they depend on the geometric shape of the object being simulated. This is why they are automatically computed by Rapier when a collider is attached to the rigid-body: the collider add its own mass and angular-inertia contribution (computed based on the collider's shape and density) to the rigid-body it is attached to:
let rigid_body = RigidBodyBuilder::dynamic().build();
let rigid_body_handle = world.insert_body(rigid_body);
// The default density is 1.0, we are setting 2.0 for this example.
let collider = ColliderBuilder::ball(1.0).density(2.0).build();
// When the collider is attached, the rigid-body's mass and angular
// inertia is automatically updated to take the collider into account.
world.insert_collider(collider, Some(rigid_body_handle));
// When the collider is attached, the rigid-body's mass and angular
// inertia will be automatically updated to take the collider into account.
commands
.spawn(RigidBody::Dynamic)
.insert(Collider::ball(1.0))
// The default density is 1.0, we are setting 2.0 for this example.
.insert(ColliderMassProperties::Density(2.0));
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic();
let rigidBody = world.createRigidBody(rigidBodyDesc);
// The default density is 1.0, we are setting 2.0 for this example.
let colliderDesc = RAPIER.ColliderDesc.ball(1.0).setDensity(2.0);
// When the collider is attached, the rigid-body's mass and angular
// inertia is automatically updated to take the collider into account.
world.createCollider(colliderDesc, rigidBody);
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
R2RigidBodyHandle rigid_body_handle = r2InsertRigidBody(world, &rigid_body);
// The default density is 1.0, we are setting 2.0 for this example.
R2ColliderDesc collider = r2BallColliderDesc(1.0);
collider.density = 2.0;
// When the collider is attached, the rigid-body's mass and angular
// inertia is automatically updated to take the collider into account.
r2InsertCollider(rigid_body_handle, &collider);
rigid_body_handle = world.add_body(rp.RigidBody.dynamic())
# The default density is 1.0, we are setting 2.0 for this example.
collider = rp.Collider.ball(1.0).density(2.0)
# When the collider is attached, the rigid-body's mass and angular
# inertia is automatically updated to take the collider into account.
world.add_collider(collider, parent=rigid_body_handle)
Alternatively, it is possible to set the mass properties of a rigid-body when it is created. Keep in mind that this won't prevent the colliders' contributions to be added to these values. So make sure to set the attached colliders' densities to zero if you want your explicit values to be the final mass-properties values.
- Example 2D
- Example 3D
/* Set the mass-properties when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
.additional_mass(0.5)
// Sets both the mass and angular inertia at once.
.additional_mass_properties(MassProperties::new(Vector::new(0.0, 1.0), 0.5, 0.3))
.build();
/* Set the mass-properties after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body
.set_additional_mass_properties(MassProperties::new(Vector::new(0.0, 1.0), 0.5, 0.3), true);
/* Set the mass-properties when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
.additional_mass(0.5)
// Sets both the mass and angular inertia at once.
.additional_mass_properties(MassProperties::new(
Vector::new(0.0, 1.0, 0.0),
0.5,
Vector::new(0.3, 0.2, 0.1),
))
.build();
/* Set the mass-properties after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The `true` argument makes sure the rigid-body is awake.
rigid_body.set_additional_mass_properties(
MassProperties::new(Vector::new(0.0, 1.0, 0.0), 0.5, Vector::new(0.3, 0.2, 0.1)),
true,
);
/* Set the additional mass properties when the rigid-body bundle is created. */
commands
.spawn(RigidBody::Dynamic)
.insert(AdditionalMassProperties::Mass(10.0));
/* Change the additional mass-properties inside of a system. */
fn modify_body_mass_props(mut mprops: Query<&mut AdditionalMassProperties>) {
for mut mprops in mprops.iter_mut() {
*mprops = AdditionalMassProperties::Mass(100.0);
}
}
The resulting mass-properties (including the colliders' contributions) can be read from the ReadMassProperties
component (local-space mass, center-of-mass and angular inertia), inserted automatically with the RigidBody
component, or by adding the ReadWorldMassProperties component (world-space center-of-mass and inverse inertia, taking
the locked axes into account) to the rigid-body. The latter isn't
inserted automatically because it changes at each step for every moving rigid-body. Both are updated by the physics
engine and modifying them has no effect on the simulation.
The RapierRigidBodySet (from the ReadRapierContext system parameter) also has helpers taking the rigid-body entity,
like RapierRigidBodySet::mass and RapierRigidBodySet::center_of_mass.
- Example 2D
- Example 3D
/* Set the mass-properties when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setAdditionalMass(0.5)
// Sets both the mass and angular inertia at once.
.setAdditionalMassProperties(
0.5, // Mass.
{ x: 0.0, y: 1.0 }, // Center of mass.
0.3 // Principal angular inertia.
);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the mass-properties when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setAdditionalMass(0.5)
// Sets both the mass and angular inertia at once.
.setAdditionalMassProperties(
0.5, // Mass.
{ x: 0.0, y: 1.0, z: 0.0 }, // Center of mass.
{ x: 0.3, y: 0.2, z: 0.1 }, // Principal angular inertia.
{ w: 1.0, x: 0.0, y: 0.0, z: 0.0 } // Principal angular inertia frame (unit quaternion).
);
let rigidBody = world.createRigidBody(rigidBodyDesc);
- Example 2D
- Example 3D
/* Set the mass-properties when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
rigid_body.additionalMass = 0.5;
// Sets both the mass and angular inertia at once (this overrides `additionalMass`).
rigid_body.useAdditionalMassProperties = 1;
rigid_body.additionalMassProperties = (R2MassProperties){
.local_com = r2Vector(0.0, 1.0),
.mass = 0.5,
.principal_inertia = 0.3,
};
/* Set the mass-properties after the rigid-body creation. */
R2MassProperties mass_properties = {
.local_com = r2Vector(0.0, 1.0),
.mass = 0.5,
.principal_inertia = 0.3,
};
// The last `1` argument makes sure the rigid-body is awake.
r2RigidBody_SetAdditionalMassProperties(rigid_body_handle, mass_properties, 1);
/* Set the mass-properties when the rigid-body is created. */
R3RigidBodyDesc rigid_body = r3DynamicRigidBodyDesc();
rigid_body.additionalMass = 0.5;
// Sets both the mass and angular inertia at once (this overrides `additionalMass`).
rigid_body.useAdditionalMassProperties = 1;
rigid_body.additionalMassProperties = (R3MassProperties){
.local_com = r3Vector(0.0, 1.0, 0.0),
.mass = 0.5,
.principal_inertia = r3Vector(0.3, 0.2, 0.1),
// The principal inertia axes are aligned with the rigid-body's local axes.
.principal_inertia_local_frame = {0.0, 0.0, 0.0, 1.0},
};
/* Set the mass-properties after the rigid-body creation. */
R3MassProperties mass_properties = {
.local_com = r3Vector(0.0, 1.0, 0.0),
.mass = 0.5,
.principal_inertia = r3Vector(0.3, 0.2, 0.1),
.principal_inertia_local_frame = {0.0, 0.0, 0.0, 1.0},
};
// The last `1` argument makes sure the rigid-body is awake.
r3RigidBody_SetAdditionalMassProperties(rigid_body_handle, mass_properties, 1);
The additionalMass field of R3RigidBodyDesc only adds a mass (the angular inertia being scaled accordingly, based
on the shapes of the colliders), whereas the additionalMassProperties field (taken into account only if useAdditionalMassProperties
is set to 1) specifies the full mass-properties (mass, center-of-mass, and principal angular inertia) of type
R3MassProperties. After the creation of the rigid-body, they are modified with r3RigidBody_SetAdditionalMass and
r3RigidBody_SetAdditionalMassProperties respectively.
The resulting mass-properties (including the colliders' contributions) can be read with r3RigidBody_Mass,
r3RigidBody_LocalCenterOfMass (in the local-space of the rigid-body), and r3RigidBody_CenterOfMass (in
world-space). They are updated automatically by the physics engine but, if you need them right after modifying the
colliders or the additional mass-properties of a rigid-body (without waiting for the next timestep), they can be
updated manually with r3RigidBody_RecomputeMassPropertiesFromColliders.
# Set the mass-properties when the rigid-body is created.
rigid_body = (
rp.RigidBody.dynamic()
.additional_mass(0.5)
# Sets both the mass and angular inertia at once.
.additional_mass_properties(
rp.MassProperties(local_com=(0.0, 1.0, 0.0), mass=0.5, principal_inertia=(0.3, 0.2, 0.1))
)
.build()
)
# Set the mass-properties after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
# The rigid-body is woken up, unless `wake_up=False` is given.
rigid_body.set_additional_mass_properties(
rp.MassProperties(local_com=(0.0, 1.0, 0.0), mass=0.5, principal_inertia=(0.3, 0.2, 0.1))
)
The RigidBodyBuilder.additional_mass method only adds a mass (the angular inertia being scaled accordingly, based
on the shapes of the colliders), whereas RigidBodyBuilder.additional_mass_properties specifies the full
mass-properties (mass, center-of-mass, and principal angular inertia) given as a MassProperties. After the creation
of the rigid-body, they are modified with RigidBody.set_additional_mass and RigidBody.set_additional_mass_properties
respectively.
The resulting mass-properties (including the colliders' contributions) can be read with the mass,
local_center_of_mass (in the local-space of the rigid-body), center_of_mass (in world-space), and
mass_properties properties of the RigidBody. They are updated automatically by the physics engine but, if you
need them right after modifying the colliders or the additional mass-properties of a rigid-body (without waiting for
the next timestep), they can be updated manually with rigid_body.recompute_mass_properties_from_colliders(world.colliders).
Locking translations/rotations
It is sometimes useful to prevent a rigid-body from rotating or translating. One typical use-case for locking rotations
is to prevent a player modeled as a dynamic rigid-body from tilting. These kind of degree-of-freedom restrictions could
be achieved by joints, but locking translations/rotations of a single rigid-body wrt. the cartesian coordinate
axes can be done in a much more efficient and numerically stable way. That's why rigid-bodies have
dedicated
- Example 2D
- Example 3D
/* Lock translations/rotations when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
.lock_translations() // prevent translations along along all axes.
.lock_rotations() // prevent rotations.
.build();
/* Lock translations/rotations after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The last `true` argument makes sure the rigid-body is awake.
rigid_body.lock_translations(true, true);
rigid_body.lock_rotations(true, true);
/* Lock translations/rotations when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
.lock_translations() // prevent translations along along all axes.
.lock_rotations() // prevent rotations along all axes.
.enabled_rotations(true, false, false) // only enable rotations along the X axis.
.build();
/* Lock translations/rotations after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
// The last `true` argument makes sure the rigid-body is awake.
rigid_body.lock_translations(true, true);
rigid_body.lock_rotations(true, true);
rigid_body.set_enabled_rotations(true, false, false, true);
- Example 2D
- Example 3D
/* Lock translations and/or rotations when the rigid-body bundle is created. */
commands
.spawn(RigidBody::Dynamic)
.insert(LockedAxes::TRANSLATION_LOCKED);
/* Lock translations and/or rotations inside of a system. */
fn modify_body_locked_flags(mut locked_axes: Query<&mut LockedAxes>) {
for mut locked_axes in locked_axes.iter_mut() {
*locked_axes = LockedAxes::ROTATION_LOCKED;
}
}
/* Lock translations and/or rotations when the rigid-body bundle is created. */
commands
.spawn(RigidBody::Dynamic)
.insert(LockedAxes::TRANSLATION_LOCKED | LockedAxes::ROTATION_LOCKED_X);
/* Lock translations and/or rotations inside of a system. */
fn modify_body_locked_flags(mut locked_axes: Query<&mut LockedAxes>) {
for mut locked_axes in locked_axes.iter_mut() {
*locked_axes = LockedAxes::ROTATION_LOCKED;
}
}
- Example 2D
- Example 3D
/* Lock translations/rotations when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.lockTranslations() // prevent translations along along all axes.
.lockRotations(); // prevent rotations.
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Lock translations/rotations after the rigid-body creation. */
// The last `true` argument makes sure the rigid-body is awake.
rigidBody.lockTranslations(true, true);
rigidBody.lockRotations(true, true);
/* Lock translations/rotations when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.lockTranslations() // prevent translations along along all axes.
.lockRotations() // prevent rotations along all axes.
.enabledRotations(true, false, false); // only enable rotations along the X axis.
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Lock translations/rotations after the rigid-body creation. */
// The last `true` argument makes sure the rigid-body is awake.
rigidBody.lockTranslations(true, true);
rigidBody.lockRotations(true, true);
rigidBody.setEnabledRotations(true, false, false, true);
The locked axes are given by a bitmask combining the R3_LOCK_TRANSLATION_X, R3_LOCK_TRANSLATION_Y,
R3_LOCK_TRANSLATION_Z, R3_LOCK_ROTATION_X, R3_LOCK_ROTATION_Y, and R3_LOCK_ROTATION_Z flags (in 2D, only the
translations along X and Y, and the rotation around Z, are relevant). It is set with the lockedAxes field of
R3RigidBodyDesc when the rigid-body is created, or with r3RigidBody_SetLockedAxes afterwards (and read with
r3RigidBody_LockedAxes). The r3RigidBody_SetTranslationsLocked and r3RigidBody_SetRotationsLocked functions
lock (or unlock) all the translations or all the rotations at once.
- Example 2D
- Example 3D
/* Lock translations/rotations when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
rigid_body.lockedAxes = R2_LOCK_TRANSLATION_X | R2_LOCK_TRANSLATION_Y // prevent translations along all axes.
| R2_LOCK_ROTATION_Z; // prevent rotations.
/* Lock translations/rotations after the rigid-body creation. */
// The last `1` argument makes sure the rigid-body is awake.
r2RigidBody_SetTranslationsLocked(rigid_body_handle, 1, 1);
r2RigidBody_SetRotationsLocked(rigid_body_handle, 1, 1);
/* Lock translations/rotations when the rigid-body is created. */
R3RigidBodyDesc rigid_body = r3DynamicRigidBodyDesc();
rigid_body.lockedAxes =
R3_LOCK_TRANSLATION_X | R3_LOCK_TRANSLATION_Y | R3_LOCK_TRANSLATION_Z // prevent translations along all axes.
| R3_LOCK_ROTATION_Y | R3_LOCK_ROTATION_Z; // only enable rotations along the X axis.
/* Lock translations/rotations after the rigid-body creation. */
// The last `1` argument makes sure the rigid-body is awake.
r3RigidBody_SetTranslationsLocked(rigid_body_handle, 1, 1);
r3RigidBody_SetRotationsLocked(rigid_body_handle, 1, 1);
// Only enable rotations along the X axis.
uint8_t locked_axes = r3RigidBody_LockedAxes(rigid_body_handle) & ~R3_LOCK_ROTATION_X;
r3RigidBody_SetLockedAxes(rigid_body_handle, locked_axes, 1);
The locked axes are given by a LockedAxes value combining, with the | operator, the TRANSLATION_LOCKED_X,
TRANSLATION_LOCKED_Y, TRANSLATION_LOCKED_Z, ROTATION_LOCKED_X, ROTATION_LOCKED_Y, and ROTATION_LOCKED_Z
flags (or TRANSLATION_LOCKED and ROTATION_LOCKED to lock all the translations or all the rotations at once). It is
set with RigidBodyBuilder.locked_axes when the rigid-body is created, or with the RigidBody.locked_axes property
afterwards. Alternatively, the enabled_translations and enabled_rotations builder methods and properties take a
tuple of three booleans indicating, for each axis, if the corresponding translation or rotation is allowed.
# Lock translations/rotations when the rigid-body is created.
rigid_body = (
rp.RigidBody.dynamic()
# Prevent translations along all axes, and rotations around all axes.
.locked_axes(rp.LockedAxes.TRANSLATION_LOCKED | rp.LockedAxes.ROTATION_LOCKED)
# Only enable rotations around the X axis.
.enabled_rotations((True, False, False))
.build()
)
# Lock translations/rotations after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
# Setting these properties automatically wakes the rigid-body up.
rigid_body.locked_axes = rp.LockedAxes.TRANSLATION_LOCKED | rp.LockedAxes.ROTATION_LOCKED
# Only enable rotations around the X axis.
rigid_body.enabled_rotations = (True, False, False)
Damping
Damping lets you slow down a rigid-body automatically. This can be used to achieve a wide variety of effects like
fake air friction. Each rigid-body is given a linear damping coefficient (affecting its linear velocity) and an
angular damping coefficient (affecting its angular velocity). Larger values of the damping coefficients lead to
a stronger slow-downs. Their default values are 0.0 (no damping at all).
This damping coefficients can be set when the rigid-body is created or after its creation:
/* Set the damping coefficients when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic()
.linear_damping(0.5)
.angular_damping(1.0)
.build();
/* Set the damping coefficients after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
rigid_body.set_linear_damping(0.5);
rigid_body.set_angular_damping(1.0);
assert_eq!(rigid_body.linear_damping(), 0.5);
assert_eq!(rigid_body.angular_damping(), 1.0);
/* Set damping when the rigid-body bundle is created. */
commands.spawn(RigidBody::Dynamic).insert(Damping {
linear_damping: 0.5,
angular_damping: 1.0,
});
/* Set damping inside of a system. */
fn modify_body_damping(mut dampings: Query<&mut Damping>) {
for mut rb_damping in dampings.iter_mut() {
rb_damping.linear_damping = 0.5;
rb_damping.angular_damping = 1.0;
}
}
/* Set the damping coefficients when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setLinearDamping(0.5)
.setAngularDamping(1.0);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the damping coefficients after the rigid-body creation. */
rigidBody.setLinearDamping(0.5);
rigidBody.setAngularDamping(1.0);
/* Set the damping coefficients when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
rigid_body.linearDamping = 0.5;
rigid_body.angularDamping = 1.0;
/* Set the damping coefficients after the rigid-body creation. */
r2RigidBody_SetLinearDamping(rigid_body_handle, 0.5);
r2RigidBody_SetAngularDamping(rigid_body_handle, 1.0);
assert(r2RigidBody_LinearDamping(rigid_body_handle) == 0.5);
assert(r2RigidBody_AngularDamping(rigid_body_handle) == 1.0);
# Set the damping coefficients when the rigid-body is created.
rigid_body = rp.RigidBody.dynamic().linear_damping(0.5).angular_damping(1.0).build()
# Set the damping coefficients after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
rigid_body.linear_damping = 0.5
rigid_body.angular_damping = 1.0
assert rigid_body.linear_damping == 0.5
assert rigid_body.angular_damping == 1.0
Dominance
Dominance is a non-realistic, but sometimes useful, feature. It can be used to make one rigid-body immune to forces originating from contacts with some other bodies. For example this can be used to model a player represented as a dynamic rigid-body that cannot be "pushed back" by any, or some, other dynamic rigid-bodies part of the environment.
Each rigid-body is part of a dominance group in [-127; 127] (the default group is 0). If the colliders from two rigid-bodies
are in contact, the one with the highest dominance will act as if it has an infinite mass, making it immune to the
contact forces the other body would apply on it. If both bodies are part of the same dominance group, then their
contacts will work in the usual way (both are affected by opposite forces with the same magnitude).
For example, if a dynamic body A is in the dominance group 10, and a dynamic body B in the dominance group -20, then
a contact between a collider attached to A and a collider attached B will result in A remaining immobile and B being
pushed by A (independently from their mass).
A non-dynamic rigid-body is always considered as being part of a dominance group greater than any dynamic rigid-body. This means that dynamic/fixed and dynamic/kinematic contacts will continue to work normally, independently from the dominance group they were given by the user.
The dominance group can be set when the rigid-body is created or after its creation:
/* Set the dominance group when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic().dominance_group(10).build();
/* Set the dominance group after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
rigid_body.set_dominance_group(10);
assert_eq!(rigid_body.dominance_group(), 10);
/* Set dominance when the rigid-body bundle is created. */
commands
.spawn(RigidBody::Dynamic)
.insert(Dominance::group(10));
/* Set dominance inside of a system. */
fn modify_body_dominance(mut dominances: Query<&mut Dominance>) {
for mut rb_dominance in dominances.iter_mut() {
rb_dominance.groups = 10;
}
}
/* Set the damping coefficients when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setDominanceGroup(10);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Set the damping coefficients after the rigid-body creation. */
rigidBody.setDominanceGroup(10);
/* Set the dominance group when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
rigid_body.dominanceGroup = 10;
/* Set the dominance group after the rigid-body creation. */
r2RigidBody_SetDominanceGroup(rigid_body_handle, 10);
assert(r2RigidBody_DominanceGroup(rigid_body_handle) == 10);
# Set the dominance group when the rigid-body is created.
rigid_body = rp.RigidBody.dynamic().dominance_group(10).build()
# Set the dominance group after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
rigid_body.dominance_group = 10
assert rigid_body.dominance_group == 10
Continuous collision detection
Continuous Collision Detection (CCD) is used to make sure that fast-moving objects don't miss any contacts (a problem usually called tunneling). This is done by looking for collisions along the shapes motion: a rigid-body that moved fast during the timestep casts its colliders from their previous position to their new one, and its position is clamped to the first impact found this way. Its velocities are left untouched and the solver is responsible for preventing penetrations. Since the trajectory is clamped to some intermediate location along its path this technique is commonly called motion clamping.
Rapier applies CCD in two ways:
- Every fast-moving dynamic rigid-body is swept against the fixed colliders and the soft-bodies of the scene. This is automatic and doesn't need to be enabled: this prevents a falling crate from going through the floor, or a projectile from going through a wall, etc. Note however that this is not enabled fol dynamic rigid-bodies with mesh-like shapes (triangle meshes, polylines, heightfields) as that would be too computationally expensive.
- A dynamic rigid-body with CCD enabled (aka. a bullet) is swept against the kinematic and dynamic bodies as well. This is more expensive, therefore it is disabled by default and should be reserved to the objects that must not tunnel through moving obstacles. Note that two CCD-enabled objects might still tunel since the CCD resolution does currently not take both continuous motions into account simultaneously.
CCD takes action only if the rigid-body is moving fast relative to another collider. Therefore it is useless to enable it on fixed rigid-bodies and on rigid-bodies that are expected to move slowly.
The CCD feature, including the automatic sweeping of the fast dynamic bodies, can be fully disabled by setting the
maximum number of CCD substeps to zero in the
IntegrationParameters
(the IntegrationParameters::max_ccd_substepsRapierContextSimulation::integration_parameters.max_ccd_substepsIntegrationParameters.maxCcdSubstepsR3IntegrationParameters.maxCcdSubstepsPhysicsWorld.integration_parameters.max_ccd_substepsr3SetMaxCcdSubsteps.
Per-object CCD can be enabled when creating a rigid-body or after its creation:
/* Enable CCD when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic().ccd_enabled(true).build();
/* Enable CCD after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
rigid_body.enable_ccd(true);
assert_eq!(rigid_body.is_ccd_enabled(), true);
/* Enable CCD when the rigid-body bundle is created. */
commands.spawn(RigidBody::Dynamic).insert(Ccd::enabled());
/* Enable CCD inside of a system. */
fn modify_body_ccd(mut ccds: Query<&mut Ccd>) {
for mut rb_ccd in ccds.iter_mut() {
rb_ccd.enabled = true;
}
}
/* Enable CCD when the rigid-body is created. */
let rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
.setCcdEnabled(true);
let rigidBody = world.createRigidBody(rigidBodyDesc);
/* Enable CCD after the rigid-body creation. */
rigidBody.enableCcd(true);
/* Enable CCD when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
rigid_body.ccdEnabled = 1;
/* Enable CCD after the rigid-body creation. */
r2RigidBody_SetCcdEnabled(rigid_body_handle, 1);
assert(r2RigidBody_IsCcdEnabled(rigid_body_handle));
Keep in mind that r3RigidBody_IsCcdEnabled only tells if CCD was enabled for this rigid-body. Whether a rigid-body
is currently moving fast enough for CCD to take action (which is also the case of fast dynamic bodies without CCD
enabled, since they are swept against the fixed colliders) is given by r3RigidBody_IsCcdActive.
# Enable CCD when the rigid-body is created.
rigid_body = rp.RigidBody.dynamic().ccd_enabled(True).build()
# Enable CCD after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
rigid_body.ccd_enabled = True
assert rigid_body.ccd_enabled
Sleeping
When a dynamic rigid-body doesn't move (or moves very slowly) during a few seconds, it will be marked as sleeping by the physics pipeline. Rigid-bodies marked as sleeping are no longer simulated by the physics engine until they are woken up. That way the physics engine doesn't waste any computational resources simulating objects that don't actually move. They are woken up automatically whenever another non-sleeping rigid-body starts interacting with them (either with a joint, or with one of its attached colliders generating contacts).
Rigid-bodies are also automatically woken up whenever one of the
components of the rigid-body is modified (to apply forces, change its position, etc.) They will not be awaken
automatically when changing the gravity though. So you may sometimes want to wake a rigid-body manually by setting
the component field Sleeping::sleeping to false: this wakes up the rigid-body as well as the rigid-bodies
interacting with it. Setting it to true puts the rigid-body to sleep.
The other fields of the Sleeping component control when the rigid-body falls asleep: it has to move slower than
Sleeping::normalized_linear_threshold and Sleeping::angular_threshold during Sleeping::time_until_sleep seconds.
A rigid-body can be prevented from ever sleeping by giving it the Sleeping::disabled() component.
However, a sleeping rigid-body won't respond to any user action. This is why it is possible to wake-up the rigid-body
manually with RigidBody::wake_up. Some rigid-body methods take an additional wake_up boolean argument that, if
true, ensures that the rigid-body wakes up before the action takes place. For example:
RigidBody::add_force(force, true)will wake-up the rigid-body before adding the force.ImpulseJointSet::remove(..., true)will wake-up the two rigid-bodies attached by the removed joints.ColliderSet::remove(..., true)will wake-up the rigid-body the removed collider is attached to.
Unless you want to achieve special effects, it is recommended to always set the wake_up argument to true.
One example of case where setting the argument of wake_up to false makes sense is to simulate a custom
constant gravity with RigidBody::add_force(force, false). This will result in the force being added
to the rigid-body, but will allow the rigid-body to fall asleep if it reaches a dynamic equilibrium.
However, a sleeping rigid-body won't respond to any user action. This is why it is possible to wake-up the rigid-body
manually with RigidBody.wakeUp(). Some rigid-body methods take an additional wakeUp boolean argument that, if
true, ensures that the rigid-body wakes up before the action takes place. For example:
RigidBody.addForce(force, true)will wake-up the rigid-body before adding the force.World.removeImpulseJoint(joint, true)(resp.World.removeMultibodyJoint) will wake-up the two rigid-bodies attached by the removed joint.World.removeCollider(collider, true)will wake-up the rigid-body the removed collider is attached to.
Unless you want to achieve special effects, it is recommended to always set the wakeUp argument to true.
One example of case where setting the argument of wakeUp to false makes sense is to simulate a custom
constant gravity with RigidBody.addForce(force, false). This will result in the force being applied
to the rigid-body, but will allow the rigid-body to fall asleep if it reaches a dynamic equilibrium.
However, a sleeping rigid-body won't respond to any user action. This is why it is possible to wake-up the rigid-body
manually with r3RigidBody_WakeUp (if its strong argument is 1, the rigid-body is guaranteed to stay awake for
several timesteps, otherwise it may fall asleep again immediately). Some functions take an additional wake_up
argument that, if set to 1, ensures that the rigid-body wakes up before the action takes place. For example:
r3RigidBody_AddForce(handle, force, 1)will wake-up the rigid-body before adding the force.r3RemoveImpulseJoint(joint, 1)(resp.r3RemoveMultibodyJoint) will wake-up the two rigid-bodies attached by the removed joint.r3RemoveCollider(collider, 1)will wake-up the rigid-body the removed collider is attached to.
Unless you want to achieve special effects, it is recommended to always set the wake_up argument to 1.
One example of case where setting the argument of wake_up to 0 makes sense is to simulate a custom
constant gravity with r3RigidBody_AddForce(handle, force, 0). This will result in the force being added
to the rigid-body, but will allow the rigid-body to fall asleep if it reaches a dynamic equilibrium.
Whether a rigid-body is sleeping is given by r3RigidBody_IsSleeping, and it can be put to sleep manually with
r3RigidBody_Sleep. A rigid-body can be prevented from ever sleeping by setting the canSleep field of its
R3RigidBodyDesc to 0, or created already asleep by setting its sleeping field to 1.
However, a sleeping rigid-body won't respond to any user action. This is why it is possible to wake-up the rigid-body
manually with RigidBody.wake_up() or PhysicsWorld.wake_up(handle) (if their strong argument is True, the
default, the rigid-body is guaranteed to stay awake for several timesteps, otherwise it may fall asleep again
immediately). Setting the pose, the velocities, the gravity scale, the type, or the locked axes of a rigid-body through
its properties always wakes it up. Some methods take an additional wake_up boolean argument that, if True (the
default), ensures that the rigid-body wakes up before the action takes place. For example:
RigidBody.add_force(force, wake_up=True)will wake-up the rigid-body before adding the force.ImpulseJointSet.remove(joint, wake_up=True)(resp.MultibodyJointSet.remove) will wake-up the two rigid-bodies attached by the removed joint.ColliderSet.remove(collider, islands, bodies, wake_up=True)will wake-up the rigid-body the removed collider is attached to (PhysicsWorld.remove_collideralways does).
Unless you want to achieve special effects, it is recommended to keep the default value True of the wake_up
argument. One example of case where setting the argument of wake_up to False makes sense is to simulate a custom
constant gravity with RigidBody.add_force(force, wake_up=False). This will result in the force being added
to the rigid-body, but will allow the rigid-body to fall asleep if it reaches a dynamic equilibrium.
Whether a rigid-body is sleeping is given by the RigidBody.is_sleeping property, and it can be put to sleep manually
with RigidBody.sleep(). A rigid-body can be prevented from ever sleeping with RigidBodyBuilder.can_sleep(False),
or created already asleep with RigidBodyBuilder.sleeping(True). The velocity thresholds and the delay before the
rigid-body falls asleep are given by the linear_threshold, angular_threshold, and time_until_sleep attributes of
the RigidBodyActivation returned by the RigidBody.activation property. Keep in mind that this property returns a
copy: the modified RigidBodyActivation must be assigned back to RigidBody.activation to take effect.
Solver settings
The accuracy of the constraints solver is configured for the whole world by the integration parameters. However, in some cases, part of the simulation might need more fine-grained control. For example, an articulated robot, or a stack involving large mass ratios, might require more solver iterations. This is why a rigid-body can ask for additional solver iterations (either substeps, or internal steps) resulting in the island it belongs to (the bodies it is connected to by contacts and joints) to run with a higher accuracy without hurting the performances of other islands.
Two other settings affect how a rigid-body is integrated. The soft-CCD prediction distance makes the body generate predictive contacts ahead of its own path, which is a cheaper alternative to CCD for the objects that are thin or moderately fast (large values impact the performances badly by increasing significantly the number of collision pairs). Finally, the fast-rotation flag lets the body exceed the angular speed cap, which is enabled by default to keep CCD reliable.
- Example 2D
- Example 3D
/* Give a rigid-body more solver accuracy than the rest of the scene. */
let rigid_body = RigidBodyBuilder::dynamic()
// Extra substeps run for the whole island component this body belongs to.
.additional_solver_iterations(4)
// Extra internal PGS iterations run per substep for that same component.
.additional_pgs_iterations(2)
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
.soft_ccd_prediction(0.5)
// Let the body exceed the angular speed cap, e.g. for a wheel.
.allow_fast_rotation(true)
.build();
/* Give a rigid-body more solver accuracy than the rest of the scene. */
let rigid_body = RigidBodyBuilder::dynamic()
// Extra substeps run for the whole island component this body belongs to.
.additional_solver_iterations(4)
// Extra internal PGS iterations run per substep for that same component.
.additional_pgs_iterations(2)
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
.soft_ccd_prediction(0.5)
// Let the body exceed the angular speed cap, e.g. for a wheel.
.allow_fast_rotation(true)
// Gyroscopic forces give more realistic behaviors, e.g. the precession of a spinning top.
.gyroscopic_forces_enabled(true)
.build();
These settings are given by the AdditionalSolverIterations, AdditionalPgsIterations,
SoftCcd, and AllowFastRotation components. Removing them restores the default behavior.
- Example 2D
- Example 3D
/* Give a rigid-body more solver accuracy than the rest of the scene. */
commands.spawn((
RigidBody::Dynamic,
// Extra substeps run for the whole island this body belongs to.
AdditionalSolverIterations(4),
// Extra internal PGS iterations run per substep for that same island.
AdditionalPgsIterations(2),
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
SoftCcd { prediction: 0.5 },
// Let the body exceed the angular speed cap, e.g. for a wheel.
AllowFastRotation,
));
/* Give a rigid-body more solver accuracy than the rest of the scene. */
commands.spawn((
RigidBody::Dynamic,
// Extra substeps run for the whole island this body belongs to.
AdditionalSolverIterations(4),
// Extra internal PGS iterations run per substep for that same island.
AdditionalPgsIterations(2),
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
SoftCcd { prediction: 0.5 },
// Let the body exceed the angular speed cap, e.g. for a wheel.
AllowFastRotation,
// Gyroscopic forces give more realistic behaviors, e.g. the precession of a spinning top.
// Default: enabled (even without this component).
GyroscopicForces::enabled(),
));
- Example 2D
- Example 3D
/* Give a rigid-body more solver accuracy than the rest of the scene. */
let solverBodyDesc = RAPIER.RigidBodyDesc.dynamic()
// Extra substeps run for the whole island component this body belongs to.
.setAdditionalSolverIterations(4)
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
.setSoftCcdPrediction(0.5);
let solverBody = world.createRigidBody(solverBodyDesc);
/* Give a rigid-body more solver accuracy than the rest of the scene. */
let solverBodyDesc = RAPIER.RigidBodyDesc.dynamic()
// Extra substeps run for the whole island component this body belongs to.
.setAdditionalSolverIterations(4)
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
.setSoftCcdPrediction(0.5);
let solverBody = world.createRigidBody(solverBodyDesc);
These settings are given by the additionalSolverIterations, additionalPgsIterations, softCcdPrediction, and
allowFastRotation fields of R3RigidBodyDesc. After the creation of the rigid-body, they can be modified with
r3RigidBody_SetAdditionalSolverIterations, r3RigidBody_SetAdditionalPgsIterations,
r3RigidBody_SetSoftCcdPrediction, and r3RigidBody_SetAllowFastRotation (and read with
r3RigidBody_AdditionalSolverIterations, r3RigidBody_AdditionalPgsIterations, r3RigidBody_SoftCcdPrediction,
and r3RigidBody_IsFastRotationAllowed).
- Example 2D
- Example 3D
/* Give a rigid-body more solver accuracy than the rest of the scene. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
// Extra substeps run for the whole island component this body belongs to.
rigid_body.additionalSolverIterations = 4;
// Extra internal PGS iterations run per substep for that same component.
rigid_body.additionalPgsIterations = 2;
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
rigid_body.softCcdPrediction = 0.5;
// Let the body exceed the angular speed cap, e.g. for a wheel.
rigid_body.allowFastRotation = 1;
/* Give a rigid-body more solver accuracy than the rest of the scene. */
R3RigidBodyDesc rigid_body = r3DynamicRigidBodyDesc();
// Extra substeps run for the whole island component this body belongs to.
rigid_body.additionalSolverIterations = 4;
// Extra internal PGS iterations run per substep for that same component.
rigid_body.additionalPgsIterations = 2;
// Predictive contacts generated up to that distance ahead of the body's path: a cheaper
// alternative to CCD for slow-but-thin or moderately fast objects.
rigid_body.softCcdPrediction = 0.5;
// Let the body exceed the angular speed cap, e.g. for a wheel.
rigid_body.allowFastRotation = 1;
// Gyroscopic forces give more realistic behaviors, e.g. the precession of a spinning top.
rigid_body.gyroscopicForcesEnabled = 1;
These settings are given by the additional_solver_iterations, additional_pgs_iterations, soft_ccd_prediction,
and allow_fast_rotation methods of the RigidBodyBuilder. After the creation of the rigid-body, they can be read
and modified with the RigidBody properties of the same names.
# Give a rigid-body more solver accuracy than the rest of the scene.
rigid_body = (
rp.RigidBody.dynamic()
# Extra substeps run for the whole island component this body belongs to.
.additional_solver_iterations(4)
# Extra internal PGS iterations run per substep for that same component.
.additional_pgs_iterations(2)
# Predictive contacts generated up to that distance ahead of the body's path: a cheaper
# alternative to CCD for slow-but-thin or moderately fast objects.
.soft_ccd_prediction(0.5)
# Let the body exceed the angular speed cap, e.g. for a wheel.
.allow_fast_rotation(True)
# Gyroscopic forces give more realistic behaviors, e.g. the precession of a spinning top.
.gyroscopic_forces(True)
.build()
)
In 3D, the gyroscopic forces of a rigid-body can be disabled as well. When enabled (the default), they give the more realistic behaviors of a spinning solid, e.g., the precession of a spinning top or the Dzhanibekov effect. Disabling them is only recommended if they represent a measurable overhead in your simulation.
In 3D, the gyroscopic forces of a rigid-body can be disabled as well by giving it the GyroscopicForces::disabled()
component. When enabled (the default, even without this component), they give the more realistic behaviors of a
spinning solid, e.g., the precession of a spinning top or the Dzhanibekov effect. Disabling them is only recommended if
they represent a measurable overhead in your simulation.
In 3D, the gyroscopic forces of a rigid-body can be disabled as well by setting the gyroscopicForcesEnabled field
of its R3RigidBodyDesc to 0, or with r3RigidBody_SetGyroscopicForcesEnabled after its creation. When enabled
(the default), they give the more realistic behaviors of a spinning solid, e.g., the precession of a spinning top or
the Dzhanibekov effect. Disabling them is only recommended if they represent a measurable overhead in your simulation.
The gyroscopic forces of a rigid-body can be disabled as well with RigidBodyBuilder.gyroscopic_forces(False), or by
setting its RigidBody.gyroscopic_forces_enabled property to False after its creation. When enabled (the default),
they give the more realistic behaviors of a spinning solid, e.g., the precession of a spinning top or the Dzhanibekov
effect. Disabling them is only recommended if they represent a measurable overhead in your simulation.
User-data
Each rigid-body can be given a user-defined data of type u128. This integer can have any value
and is never used/modified by the physics-engine. This can for example be useful to add some custom data for
custom contact filtering/modification.
This user-data can be set when the rigid-body is created or after its creation:
/* Set the user-data when the rigid-body is created. */
let rigid_body = RigidBodyBuilder::dynamic().user_data(42).build();
/* Set the user-data after the rigid-body creation. */
let rigid_body = &mut world.bodies[rigid_body_handle];
rigid_body.user_data = 42;
assert_eq!(rigid_body.user_data, 42);
User-data
Each rigid-body can be given a user-defined data of type R3UserData: a 128-bits integer split into its low and
high 64-bits halves. This integer can have any value and is never used/modified by the physics-engine. This can for
example be useful to store an index (or a pointer converted to an integer) referencing your own data associated to the
rigid-body, or to add some custom data for custom
contact filtering/modification.
This user-data can be set when the rigid-body is created or after its creation:
/* Set the user-data when the rigid-body is created. */
R2RigidBodyDesc rigid_body = r2DynamicRigidBodyDesc();
// The 128 bits of the user-data are split into its `low` and `high` 64 bits.
rigid_body.userData.low = 42;
/* Set the user-data after the rigid-body creation. */
R2UserData user_data = {.low = 42, .high = 0};
r2RigidBody_SetUserData(rigid_body_handle, user_data);
assert(r2RigidBody_UserData(rigid_body_handle).low == 42);
User-data
Each rigid-body can be given a user-defined data: a Python int in the range of an unsigned 128-bits integer (i.e.,
from 0 to 2**128 - 1). This integer can have any value and is never used/modified by the physics-engine. This can
for example be useful to store an index (or any integer identifier) referencing your own data associated to the rigid-body,
or to add some custom data for custom contact filtering/modification.
This user-data can be set when the rigid-body is created or after its creation with the RigidBody.user_data
property:
# Set the user-data when the rigid-body is created.
rigid_body = rp.RigidBody.dynamic().user_data(42).build()
# Set the user-data after the rigid-body creation.
rigid_body = world.rigid_bodies[rigid_body_handle]
rigid_body.user_data = 42
assert rigid_body.user_data == 42