bevy_rapier icon indicating copy to clipboard operation
bevy_rapier copied to clipboard

Despawn events are missed in fixed timestep simulation with low tick rate

Open hikikones opened this issue 1 year ago • 0 comments

Detection for despawns/removed components are now backed by events (#324). However, since events only persist for 2 frames, they will likely be missed by a fixed timestep simulation with low tick rate. This results in colliders/rigid bodies still persisting when they should have been despawned.

Here is an example where a cube is despawned, but the collider remains visible:

Example
use bevy::prelude::*;
use bevy_rapier3d::prelude::*;

const PHYSICS_DELTA: f32 = 1.0 / 4.0;

fn main() {
    App::new()
        .insert_resource(ClearColor(Color::BLACK))
        .insert_resource(RapierConfiguration {
            timestep_mode: TimestepMode::Fixed {
                dt: PHYSICS_DELTA,
                substeps: 1,
            },
            ..Default::default()
        })
        .insert_resource(FixedTime::new_from_secs(PHYSICS_DELTA))
        .add_plugins(DefaultPlugins)
        .add_plugin(RapierPhysicsPlugin::<NoUserData>::default().with_default_system_setup(false))
        .add_plugin(RapierDebugRenderPlugin::default())
        .add_startup_system(setup)
        .edit_schedule(CoreSchedule::FixedUpdate, |schedule| {
            schedule
                .configure_sets(
                    (
                        PhysicsSet::SyncBackend,
                        PhysicsSet::SyncBackendFlush,
                        PhysicsSet::StepSimulation,
                        PhysicsSet::Writeback,
                    )
                        .chain(),
                )
                .add_systems(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsSet::SyncBackend)
                        .in_base_set(PhysicsSet::SyncBackend),
                )
                .add_systems(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsSet::SyncBackendFlush)
                        .in_base_set(PhysicsSet::SyncBackendFlush),
                )
                .add_systems(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsSet::StepSimulation)
                        .in_base_set(PhysicsSet::StepSimulation),
                )
                .add_system(despawn.in_base_set(PhysicsSet::StepSimulation))
                .add_systems(
                    RapierPhysicsPlugin::<NoUserData>::get_systems(PhysicsSet::Writeback)
                        .in_base_set(PhysicsSet::Writeback),
                );
        })
        // .add_system(systems::sync_removals.in_base_set(CoreSet::Last))
        .run();
}

fn setup(mut commands: Commands) {
    // Camera
    commands.spawn(Camera3dBundle {
        transform: Transform::from_xyz(5.0, 5.0, 5.0).looking_at(Vec3::ZERO, Vec3::Y),
        ..Default::default()
    });

    // Cube
    commands.spawn((TransformBundle::IDENTITY, Collider::cuboid(1.0, 1.0, 1.0)));
}

fn despawn(cube_q: Query<Entity, With<Collider>>, mut commands: Commands) {
    if let Ok(entity) = cube_q.get_single() {
        println!("Despawn!");
        commands.entity(entity).despawn();
    }
}

Adding the sync_removals system to CoreSet::Last resolves this issue. Should this perhaps be the default, as it was before with the DetectDespawn stage?

hikikones avatar Mar 15 '23 08:03 hikikones