bevy_xpbd
bevy_xpbd copied to clipboard
Infinitely bouncing ball
Was tinkering around and was trying to make a ball bounce infinitely. Usually, you can accomplish this by just setting the restitution to 1 and the calculation to max. However, doing this results in the ball bouncing 1 time and then stopping. Here is the code I was using:
use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
use bevy_xpbd_2d::{math::*, prelude::*};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PhysicsPlugins::default())
.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
.insert_resource(SubstepCount(6))
.insert_resource(Gravity(Vector::NEG_Y * 1000.0))
.add_systems(Startup, setup)
.add_systems(PostUpdate, collision_event)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2dBundle::default());
commands.spawn((
MaterialMesh2dBundle {
mesh: meshes.add(shape::Circle::new(10.).into()).into(),
material: materials.add(ColorMaterial::from(Color::GREEN)),
transform: Transform::from_translation(Vec3::new(0., 0., 0.)),
..default()
},
RigidBody::Dynamic,
Restitution::new(1.0).with_combine_rule(CoefficientCombine::Max),
Position(Vec2::new(0.0, 0.0)),
Collider::ball(10.0)
));
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::rgb(0.7, 0.7, 0.8),
custom_size: Some(Vec2::splat(1.0)),
..default()
},
transform: Transform {
scale: Vec3::new(1000.0, 5.0, 1.0),
translation: Vec3::new(0.0, -50.0, 0.0),
rotation: Quat::IDENTITY
},
..default()
},
RigidBody::Static,
Position(Vec2::new(0.0, -50.0)),
Collider::cuboid(200.0, 5.0),
));
}
fn collision_event(mut collision_event_reader: EventReader<Collision>) {
for Collision(contact) in collision_event_reader.iter() {
println!("Normal: {:?}", contact.normal);
}
}
I've tried both with optimizations and without, same deal. I get similar issues with Rapier trying this, but with Rapier it was specific to which monitor I was having bevy start on (which is really weird), but no difference for doing it with xpbd.
Did you test this in 0.2 or the main branch of this repo? I think a similar bouncing issue was fixed on main a while ago so it could be fixed, but this could of course be a separate problem as well.
I was not using main, but switched to it. It does bounce now, but bouncing quickly fades and doesn't maintain height
Nevermind, was just visually hard to tell how high it was bouncing until I added a bar above it
Actually did find a case where it does stop bouncing at full intensity. I added 20 x linear velocity to the ball and by the time it gets to the end of the platform it is no longer bouncing at full height.
use bevy::{prelude::*, sprite::MaterialMesh2dBundle};
use bevy_xpbd_2d::{math::*, prelude::*};
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PhysicsPlugins::default())
.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
.insert_resource(SubstepCount(6))
.insert_resource(Gravity(Vector::NEG_Y * 1000.0))
.add_systems(Startup, setup)
.run();
}
fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
commands.spawn(Camera2dBundle::default());
commands.spawn((
MaterialMesh2dBundle {
mesh: meshes.add(shape::Circle::new(10.).into()).into(),
material: materials.add(ColorMaterial::from(Color::GREEN)),
..default()
},
RigidBody::Dynamic,
Restitution::new(1.0).with_combine_rule(CoefficientCombine::Max),
Position(Vec2::new(0.0, 50.0)),
Collider::ball(10.0),
LinearVelocity(Vec2::new(20.0, 0.0)),
));
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::rgb(1.0, 0.0, 0.0),
custom_size: Some(Vec2::splat(1.0)),
..default()
},
transform: Transform {
scale: Vec3::new(1000.0, 1.0, 1.0),
translation: Vec3::new(0.0, 61.0, 0.0),
..default()
},
..default()
},
));
commands.spawn((
SpriteBundle {
sprite: Sprite {
color: Color::rgb(1.0, 1.0, 1.0),
custom_size: Some(Vec2::splat(1.0)),
..default()
},
transform: Transform::from_scale(Vec3::new(1000.0, 5.0, 1.0)),
..default()
},
RigidBody::Static,
Position(Vec2::new(0.0, 0.0)),
Collider::cuboid(1.0, 1.0),
));
}
I'm pretty sure this is because the ball has friction and is moving horizontally. It makes the ball spin when it hits the ground, and the spin makes it slightly change trajectory when hittting the ground.
With this, it looks better to me:
commands.spawn((
MaterialMesh2dBundle {
mesh: meshes.add(shape::Circle::new(10.).into()).into(),
material: materials.add(ColorMaterial::from(Color::GREEN)),
..default()
},
RigidBody::Dynamic,
Restitution::new(1.0).with_combine_rule(CoefficientCombine::Max),
Friction::ZERO.with_combine_rule(CoefficientCombine::Min),
Position(Vec2::new(0.0, 50.0)),
Collider::ball(10.0),
LinearVelocity(Vec2::new(20.0, 0.0)),
));
Yeah, removing friction worked. I did some more testing and with no friction the following happens:
- No extra velocities, ball bounces forever.
- Linear Velocity > 0 & Angular Velocity = 0, ball bounces forever.
- Linear Velocity = 0 & Angular Velocity > 0, ball bounce decays very quickly.
- Linear Velocity > 0 & Angular Velocity > 0, ball bounce decays, slower than case # 3. Linear Velocity to Angular Velocity ratio seems to effect the speed of decay.
- Initial height of ball from platform does not matter.
- Ratio of Angular Velocity to initial height of ball does not matter.
So there seems to be some weird interaction between Angular Velocity and Restitution going on that I wouldn't expect.
Wont really effect my end goal and may not really be a bug, but probably worth a note somewhere.
Yeah, that sounds like it could be a bug. I don't see why the angular velocity would do anything to the ball if there's no friction. Thanks for testing so many cases though, I'll try to see what's causing this when I have time.
Actually I think the computation used for computing the velocity at the contact point uses angular velocity, and that value is used when computing the restitution impulse. So the restitution in fact seems to be dependent on the angular velocity in some way even if there is no friction. It doesn't logically make sense to me though, so I'll have to look into it more