Skip to content

Commit f77c264

Browse files
committed
fix sync_removals with low physics fixed rate
1 parent 6d7b84b commit f77c264

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
@@ -238,6 +238,10 @@ where
238238
);
239239
app.add_systems(PreStartup, insert_default_world);
240240

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+
241245
// Add each set as necessary
242246
if self.default_system_setup {
243247
app.configure_sets(
@@ -489,4 +493,94 @@ mod test {
489493
));
490494
}
491495
}
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+
}
492586
}

0 commit comments

Comments
 (0)