bevy_xpbd icon indicating copy to clipboard operation
bevy_xpbd copied to clipboard

Low mass joint with angle limits unstable

Open francisdb opened this issue 1 week ago • 0 comments
trafficstars

Only tested in 2D

bevy = "0.17.2"
avian2d = "0.4.1"

https://github.com/user-attachments/assets/624a8de7-f346-4977-9715-7353fb03c8a3

  • Decreasing the SubstepCount improves the situation
  • Increasing the Mass improves the situation but even with 0.1 (100g) it stays unstable
  • When the mass goes even lower (0.0001) the angle limits are no longer respected.
use avian2d::{math::*, prelude::*};
use bevy::color::palettes::css;
use bevy::prelude::*;

#[derive(Component)]
struct Flipper;

fn main() {
    App::new()
        .add_plugins((DefaultPlugins, PhysicsPlugins::default()))
        .insert_resource(SubstepCount(50))
        .insert_resource(Gravity(Vector::NEG_Y * 9.81))
        .add_systems(Startup, setup)
        .add_systems(Update, keyboard_control)
        .run();
}

const FLIPPER_LENGTH: f32 = 0.071;
const FLIPPER_WIDTH: f32 = 0.018;

fn setup(
    mut commands: Commands,
    mut meshes: ResMut<Assets<Mesh>>,
    mut materials: ResMut<Assets<ColorMaterial>>,
) {
    // we model a pinball flipper in actual size
    commands.spawn((
        Camera2d,
        Projection::Orthographic(OrthographicProjection {
            scale: 0.0002,
            ..OrthographicProjection::default_2d()
        }),
    ));

    let anchor = commands
        .spawn((
            Mesh2d(meshes.add(Mesh::from(Circle::new(0.002)))),
            MeshMaterial2d(materials.add(ColorMaterial::from(Color::from(css::YELLOW)))),
            RigidBody::Static,
            Transform::from_xyz(0.0, 0.0, 1.0),
        ))
        .id();

    let flipper = commands
        .spawn((
            Flipper,
            Mesh2d(meshes.add(Mesh::from(Rectangle::new(FLIPPER_LENGTH, FLIPPER_WIDTH)))),
            MeshMaterial2d(materials.add(ColorMaterial::from(Color::from(css::TEAL)))),
            RigidBody::Dynamic,
            Mass::from(0.01), // 10g
            Collider::rectangle(FLIPPER_LENGTH, FLIPPER_WIDTH),
            AngularDamping::from(10.0),
        ))
        .id();

    commands.spawn(
        RevoluteJoint::new(anchor, flipper)
            .with_local_anchor1(Vec2::ZERO)
            .with_local_anchor2(Vec2::new(-FLIPPER_LENGTH / 2.0, 0.0))
            .with_angle_limits((-30.0f32).to_radians(), (30.0f32).to_radians()),
    );
}

fn keyboard_control(
    keyboard_input: Res<ButtonInput<KeyCode>>,
    flippers: Query<Entity, With<Flipper>>,
    mut commands: Commands,
) {
    for entity in flippers.iter() {
        if keyboard_input.pressed(KeyCode::ShiftLeft) {
            commands.entity(entity).insert(ConstantTorque(0.05));
        } else if keyboard_input.pressed(KeyCode::ShiftRight) {
            commands.entity(entity).insert(ConstantTorque(-0.05));
        } else {
            commands.entity(entity).remove::<ConstantTorque>();
        }
    }
}

francisdb avatar Nov 07 '25 11:11 francisdb