bevy_rapier
bevy_rapier copied to clipboard
Inaccurate collisions when using a lot of `ColliderShape`s with the same height
When programming a simple platformer, building a straight path out of many ColliderShape
s and having a character walk on it doesn't work.
I am assuming that the issue is that the y-coordinate of every ColliderShape
is not exactly the same, because of f32
inaccuracies. So the character walks along the straight path but bumps into a block that isn't exactly on the same y-coordinate as the one before it.
Example code
use bevy::prelude::*;
use bevy_rapier2d::prelude::*;
const SCALE: f32 = 16.;
const PLATFORM_Y: f32 = -8.;
const TILES: isize = 100;
const CONTROLLER_JUMP_IMPULSE: f32 = 15.;
fn main() {
let mut app = App::new();
app.add_plugins(DefaultPlugins);
app.add_plugin(RapierRenderPlugin);
app.add_plugin(RapierPhysicsPlugin::<NoUserData>::default());
app.add_startup_system(setup)
.add_system(movement);
app.run()
}
#[derive(Component)]
struct Controller;
fn setup(mut commands: Commands, mut rapier_config: ResMut<RapierConfiguration>) {
commands
.spawn()
.insert_bundle(OrthographicCameraBundle::new_2d());
rapier_config.scale = SCALE;
commands
.spawn()
.insert_bundle(RigidBodyBundle {
mass_properties: RigidBodyMassPropsFlags::ROTATION_LOCKED.into(),
..Default::default()
})
.insert_bundle(SpriteBundle {
sprite: Sprite {
color: Color::rgb(1., 0., 0.),
custom_size: Some(Vec2::new(1. * SCALE, 2. * SCALE)),
..Default::default()
},
..Default::default()
})
.insert_bundle(ColliderBundle {
shape: ColliderShape::cuboid(0.5, 1.).into(),
..Default::default()
})
.insert(ColliderPositionSync::Discrete).insert(Controller);
for x in -TILES / 2..TILES / 2 {
commands
.spawn()
.insert_bundle(SpriteBundle {
sprite: Sprite {
color: Color::rgb(0., 1., 0.),
custom_size: Some(Vec2::new(1. * SCALE, 1. * SCALE)),
..Default::default()
},
transform: Transform::from_xyz(x as f32 * SCALE, PLATFORM_Y * SCALE, 0.),
..Default::default()
})
.insert_bundle(ColliderBundle {
shape: ColliderShape::cuboid(0.5, 0.5).into(),
position: [x as f32, PLATFORM_Y].into(),
..Default::default()
});
}
}
fn movement(
keyboard_input: Res<Input<KeyCode>>,
mut controller: Query<(
&mut RigidBodyForcesComponent,
&mut RigidBodyVelocityComponent,
&RigidBodyMassPropsComponent,
&mut ColliderMaterialComponent,
), With<Controller>>,
) {
let (mut rb_forces, mut rb_vel, rb_mprops, mut co_material) = controller.single_mut();
if keyboard_input.just_pressed(KeyCode::Space) {
rb_vel.apply_impulse(rb_mprops, Vec2::new(0., CONTROLLER_JUMP_IMPULSE).into());
}
let mut x_axis = 0.;
if keyboard_input.pressed(KeyCode::A) || keyboard_input.pressed(KeyCode::Left) {
x_axis -= 1.;
} else if keyboard_input.pressed(KeyCode::D) || keyboard_input.pressed(KeyCode::Right) {
x_axis += 1.;
}
if x_axis == 0. {
co_material.friction = 2.;
} else {
co_material.friction = 0.;
x_axis *= 25. * rb_mprops.mass();
rb_forces.force.x = x_axis;
}
}
Somebody else had this issue before, probably with a better explanation of what the issue is about: https://dev.to/sbelzile/making-games-in-rust-part-7-fixing-the-player-randomly-stuck-issue-3mj5.
I've run into this as well, but meshing all of the tiles is probably the right solution anyway. Still annoying that it blocks quick n' dirty tests.
I think that's a rapier
issue, but I agree it would be convenient to be able to have a smoother experience for "quick n' dirty tests".
Any update on reproduction code is appreciated.