The Rapier physics plugin

The easiest way to setup Rapier-based physics into Bevy is to use the RapierPhysicsPlugin:

use bevy::prelude::*;
use bevy_rapier2d::physics::RapierPhysicsPlugin;
fn main() {
App::build()
.add_plugin(RapierPhysicsPlugin)
.run();
}

Adding this plugin will result in the registration of all the Rapier resources necessary for the physics simulation. The following structures are added as individual resources by this plugin:

Resource typeDescription
rapier2d::geometry::BroadPhasefor the broad-phase stage of collision detection.
rapier2d::geometry::NarrowPhasefor the narrow-phase stage of collision detection.
rapier2d::geometry::ColliderSetfor the set of colliders.
rapier2d::dynamics::IntegrationParametersfor configuring the constraint solver.
rapier2d::dynamics::RigidBodySetfor the set of rigid-bodies.
rapier2d::dynamics::JointSetfor the set of joint constraints.
rapier2d::pipeline::PhysicsPipelinefor combining all the other elements to run the simulation step by step.
bevy_rapier2d::physics::Gravityfor configuring the gravity.
bevy_rapier2d::physics::EventQueuefor collecting the physics events (collision/proximity started/ended).

Each resource can be accessed by any system using the usual Bevy resource wrappers:

fn my_system(mut broad_phase: ResMut<BroadPhase>, rigid_bodies: Res<RigidBodySet>) {
// Do something.
}

The RapierPhysicsPlugin will actually register two other resources, namely EntityToBody and RapierPhysicsScale. However they will likely disappear in the future.

Besides these resources, the RapierPhysicsPlugin will register a few systems responsible for:

  • Building rigid-bodies, colliders, and joints based some transient components.
  • Performing one timestep at each update executed by the Bevy game loop.
  • Writing the Rapier rigid-bodies positions back into the Translation and Rotation components of the entities they are attached to.

Once the plugin is registered, the next step is to create rigid-bodies an colliders.

Creating rigid-bodies and colliders

Creating rigid-bodies and colliders using the Bevy plugin is mostly the same as when using Rapier directly as described in that section of the user-guide. You will need to:

  • Create a RigidBodyBuilder which is responsible for building a rigid-body.
  • Create a ColliderBuilder which is responsible for building a collider.

However, using the bevy_rapier plugin, the .build() method of these builders must not be called directly. Instead these builders are components to be attached to a Bevy entity:

use bevy::prelude::*;
use bevy_rapier2d::physics::RapierPhysicsPlugin;
use bevy_rapier2d::rapier::dynamics::RigidBodyBuilder;
use bevy_rapier2d::rapier::geometry::ColliderBuilder;
fn main() {
App::build()
.add_plugin(RapierPhysicsPlugin)
.add_startup_system(setup_physics.system())
.run();
}
fn setup_physics(mut commands: Commands) {
// Static rigid-body with a cuboid shape.
let rigid_body1 = RigidBodyBuilder::new_static();
let collider1 = ColliderBuilder::cuboid(10.0, 1.0);
commands.spawn((rigid_body1, collider1));
// Dynamic rigid-body with ball shape.
let rigid_body2 = RigidBodyBuilder::new_dynamic().translation(0.0, 3.0);
let collider2 = ColliderBuilder::ball(0.5);
commands.spawn((rigid_body2, collider2));
}

Once these components are added, three things will happen during the next step of the Bevy game loop:

  1. One of the systems automatically registered by the RapierPhysicsPlugin will read these builder components, call their .build() method, and add the newly built rigid-body/collider to the RigidBodySet and ColliderSet accessible through Bevy resources.
  2. If the rigid-body/collider creation succeeded, the builder components are removed from the entity.
  3. One RigidBodyHandleComponent and one ColliderHandleComponent will be created and attached to the entity being processed. These components contain the Rapier handles needed to retrieve the rigid-body or collider from the RigidBodySet and ColliderSet.RigidBodySet
warning

The current version of our bevy_rapier plugin is limited to one rigid-body and one collider per entity. An entity with a rigid-body but no collider is not supported. Nor is an entity with no rigid-body and a collider. Entities with multiple rigid-bodies or multiple colliders attached to them are not supported either. This will be improved in future versions of our plugins.

Creating joints

The creation of joints is slightly different from the creation of rigid-bodies and colliders because a joint affects two rigid-bodies at the same time. First joint parameters like BallJoint, FixedJoint, etc. have to be created and initialized, as usual. However instead of calling JointSet::insert_joint(...) directly to create the joint, we need to create a JointBuilderComponent which is responsible for letting the Rapier plugin know that a joint has to be created between two Bevy entities containing a rigid-body each:

use bevy::prelude::*;
use bevy_rapier2d::na::Point2;
use bevy_rapier2d::physics::{JointBuilderComponent, RapierPhysicsPlugin};
use bevy_rapier2d::rapier::dynamics::{BallJoint, RigidBodyBuilder};
use bevy_rapier2d::rapier::geometry::ColliderBuilder;
fn main() {
App::build()
.add_plugin(RapierPhysicsPlugin)
.add_startup_system(setup_physics.system())
.run();
}
fn setup_physics(mut commands: Commands) {
// Static rigid-body with a cuboid shape.
let rigid_body1 = RigidBodyBuilder::new_static();
let collider1 = ColliderBuilder::cuboid(10.0, 1.0);
// Keep the entity identifier.
let entity1 = commands
.spawn((rigid_body1, collider1))
.current_entity()
.unwrap();
// Dynamic rigid-body with ball shape.
let rigid_body2 = RigidBodyBuilder::new_dynamic().translation(0.0, 3.0);
let collider2 = ColliderBuilder::ball(0.5);
// Keep the entity identifier.
let entity2 = commands
.spawn((rigid_body2, collider2))
.current_entity()
.unwrap();
// Create the joint.
let joint_params = BallJoint::new(Point2::origin(), Point2::new(0.0, -3.0));
let joint_builder_component = JointBuilderComponent::new(joint_params, entity1, entity2);
commands.spawn((joint_builder_component,));
}

This JointBuilderComponent is a transient component that will cause one of the Rapier plugin systems to:

  1. Create the corresponding Joint and add it to the JointSet resource.
  2. Remove this JointBuilderComponent from the processed entity.
  3. Add a JointHandleComponent to the processed entity. This new component will contain an handle necessary to get a reference to the joint from the JointSet. It also contains the identifiers of the entity this joint is attached to.

Handling contact/proximity events

The RapierPhysicsPlugin automatically creates and add the EventQueue resource to the Bevy app. This event queue can be used to read and handle events generated by the physics pipeline:

use bevy::prelude::*;
use bevy_rapier2d::physics::{RapierPhysicsPlugin, EventQueue};
fn main() {
App::build()
.add_plugin(RapierPhysicsPlugin)
.add_system(print_events.system())
.run();
}
fn print_events(events: Res<EventQueue>) {
while let Ok(proximity_event) = events.proximity_events.pop() {
println!("Received proximity event: {:?}", proximity_event);
}
while let Ok(contact_event) = events.contact_events.pop() {
println!("Received contact event: {:?}", contact_event);
}
}

Going further

This is as far as the current capabilities of the bevy rapier plugin go. Keep in mind that you can still access directly all the Rapier structures, rigid-bodies, contact graph, etc. through the resources added by the RapierBevyPlugin. This gives you the same level of flexibility as using the original Rapier crate directly.print_events

In the next section, we are presenting a second plugin we provide in order to easily render the content of the Rapier resources.