@@ -240,6 +240,10 @@ where
240
240
) ;
241
241
app. add_systems ( PreStartup , insert_default_world) ;
242
242
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
+
243
247
// Add each set as necessary
244
248
if self . default_system_setup {
245
249
app. configure_sets (
@@ -491,4 +495,94 @@ mod test {
491
495
) ) ;
492
496
}
493
497
}
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
+ }
494
588
}
0 commit comments