Jondolf / avian

ECS-driven 2D and 3D physics engine for the Bevy game engine.
https://crates.io/crates/avian3d
Apache License 2.0
1.49k stars 119 forks source link

How to write a unit test for bevy_xpbd? #303

Open Netzeband opened 9 months ago

Netzeband commented 9 months ago

Hello,

I try to check some collision detection systems inside unit-tests. Here I noticed, that the collision detection seems not to work as expected. Just an example:

In this test, I spawn two entities with a collider. I spawn them on different positions, but then I move them to the same position. My expectation is, that after the next update of the app, I can find the collision in the CollidingEntities component of those entities.

If I implement the test like this, it works:

    #[test]
    fn test_collision2() {
        let mut app = App::new();
        app.add_plugins(MinimalPlugins);
        app.add_plugins(PhysicsPlugins::default());
        app.insert_resource(Gravity(Vector::NEG_Y * 0.0));

        let entity1_id = app.world.spawn((
            SpatialBundle {
                transform: Transform::from_translation(Vec3 {
                    x: -100.0,
                    y: 0.0,
                    z: 0.0
                }),
                ..default()
            },
            Collider::ball(10.0)
        )).id();

        app.world.spawn((
            SpatialBundle {
                transform: Transform::from_translation(Vec3 {
                    x: 0.0,
                    y: 0.0,
                    z: 0.0
                }),
                ..default()
            },
            Collider::ball(10.0)
        ));

        let mut entity1_transform = app.world.get_mut::<Transform>(entity1_id).unwrap();
        entity1_transform.translation = Vec3 {
            x: 0.0,
            y: 0.0,
            z: 0.0
        };

        app.update();

        let mut colliding_entities_query = app.world.query::<&CollidingEntities>();

        let number_of_collisions: usize = colliding_entities_query.iter(&app.world).map(|c| c.len()).sum();

        assert_eq!(2, number_of_collisions);  
    }

However, if I have an update between the spawning and the mutation of the translation of entity 1, it does not work anymore:

    #[test]
    fn test_collision2() {
        let mut app = App::new();
        app.add_plugins(MinimalPlugins);
        app.add_plugins(PhysicsPlugins::default());
        app.insert_resource(Gravity(Vector::NEG_Y * 0.0));

        let entity1_id = app.world.spawn((
            SpatialBundle {
                transform: Transform::from_translation(Vec3 {
                    x: -100.0,
                    y: 0.0,
                    z: 0.0
                }),
                ..default()
            },
            Collider::ball(10.0)
        )).id();

        app.world.spawn((
            SpatialBundle {
                transform: Transform::from_translation(Vec3 {
                    x: 0.0,
                    y: 0.0,
                    z: 0.0
                }),
                ..default()
            },
            Collider::ball(10.0)
        ));

        app.update();

        let mut entity1_transform = app.world.get_mut::<Transform>(entity1_id).unwrap();
        entity1_transform.translation = Vec3 {
            x: 0.0,
            y: 0.0,
            z: 0.0
        };

        app.update();

        let mut colliding_entities_query = app.world.query::<&CollidingEntities>();

        let number_of_collisions: usize = colliding_entities_query.iter(&app.world).map(|c| c.len()).sum();

        assert_eq!(2, number_of_collisions);   // error, this is 0
    }

From the documentation, I cannot see why this does not work. Is there any flag, I have to set in order to tell the physic system to perform another check on those entities?

Netzeband commented 9 months ago

Just another thing I noticed: It also does not work, if I have an update of the app before doing the spawns.