bevy_xpbd icon indicating copy to clipboard operation
bevy_xpbd copied to clipboard

Infinitely bouncing ball

Open Wolftousen opened this issue 2 years ago • 8 comments
trafficstars

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.

Wolftousen avatar Oct 27 '23 14:10 Wolftousen

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.

Jondolf avatar Oct 27 '23 18:10 Jondolf

I was not using main, but switched to it. It does bounce now, but bouncing quickly fades and doesn't maintain height

Wolftousen avatar Oct 28 '23 04:10 Wolftousen

Nevermind, was just visually hard to tell how high it was bouncing until I added a bar above it

Wolftousen avatar Oct 28 '23 04:10 Wolftousen

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),
        ));
    }

Wolftousen avatar Oct 28 '23 05:10 Wolftousen

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)),
    ));

Jondolf avatar Oct 28 '23 10:10 Jondolf

Yeah, removing friction worked. I did some more testing and with no friction the following happens:

  1. No extra velocities, ball bounces forever.
  2. Linear Velocity > 0 & Angular Velocity = 0, ball bounces forever.
  3. Linear Velocity = 0 & Angular Velocity > 0, ball bounce decays very quickly.
  4. 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.

Wolftousen avatar Oct 29 '23 05:10 Wolftousen

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.

Jondolf avatar Oct 29 '23 20:10 Jondolf

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

Jondolf avatar Oct 29 '23 20:10 Jondolf