Skip to content

Commit b9bbc4d

Browse files
committed
fix sync_removals with low physics fixed rate
1 parent ce3847c commit b9bbc4d

File tree

1 file changed

+94
-0
lines changed

1 file changed

+94
-0
lines changed

Diff for: src/plugin/plugin.rs

+94
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,10 @@ where
240240
);
241241
app.add_systems(PreStartup, insert_default_world);
242242

243+
// These *must* be in the main schedule currently so that they do not miss events.
244+
// See test `test_sync_removal` for an example of this.
245+
app.add_systems(PostUpdate, (systems::sync_removals,));
246+
243247
// Add each set as necessary
244248
if self.default_system_setup {
245249
app.configure_sets(
@@ -491,4 +495,94 @@ mod test {
491495
));
492496
}
493497
}
498+
499+
#[test]
500+
pub fn test_sync_removal() {
501+
return main();
502+
503+
use bevy::prelude::*;
504+
505+
fn run_test(app: &mut App) {
506+
app.insert_resource(TimeUpdateStrategy::ManualDuration(
507+
std::time::Duration::from_secs_f32(1f32 / 60f32),
508+
));
509+
app.insert_resource(Time::<Fixed>::from_hz(20.0));
510+
511+
app.add_systems(Startup, setup_physics);
512+
app.add_systems(Update, remove_rapier_entity);
513+
app.add_systems(FixedUpdate, || println!("Fixed Update"));
514+
app.add_systems(Update, || println!("Update"));
515+
// startup
516+
app.update();
517+
// normal updates starting
518+
// render only
519+
app.update();
520+
app.update();
521+
// render + physics
522+
app.update();
523+
524+
let context = app
525+
.world_mut()
526+
.query::<&RapierContext>()
527+
.get_single(&app.world())
528+
.unwrap();
529+
assert_eq!(context.entity2body.len(), 1);
530+
531+
// render only + remove entities
532+
app.update();
533+
// Fixed Update hasn´t run yet, so it's a risk of not having caught the removed event, which will be cleaned next frame.
534+
app.update();
535+
// render + physics
536+
app.update();
537+
// render only
538+
app.update();
539+
app.update();
540+
// render + physics
541+
app.update();
542+
543+
let context = app
544+
.world_mut()
545+
.query::<&RapierContext>()
546+
.get_single(&app.world())
547+
.unwrap();
548+
549+
println!("{:?}", &context.entity2body);
550+
assert_eq!(context.entity2body.len(), 0);
551+
}
552+
553+
fn main() {
554+
let mut app = App::new();
555+
app.add_plugins((
556+
HeadlessRenderPlugin,
557+
TransformPlugin,
558+
TimePlugin,
559+
RapierPhysicsPlugin::<NoUserData>::default().in_fixed_schedule(),
560+
));
561+
run_test(&mut app);
562+
}
563+
564+
pub fn setup_physics(mut commands: Commands) {
565+
commands.spawn((
566+
TransformBundle::from(Transform::from_xyz(0.0, 13.0, 0.0)),
567+
RigidBody::Dynamic,
568+
cuboid(0.5, 0.5, 0.5),
569+
TestMarker,
570+
));
571+
println!("spawned rapier entity");
572+
}
573+
pub fn remove_rapier_entity(
574+
mut commands: Commands,
575+
to_remove: Query<Entity, With<TestMarker>>,
576+
mut counter: Local<i32>,
577+
) {
578+
*counter += 1;
579+
if *counter != 5 {
580+
return;
581+
}
582+
println!("removing rapier entity");
583+
for e in &to_remove {
584+
commands.entity(e).despawn();
585+
}
586+
}
587+
}
494588
}

0 commit comments

Comments
 (0)