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