bevy_hanabi icon indicating copy to clipboard operation
bevy_hanabi copied to clipboard

Spawning particle effect in PostUpdate crashes

Open barsoosayque opened this issue 5 months ago • 0 comments

Crate versions bevy version: 0.14.1 bevy_hanabi version: 0.12.2

Describe the bug Spawning a new entity with ParticleEffect component in PostUpdate schedule results in unwrap:

thread 'Compute Task Pool (1)' panicked at /<...>/.cargo/registry/src/index.crates.io-6f17d22bba15001f/bevy_hanabi-0.12.2/src/render/mod.rs:2107:59:
called `Option::unwrap()` on a `None` value

Expected behavior I would expect any schedule to be valid for spawning particle effects.

To Reproduce Here is a diff for examples/spawn_on_command to reproduce this crash:

Diff


diff --git a/examples/spawn_on_command.rs b/examples/spawn_on_command.rs
index 7b4c0b5..42f324b 100644
--- a/examples/spawn_on_command.rs
+++ b/examples/spawn_on_command.rs
@@ -56,12 +56,15 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
     app.add_plugins(WorldInspectorPlugin::default());

     app.add_systems(Startup, setup)
-        .add_systems(Update, (utils::close_on_esc, update))
+        .add_systems(PostUpdate, (utils::close_on_esc, update))
         .run();

     Ok(())
 }

+#[derive(Resource)]
+struct Storage(Handle<EffectAsset>);
+
 #[derive(Component)]
 struct Ball {
     velocity: Vec2,
@@ -119,7 +122,7 @@ fn setup(
         .insert(Name::new("ball"));

     // Set `spawn_immediately` to false to spawn on command with Spawner::reset()
-    let spawner = Spawner::once(100.0.into(), false);
+    let spawner = Spawner::once(100.0.into(), true);

     let writer = ExprWriter::new();

@@ -186,24 +189,17 @@ fn setup(
             .render(ScreenSpaceSizeModifier),
     );

-    commands
-        .spawn(ParticleEffectBundle::new(effect))
-        .insert(Name::new("effect"));
+    commands.insert_resource(Storage(effect));
 }

 fn update(
+    mut commands: Commands,
+    storage: Res<Storage>,
     mut balls: Query<(&mut Ball, &mut Transform)>,
-    mut effect: Query<(&mut EffectProperties, &mut EffectSpawner, &mut Transform), Without<Ball>>,
     time: Res<Time>,
 ) {
     const HALF_SIZE: f32 = BOX_SIZE / 2.0 - BALL_RADIUS;

-    // Note: On first frame where the effect spawns, EffectSpawner is spawned during
-    // PostUpdate, so will not be available yet. Ignore for a frame if so.
-    let Ok((mut properties, mut spawner, mut effect_transform)) = effect.get_single_mut() else {
-        return;
-    };
-
     for (mut ball, mut transform) in balls.iter_mut() {
         let mut pos = transform.translation.xy() + ball.velocity * time.delta_seconds();
         let mut collision = false;
@@ -231,9 +227,7 @@ fn update(
         transform.translation = pos.extend(transform.translation.z);

         if collision {
-            // This isn't the most accurate place to spawn the particle effect,
-            // but this is just for demonstration, so whatever.
-            effect_transform.translation = transform.translation;
+            let mut properties = EffectProperties::default();

             // Pick a random particle color
             let r = rand::random::<u8>();
@@ -248,7 +242,14 @@ fn update(
             properties.set("normal", normal.extend(0.).into());

             // Spawn the particles
-            spawner.reset();
+            commands
+                .spawn(ParticleEffectBundle {
+                    effect: ParticleEffect::new(storage.0.clone()),
+                    effect_properties: properties,
+                    transform: transform.clone(),
+                    ..default()
+                })
+                .insert(Name::new("effect"));
         }
     }
 }

barsoosayque avatar Sep 10 '24 23:09 barsoosayque