Skip to main content

rigid_body_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: Δa=m−1f\Delta{}a = m^{-1}f
  • Impulses: the velocity change is equal to the impulse divided by the mass: Δv=m−1i\Delta{}v = m^{-1}i

Forces can be added, and impulses can be applied, to a rigid-body after it has been createdwhen it is created or after its creationafter it has been createdafter it has been createdafter it has been created. Added forces are persistent across simulation steps, and can be cleared manually.

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);
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,
);
}
}

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.

// 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 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 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.

info

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:

  1. The rigid-body is dynamic.
  2. It is strong enough to make the rigid-body move (try a very large value and see if it does something).
  3. 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.
4. The rigid-body is awake (by waking it up manually or setting the last wake_up parameter to true).4. The rigid-body is awake (by waking it up manually with r3RigidBody_WakeUp or setting the last wake_up argument to 1).4. The rigid-body is awake (by waking it up manually with RigidBody.wake_up or keeping the wake_up argument to its default value True).