Skip to content

Commit 57b635c

Browse files
committed
feat! port platformer example from bevy_rapier to avian
Being a native bevy plugin, avian provides support for new bevy versions quickly and is not plagued by dependency incompatibilities (like requiring rapier and bevy to use the same version of glam).
1 parent 564dcb7 commit 57b635c

9 files changed

Lines changed: 117 additions & 99 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ derive_more = "0.99.20"
2929

3030
[dev-dependencies]
3131
bevy = "0.18"
32-
bevy_rapier2d = { git = "https://github.com/buncys/bevy_rapier.git", rev = "8c25504d336c8c9fc7a3c765422d56845cc36075" }
32+
avian2d = "0.6"
3333
fake = { version = "4.4", features = ["uuid"] }
3434
rand = "0.9"
3535
bevy-inspector-egui = "0.36.0"

examples/platformer/climbing.rs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use avian2d::prelude::*;
12
use bevy::{platform::collections::HashSet, prelude::*};
23
use bevy_ecs_ldtk::prelude::*;
3-
use bevy_rapier2d::prelude::*;
44

55
use crate::colliders::SensorBundle;
66

@@ -23,35 +23,43 @@ pub struct LadderBundle {
2323
pub fn detect_climb_range(
2424
mut climbers: Query<&mut Climber>,
2525
climbables: Query<Entity, With<Climbable>>,
26-
mut collisions: MessageReader<CollisionEvent>,
26+
mut collision_starts: MessageReader<CollisionStart>,
27+
mut collision_ends: MessageReader<CollisionEnd>,
2728
) {
28-
for collision in collisions.read() {
29-
match collision {
30-
CollisionEvent::Started(collider_a, collider_b, _) => {
31-
if let (Ok(mut climber), Ok(climbable)) =
32-
(climbers.get_mut(*collider_a), climbables.get(*collider_b))
33-
{
34-
climber.intersecting_climbables.insert(climbable);
35-
}
36-
if let (Ok(mut climber), Ok(climbable)) =
37-
(climbers.get_mut(*collider_b), climbables.get(*collider_a))
38-
{
39-
climber.intersecting_climbables.insert(climbable);
40-
};
41-
}
42-
CollisionEvent::Stopped(collider_a, collider_b, _) => {
43-
if let (Ok(mut climber), Ok(climbable)) =
44-
(climbers.get_mut(*collider_a), climbables.get(*collider_b))
45-
{
46-
climber.intersecting_climbables.remove(&climbable);
47-
}
29+
for CollisionStart {
30+
collider1,
31+
collider2,
32+
..
33+
} in collision_starts.read()
34+
{
35+
if let (Ok(mut climber), Ok(climbable)) =
36+
(climbers.get_mut(*collider1), climbables.get(*collider2))
37+
{
38+
climber.intersecting_climbables.insert(climbable);
39+
}
40+
if let (Ok(mut climber), Ok(climbable)) =
41+
(climbers.get_mut(*collider2), climbables.get(*collider1))
42+
{
43+
climber.intersecting_climbables.insert(climbable);
44+
};
45+
}
46+
47+
for CollisionEnd {
48+
collider1,
49+
collider2,
50+
..
51+
} in collision_ends.read()
52+
{
53+
if let (Ok(mut climber), Ok(climbable)) =
54+
(climbers.get_mut(*collider1), climbables.get(*collider2))
55+
{
56+
climber.intersecting_climbables.remove(&climbable);
57+
}
4858

49-
if let (Ok(mut climber), Ok(climbable)) =
50-
(climbers.get_mut(*collider_b), climbables.get(*collider_a))
51-
{
52-
climber.intersecting_climbables.remove(&climbable);
53-
}
54-
}
59+
if let (Ok(mut climber), Ok(climbable)) =
60+
(climbers.get_mut(*collider2), climbables.get(*collider1))
61+
{
62+
climber.intersecting_climbables.remove(&climbable);
5563
}
5664
}
5765
}

examples/platformer/colliders.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
use bevy::prelude::*;
22
use bevy_ecs_ldtk::prelude::*;
33

4-
use bevy_rapier2d::prelude::*;
4+
use avian2d::prelude::*;
55

66
#[derive(Clone, Default, Bundle, LdtkIntCell)]
77
pub struct ColliderBundle {
88
pub collider: Collider,
99
pub rigid_body: RigidBody,
10-
pub velocity: Velocity,
10+
pub velocity: LinearVelocity,
1111
pub rotation_constraints: LockedAxes,
1212
pub gravity_scale: GravityScale,
1313
pub friction: Friction,
14-
pub density: ColliderMassProperties,
14+
pub density: ColliderDensity,
1515
}
1616

1717
impl From<&EntityInstance> for ColliderBundle {
@@ -20,28 +20,29 @@ impl From<&EntityInstance> for ColliderBundle {
2020

2121
match entity_instance.identifier.as_ref() {
2222
"Player" => ColliderBundle {
23-
collider: Collider::cuboid(6., 14.),
23+
collider: Collider::rectangle(12., 28.),
2424
rigid_body: RigidBody::Dynamic,
2525
friction: Friction {
26-
coefficient: 0.0,
27-
combine_rule: CoefficientCombineRule::Min,
26+
dynamic_coefficient: 0.0,
27+
static_coefficient: 0.0,
28+
combine_rule: CoefficientCombine::Min,
2829
},
2930
rotation_constraints,
3031
..Default::default()
3132
},
3233
"Mob" => ColliderBundle {
33-
collider: Collider::cuboid(5., 5.),
34-
rigid_body: RigidBody::KinematicVelocityBased,
34+
collider: Collider::rectangle(10., 10.),
35+
rigid_body: RigidBody::Kinematic,
3536
rotation_constraints,
3637
..Default::default()
3738
},
3839
"Chest" => ColliderBundle {
39-
collider: Collider::cuboid(8., 8.),
40+
collider: Collider::rectangle(16., 16.),
4041
rigid_body: RigidBody::Dynamic,
4142
rotation_constraints,
4243
gravity_scale: GravityScale(1.0),
4344
friction: Friction::new(0.5),
44-
density: ColliderMassProperties::Density(15.0),
45+
density: ColliderDensity(15.0),
4546
..Default::default()
4647
},
4748
_ => ColliderBundle::default(),
@@ -53,7 +54,7 @@ impl From<&EntityInstance> for ColliderBundle {
5354
pub struct SensorBundle {
5455
pub collider: Collider,
5556
pub sensor: Sensor,
56-
pub active_events: ActiveEvents,
57+
pub collision_events: CollisionEventsEnabled,
5758
pub rotation_constraints: LockedAxes,
5859
}
5960

@@ -64,10 +65,10 @@ impl From<IntGridCell> for SensorBundle {
6465
// ladder
6566
if int_grid_cell.value == 2 {
6667
SensorBundle {
67-
collider: Collider::cuboid(8., 8.),
68+
collider: Collider::rectangle(16., 16.),
6869
sensor: Sensor,
6970
rotation_constraints,
70-
active_events: ActiveEvents::COLLISION_EVENTS,
71+
collision_events: CollisionEventsEnabled,
7172
}
7273
} else {
7374
SensorBundle::default()

examples/platformer/enemy.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use avian2d::prelude::*;
12
use bevy::prelude::*;
23
use bevy_ecs_ldtk::{prelude::*, utils::ldtk_pixel_coords_to_translation_pivoted};
3-
use bevy_rapier2d::dynamics::Velocity;
44

55
use crate::colliders::ColliderBundle;
66

@@ -71,7 +71,7 @@ impl LdtkEntity for Patrol {
7171
}
7272
}
7373

74-
pub fn patrol(mut query: Query<(&mut Transform, &mut Velocity, &mut Patrol)>) {
74+
pub fn patrol(mut query: Query<(&mut Transform, &mut LinearVelocity, &mut Patrol)>) {
7575
for (mut transform, mut velocity, mut patrol) in &mut query {
7676
if patrol.points.len() <= 1 {
7777
continue;
@@ -80,7 +80,7 @@ pub fn patrol(mut query: Query<(&mut Transform, &mut Velocity, &mut Patrol)>) {
8080
let mut new_velocity =
8181
(patrol.points[patrol.index] - transform.translation.truncate()).normalize() * 75.;
8282

83-
if new_velocity.dot(velocity.linvel) < 0. {
83+
if new_velocity.dot(**velocity) < 0. {
8484
if patrol.index == 0 {
8585
patrol.forward = true;
8686
} else if patrol.index == patrol.points.len() - 1 {
@@ -100,7 +100,7 @@ pub fn patrol(mut query: Query<(&mut Transform, &mut Velocity, &mut Patrol)>) {
100100
(patrol.points[patrol.index] - transform.translation.truncate()).normalize() * 75.;
101101
}
102102

103-
velocity.linvel = new_velocity;
103+
**velocity = new_velocity;
104104
}
105105
}
106106

examples/platformer/game_flow.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,10 @@
11
use crate::player::Player;
22
use bevy::prelude::*;
33
use bevy_ecs_ldtk::prelude::*;
4-
use bevy_rapier2d::prelude::*;
54

6-
pub fn setup(
7-
mut commands: Commands,
8-
asset_server: Res<AssetServer>,
9-
mut rapier_config: Query<&mut RapierConfiguration>,
10-
) -> Result {
5+
pub fn setup(mut commands: Commands, asset_server: Res<AssetServer>) -> Result {
116
commands.spawn(Camera2d);
127

13-
rapier_config.single_mut()?.gravity = Vec2::new(0.0, -2000.0);
14-
158
let ldtk_handle = asset_server
169
.load("Typical_2D_platformer_example.ldtk")
1710
.into();

examples/platformer/ground_detection.rs

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use std::collections::HashSet;
22

33
use bevy::prelude::*;
44

5-
use bevy_rapier2d::prelude::*;
5+
use avian2d::prelude::*;
66

77
#[derive(Component)]
88
pub struct GroundSensor {
@@ -20,20 +20,20 @@ pub fn spawn_ground_sensor(
2020
detect_ground_for: Query<(Entity, &Collider), Added<GroundDetection>>,
2121
) {
2222
for (entity, shape) in &detect_ground_for {
23-
if let Some(cuboid) = shape.as_cuboid() {
23+
if let Some(cuboid) = shape.shape().as_cuboid() {
2424
let Vec2 {
2525
x: half_extents_x,
2626
y: half_extents_y,
27-
} = cuboid.half_extents();
27+
} = cuboid.half_extents;
2828

29-
let detector_shape = Collider::cuboid(half_extents_x / 2.0, 2.);
29+
let detector_shape = Collider::rectangle(half_extents_x, 4.);
3030

3131
let sensor_translation = Vec3::new(0., -half_extents_y, 0.);
3232

3333
commands.entity(entity).with_children(|builder| {
3434
builder
3535
.spawn_empty()
36-
.insert(ActiveEvents::COLLISION_EVENTS)
36+
.insert(CollisionEventsEnabled)
3737
.insert(detector_shape)
3838
.insert(Sensor)
3939
.insert(Transform::from_translation(sensor_translation))
@@ -49,32 +49,40 @@ pub fn spawn_ground_sensor(
4949

5050
pub fn ground_detection(
5151
mut ground_sensors: Query<&mut GroundSensor>,
52-
mut collisions: MessageReader<CollisionEvent>,
52+
mut collision_starts: MessageReader<CollisionStart>,
53+
mut collision_ends: MessageReader<CollisionEnd>,
5354
collidables: Query<Entity, (With<Collider>, Without<Sensor>)>,
5455
) {
55-
for collision_event in collisions.read() {
56-
match collision_event {
57-
CollisionEvent::Started(e1, e2, _) => {
58-
if collidables.contains(*e1) {
59-
if let Ok(mut sensor) = ground_sensors.get_mut(*e2) {
60-
sensor.intersecting_ground_entities.insert(*e1);
61-
}
62-
} else if collidables.contains(*e2) {
63-
if let Ok(mut sensor) = ground_sensors.get_mut(*e1) {
64-
sensor.intersecting_ground_entities.insert(*e2);
65-
}
66-
}
56+
for CollisionStart {
57+
collider1,
58+
collider2,
59+
..
60+
} in collision_starts.read()
61+
{
62+
if collidables.contains(*collider1) {
63+
if let Ok(mut sensor) = ground_sensors.get_mut(*collider2) {
64+
sensor.intersecting_ground_entities.insert(*collider1);
6765
}
68-
CollisionEvent::Stopped(e1, e2, _) => {
69-
if collidables.contains(*e1) {
70-
if let Ok(mut sensor) = ground_sensors.get_mut(*e2) {
71-
sensor.intersecting_ground_entities.remove(e1);
72-
}
73-
} else if collidables.contains(*e2) {
74-
if let Ok(mut sensor) = ground_sensors.get_mut(*e1) {
75-
sensor.intersecting_ground_entities.remove(e2);
76-
}
77-
}
66+
} else if collidables.contains(*collider2) {
67+
if let Ok(mut sensor) = ground_sensors.get_mut(*collider1) {
68+
sensor.intersecting_ground_entities.insert(*collider2);
69+
}
70+
}
71+
}
72+
73+
for CollisionEnd {
74+
collider1,
75+
collider2,
76+
..
77+
} in collision_ends.read()
78+
{
79+
if collidables.contains(*collider1) {
80+
if let Ok(mut sensor) = ground_sensors.get_mut(*collider2) {
81+
sensor.intersecting_ground_entities.remove(collider1);
82+
}
83+
} else if collidables.contains(*collider2) {
84+
if let Ok(mut sensor) = ground_sensors.get_mut(*collider1) {
85+
sensor.intersecting_ground_entities.remove(collider2);
7886
}
7987
}
8088
}

examples/platformer/main.rs

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use bevy::prelude::*;
55
use bevy_ecs_ldtk::prelude::*;
66

7-
use bevy_rapier2d::prelude::*;
7+
use avian2d::prelude::*;
88

99
mod camera;
1010
mod climbing;
@@ -22,10 +22,13 @@ mod walls;
2222
fn main() {
2323
App::new()
2424
.add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest()))
25-
.add_plugins((
26-
LdtkPlugin,
27-
RapierPhysicsPlugin::<NoUserData>::pixels_per_meter(100.0),
28-
))
25+
.add_plugins((LdtkPlugin, PhysicsPlugins::default()))
26+
.insert_resource(Gravity(Vec2::new(0.0, -2000.0)))
27+
.insert_resource({
28+
let mut time = Time::<Physics>::default();
29+
time.pause();
30+
time
31+
})
2932
.insert_resource(LevelSelection::Uid(0))
3033
.insert_resource(LdtkSettings {
3134
level_spawn_behavior: LevelSpawnBehavior::UseWorldTranslation {

examples/platformer/player.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
use avian2d::prelude::*;
12
use bevy::prelude::*;
23
use bevy_ecs_ldtk::prelude::*;
3-
use bevy_rapier2d::dynamics::Velocity;
44

55
use crate::{climbing::Climber, inventory::Inventory};
66
use crate::{colliders::ColliderBundle, ground_detection::GroundDetection};
@@ -31,13 +31,13 @@ pub struct PlayerBundle {
3131

3232
pub fn player_movement(
3333
input: Res<ButtonInput<KeyCode>>,
34-
mut query: Query<(&mut Velocity, &mut Climber, &GroundDetection), With<Player>>,
34+
mut query: Query<(&mut LinearVelocity, &mut Climber, &GroundDetection), With<Player>>,
3535
) {
3636
for (mut velocity, mut climber, ground_detection) in &mut query {
3737
let right = if input.pressed(KeyCode::KeyD) { 1. } else { 0. };
3838
let left = if input.pressed(KeyCode::KeyA) { 1. } else { 0. };
3939

40-
velocity.linvel.x = (right - left) * 200.;
40+
velocity.x = (right - left) * 200.;
4141

4242
if climber.intersecting_climbables.is_empty() {
4343
climber.climbing = false;
@@ -49,11 +49,11 @@ pub fn player_movement(
4949
let up = if input.pressed(KeyCode::KeyW) { 1. } else { 0. };
5050
let down = if input.pressed(KeyCode::KeyS) { 1. } else { 0. };
5151

52-
velocity.linvel.y = (up - down) * 200.;
52+
velocity.y = (up - down) * 200.;
5353
}
5454

5555
if input.just_pressed(KeyCode::Space) && (ground_detection.on_ground || climber.climbing) {
56-
velocity.linvel.y = 500.;
56+
velocity.y = 500.;
5757
climber.climbing = false;
5858
}
5959
}

0 commit comments

Comments
 (0)