When to use this skill
- Add 2D/3D rigid bodies, colliders, sensors, joints, or character movement.
- Decide between
bevy_rapierand an alternative such as Avian. - Place input/gameplay systems around a physics step without transform jitter.
- Read collision messages or perform ray, shape, point, and overlap queries.
- Configure headless physics, interpolation, CCD, collision groups, or tests.
Bevy core does not include a physics engine. For Bevy 0.19, use
bevy_rapier2d/bevy_rapier3d 0.36 for the mature Rapier integration. Consider
avian2d/avian3d 0.7 when its ECS-native architecture or modular solver is the
specific reason to choose it. Do not install both engines on the same simulated
entities.
Canonical fixed-step Rapier 3D pattern
[dependencies]
bevy = "0.19"
bevy_rapier3d = "0.36"
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;
const PHYSICS_HZ: f64 = 60.0;
const PHYSICS_DT: f32 = 1.0 / PHYSICS_HZ as f32;
#[derive(Component)]
struct Player;
#[derive(SystemSet, Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum GamePhysics {
Drive,
React,
}
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins)
.insert_resource(Time::<Fixed>::from_hz(PHYSICS_HZ))
.insert_resource(TimestepMode::Fixed {
dt: PHYSICS_DT,
substeps: 1,
})
.add_plugins((
RapierPhysicsPlugin::<NoUserData>::default().in_fixed_schedule(),
RapierDebugRenderPlugin::default(),
))
.configure_sets(
FixedUpdate,
(
GamePhysics::Drive.before(PhysicsSet::SyncBackend),
GamePhysics::React.after(PhysicsSet::Writeback),
),
)
.add_systems(Startup, setup)
.add_systems(FixedUpdate, drive_player.in_set(GamePhysics::Drive))
.add_systems(FixedUpdate, react_to_collisions.in_set(GamePhysics::React));
app.run();
}
fn setup(mut commands: Commands) {
commands.spawn((
Camera3d::default(),
Transform::from_xyz(8.0, 6.0, 12.0).looking_at(Vec3::ZERO, Vec3::Y),
));
// A collider without RigidBody is fixed. Cuboid arguments are half-extents.
commands.spawn((
Name::new("Ground"),
Collider::cuboid(8.0, 0.25, 8.0),
Transform::from_xyz(0.0, -0.25, 0.0),
));
commands.spawn((
Name::new("Player"),
Player,
RigidBody::Dynamic,
Collider::ball(0.5),
Velocity::linear(Vec3::new(1.5, 0.0, 0.0)),
ActiveEvents::COLLISION_EVENTS,
Transform::from_xyz(0.0, 4.0, 0.0),
));
}
fn drive_player(mut bodies: Query<&mut Velocity, With<Player>>) {
for mut velocity in &mut bodies {
velocity.linear.x = 1.5;
}
}
fn react_to_collisions(mut events: MessageReader<CollisionEvent>) {
for event in events.read() {
if let CollisionEvent::Started(a, b, _) = event {
let _pair = (*a, *b);
}
}
}
The plugin normally runs in PostUpdate with TimestepMode::Variable. This example
deliberately moves it to FixedUpdate, makes Rapier's dt match Bevy's fixed clock,
drives bodies before PhysicsSet::SyncBackend, and reacts after
PhysicsSet::Writeback. Keep the default schedule if frame-coupled simulation is the
intent; order systems in that same schedule.
Choose the relevant deep dive
| Problem | Read |
|---|---|
| Cargo profiles, default vs fixed/interpolated stepping, multiple worlds | Setup and scheduling |
| Body types, collider shapes, mass, units, CCD, transform authority | Bodies and colliders |
| Collision messages, sensors, groups, ray/shape queries, hooks | Events, queries, and filtering |
| Kinematic character motion, controller output, joints | Controllers and joints |
| Rollback, determinism limits, headless tests, performance evidence | Determinism and testing |
Bevy 0.19 / Rapier 0.36 gotchas
Velocityfields arelinearandangular, notlinvelandangvel.CollisionEventandContactForceEventare messages. At least one collider must enable the correspondingActiveEventsflag.ReadRapierContextis aSystemParam; call.single()and handle itsResult.RapierContextis not a global resource and Rapier supports multiple worlds.- Dynamic bodies own their simulated pose. Repeatedly writing their
Transformis a teleport and commonly causes jitter; write forces, impulses, or velocity instead. - Kinematic bodies follow the trajectory you prescribe and do not avoid obstacles by
themselves. Use scene queries or
KinematicCharacterController. - Rapier's default Cargo features pull Bevy asset/render helpers. Disable defaults for
headless or external-renderer builds and enable the correct
dim2/dim3feature. RapierDebugRenderPluginis diagnostic visualization, not a renderer or a shipping requirement.
See also
bevy-core-concepts— fixed schedules and ordering.bevy-rendering— rendering and physics ownership boundary.bevy-cameras— camera rays for picking and aiming.- Third-person orbit camera — Rapier sphere-cast obstruction and recovery hysteresis.
bevy-input-actions— fixed-tick movement/aim commands.bevy-testing— deterministic app stepping and headless worlds.bevy-a11y— accessible movement, timing, and aim alternatives.bevy-voxel-pipeline— collider proxies for voxel worlds.