From 064e5e48b495bf408c09edc1cac248383f6daac2 Mon Sep 17 00:00:00 2001 From: Eagster <79881080+ElliottjPierce@users.noreply.github.com> Date: Mon, 9 Jun 2025 15:37:56 -0400 Subject: [PATCH] Remove entity placeholder from observers (#19440) # Objective `Entity::PLACEHOLDER` acts as a magic number that will *probably* never really exist, but it certainly could. And, `Entity` has a niche, so the only reason to use `PLACEHOLDER` is as an alternative to `MaybeUninit` that trades safety risks for logic risks. As a result, bevy has generally advised against using `PLACEHOLDER`, but we still use if for a lot internally. This pr starts removing internal uses of it, starting from observers. ## Solution Change all trigger target related types from `Entity` to `Option` Small migration guide to come. ## Testing CI ## Future Work This turned a lot of code from ```rust trigger.target() ``` to ```rust trigger.target().unwrap() ``` The extra panic is no worse than before; it's just earlier than panicking after passing the placeholder to something else. But this is kinda annoying. I would like to add a `TriggerMode` or something to `Event` that would restrict what kinds of targets can be used for that event. Many events like `Removed` etc, are always triggered with a target. We can make those have a way to assume Some, etc. But I wanted to save that for a future pr. --- crates/bevy_ecs/README.md | 4 +- crates/bevy_ecs/src/bundle.rs | 16 ++++---- crates/bevy_ecs/src/observer/mod.rs | 34 +++++++-------- crates/bevy_ecs/src/observer/runner.rs | 6 +-- crates/bevy_ecs/src/world/deferred_world.rs | 41 ++++++++++++------- crates/bevy_ecs/src/world/entity_ref.rs | 12 +++--- crates/bevy_input_focus/src/lib.rs | 2 +- crates/bevy_pbr/src/render/light.rs | 6 +-- crates/bevy_picking/src/lib.rs | 6 +-- crates/bevy_render/src/sync_world.rs | 8 ++-- crates/bevy_scene/src/scene_spawner.rs | 10 ++--- crates/bevy_winit/src/cursor.rs | 2 +- examples/3d/edit_material_on_gltf.rs | 4 +- examples/animation/animated_mesh.rs | 4 +- examples/animation/animated_mesh_events.rs | 5 ++- examples/ecs/entity_disabling.rs | 2 +- examples/ecs/observer_propagation.rs | 8 ++-- examples/ecs/observers.rs | 14 ++++--- examples/ecs/removal_detection.rs | 2 +- examples/no_std/library/src/lib.rs | 5 ++- examples/picking/debug_picking.rs | 6 +-- examples/picking/mesh_picking.rs | 4 +- examples/picking/simple_picking.rs | 6 +-- examples/picking/sprite_picking.rs | 2 +- examples/testbed/3d.rs | 2 +- examples/ui/directional_navigation.rs | 2 +- examples/ui/scroll.rs | 2 +- examples/ui/tab_navigation.rs | 2 +- examples/ui/viewport_node.rs | 4 +- .../migration-guides/observer_triggers.md | 12 ++++++ 30 files changed, 134 insertions(+), 99 deletions(-) create mode 100644 release-content/migration-guides/observer_triggers.md diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index ba283d39b2..ade3866b5d 100644 --- a/crates/bevy_ecs/README.md +++ b/crates/bevy_ecs/README.md @@ -340,8 +340,8 @@ let mut world = World::new(); let entity = world.spawn_empty().id(); world.add_observer(|trigger: Trigger, mut commands: Commands| { - println!("Entity {} goes BOOM!", trigger.target()); - commands.entity(trigger.target()).despawn(); + println!("Entity {} goes BOOM!", trigger.target().unwrap()); + commands.entity(trigger.target().unwrap()).despawn(); }); world.flush(); diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index f0641ff80c..d7eb707543 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -1143,7 +1143,7 @@ impl<'w> BundleInserter<'w> { if archetype.has_replace_observer() { deferred_world.trigger_observers( ON_REPLACE, - entity, + Some(entity), archetype_after_insert.iter_existing(), caller, ); @@ -1328,7 +1328,7 @@ impl<'w> BundleInserter<'w> { if new_archetype.has_add_observer() { deferred_world.trigger_observers( ON_ADD, - entity, + Some(entity), archetype_after_insert.iter_added(), caller, ); @@ -1346,7 +1346,7 @@ impl<'w> BundleInserter<'w> { if new_archetype.has_insert_observer() { deferred_world.trigger_observers( ON_INSERT, - entity, + Some(entity), archetype_after_insert.iter_inserted(), caller, ); @@ -1365,7 +1365,7 @@ impl<'w> BundleInserter<'w> { if new_archetype.has_insert_observer() { deferred_world.trigger_observers( ON_INSERT, - entity, + Some(entity), archetype_after_insert.iter_added(), caller, ); @@ -1519,7 +1519,7 @@ impl<'w> BundleRemover<'w> { if self.old_archetype.as_ref().has_replace_observer() { deferred_world.trigger_observers( ON_REPLACE, - entity, + Some(entity), bundle_components_in_archetype(), caller, ); @@ -1534,7 +1534,7 @@ impl<'w> BundleRemover<'w> { if self.old_archetype.as_ref().has_remove_observer() { deferred_world.trigger_observers( ON_REMOVE, - entity, + Some(entity), bundle_components_in_archetype(), caller, ); @@ -1785,7 +1785,7 @@ impl<'w> BundleSpawner<'w> { if archetype.has_add_observer() { deferred_world.trigger_observers( ON_ADD, - entity, + Some(entity), bundle_info.iter_contributed_components(), caller, ); @@ -1800,7 +1800,7 @@ impl<'w> BundleSpawner<'w> { if archetype.has_insert_observer() { deferred_world.trigger_observers( ON_INSERT, - entity, + Some(entity), bundle_info.iter_contributed_components(), caller, ); diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 767dc7ec95..ed5a8b176f 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -68,7 +68,7 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { } /// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. It may - /// be [`Entity::PLACEHOLDER`]. + /// be [`None`] if the trigger is not for a particular entity. /// /// Observable events can target specific entities. When those events fire, they will trigger /// any observers on the targeted entities. In this case, the `target()` and `observer()` are @@ -81,7 +81,7 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { /// /// This is an important distinction: the entity reacting to an event is not always the same as /// the entity triggered by the event. - pub fn target(&self) -> Entity { + pub fn target(&self) -> Option { self.trigger.target } @@ -341,7 +341,7 @@ pub struct ObserverTrigger { /// The [`ComponentId`]s the trigger targeted. components: SmallVec<[ComponentId; 2]>, /// The entity the trigger targeted. - pub target: Entity, + pub target: Option, /// The location of the source code that triggered the observer. pub caller: MaybeLocation, } @@ -416,7 +416,7 @@ impl Observers { pub(crate) fn invoke( mut world: DeferredWorld, event_type: ComponentId, - target: Entity, + target: Option, components: impl Iterator + Clone, data: &mut T, propagate: &mut bool, @@ -455,8 +455,8 @@ impl Observers { observers.map.iter().for_each(&mut trigger_observer); // Trigger entity observers listening for this kind of trigger - if target != Entity::PLACEHOLDER { - if let Some(map) = observers.entity_observers.get(&target) { + if let Some(target_entity) = target { + if let Some(map) = observers.entity_observers.get(&target_entity) { map.iter().for_each(&mut trigger_observer); } } @@ -469,8 +469,8 @@ impl Observers { .iter() .for_each(&mut trigger_observer); - if target != Entity::PLACEHOLDER { - if let Some(map) = component_observers.entity_map.get(&target) { + if let Some(target_entity) = target { + if let Some(map) = component_observers.entity_map.get(&target_entity) { map.iter().for_each(&mut trigger_observer); } } @@ -695,7 +695,7 @@ impl World { unsafe { world.trigger_observers_with_data::<_, E::Traversal>( event_id, - Entity::PLACEHOLDER, + None, targets.components(), event_data, false, @@ -708,7 +708,7 @@ impl World { unsafe { world.trigger_observers_with_data::<_, E::Traversal>( event_id, - target_entity, + Some(target_entity), targets.components(), event_data, E::AUTO_PROPAGATE, @@ -999,20 +999,20 @@ mod tests { world.add_observer( |obs: Trigger, mut res: ResMut, mut commands: Commands| { res.observed("add_a"); - commands.entity(obs.target()).insert(B); + commands.entity(obs.target().unwrap()).insert(B); }, ); world.add_observer( |obs: Trigger, mut res: ResMut, mut commands: Commands| { res.observed("remove_a"); - commands.entity(obs.target()).remove::(); + commands.entity(obs.target().unwrap()).remove::(); }, ); world.add_observer( |obs: Trigger, mut res: ResMut, mut commands: Commands| { res.observed("add_b"); - commands.entity(obs.target()).remove::(); + commands.entity(obs.target().unwrap()).remove::(); }, ); world.add_observer(|_: Trigger, mut res: ResMut| { @@ -1181,7 +1181,7 @@ mod tests { }; world.spawn_empty().observe(system); world.add_observer(move |obs: Trigger, mut res: ResMut| { - assert_eq!(obs.target(), Entity::PLACEHOLDER); + assert_eq!(obs.target(), None); res.observed("event_a"); }); @@ -1208,7 +1208,7 @@ mod tests { .observe(|_: Trigger, mut res: ResMut| res.observed("a_1")) .id(); world.add_observer(move |obs: Trigger, mut res: ResMut| { - assert_eq!(obs.target(), entity); + assert_eq!(obs.target().unwrap(), entity); res.observed("a_2"); }); @@ -1628,7 +1628,7 @@ mod tests { world.add_observer( |trigger: Trigger, query: Query<&A>, mut res: ResMut| { - if query.get(trigger.target()).is_ok() { + if query.get(trigger.target().unwrap()).is_ok() { res.observed("event"); } }, @@ -1651,7 +1651,7 @@ mod tests { fn observer_modifies_relationship() { fn on_add(trigger: Trigger, mut commands: Commands) { commands - .entity(trigger.target()) + .entity(trigger.target().unwrap()) .with_related_entities::(|rsc| { rsc.spawn_empty(); }); diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 43ece18ff5..c3acff2e6e 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -123,8 +123,8 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// struct Explode; /// /// world.add_observer(|trigger: Trigger, mut commands: Commands| { -/// println!("Entity {} goes BOOM!", trigger.target()); -/// commands.entity(trigger.target()).despawn(); +/// println!("Entity {} goes BOOM!", trigger.target().unwrap()); +/// commands.entity(trigger.target().unwrap()).despawn(); /// }); /// /// world.flush(); @@ -157,7 +157,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// # struct Explode; /// world.entity_mut(e1).observe(|trigger: Trigger, mut commands: Commands| { /// println!("Boom!"); -/// commands.entity(trigger.target()).despawn(); +/// commands.entity(trigger.target().unwrap()).despawn(); /// }); /// /// world.entity_mut(e2).observe(|trigger: Trigger, mut commands: Commands| { diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 02c12fe6a3..5a20046b2f 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -23,7 +23,7 @@ use super::{unsafe_world_cell::UnsafeWorldCell, Mut, World, ON_INSERT, ON_REPLAC /// /// This means that in order to add entities, for example, you will need to use commands instead of the world directly. pub struct DeferredWorld<'w> { - // SAFETY: Implementors must not use this reference to make structural changes + // SAFETY: Implementers must not use this reference to make structural changes world: UnsafeWorldCell<'w>, } @@ -157,7 +157,7 @@ impl<'w> DeferredWorld<'w> { if archetype.has_replace_observer() { self.trigger_observers( ON_REPLACE, - entity, + Some(entity), [component_id].into_iter(), MaybeLocation::caller(), ); @@ -197,7 +197,7 @@ impl<'w> DeferredWorld<'w> { if archetype.has_insert_observer() { self.trigger_observers( ON_INSERT, - entity, + Some(entity), [component_id].into_iter(), MaybeLocation::caller(), ); @@ -738,7 +738,7 @@ impl<'w> DeferredWorld<'w> { pub(crate) unsafe fn trigger_observers( &mut self, event: ComponentId, - target: Entity, + target: Option, components: impl Iterator + Clone, caller: MaybeLocation, ) { @@ -761,7 +761,7 @@ impl<'w> DeferredWorld<'w> { pub(crate) unsafe fn trigger_observers_with_data( &mut self, event: ComponentId, - mut target: Entity, + target: Option, components: impl Iterator + Clone, data: &mut E, mut propagate: bool, @@ -769,18 +769,20 @@ impl<'w> DeferredWorld<'w> { ) where T: Traversal, { + Observers::invoke::<_>( + self.reborrow(), + event, + target, + components.clone(), + data, + &mut propagate, + caller, + ); + let Some(mut target) = target else { return }; + loop { - Observers::invoke::<_>( - self.reborrow(), - event, - target, - components.clone(), - data, - &mut propagate, - caller, - ); if !propagate { - break; + return; } if let Some(traverse_to) = self .get_entity(target) @@ -792,6 +794,15 @@ impl<'w> DeferredWorld<'w> { } else { break; } + Observers::invoke::<_>( + self.reborrow(), + event, + Some(target), + components.clone(), + data, + &mut propagate, + caller, + ); } } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 1f67865df5..dd653831d9 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -2371,7 +2371,7 @@ impl<'w> EntityWorldMut<'w> { if archetype.has_despawn_observer() { deferred_world.trigger_observers( ON_DESPAWN, - self.entity, + Some(self.entity), archetype.components(), caller, ); @@ -2385,7 +2385,7 @@ impl<'w> EntityWorldMut<'w> { if archetype.has_replace_observer() { deferred_world.trigger_observers( ON_REPLACE, - self.entity, + Some(self.entity), archetype.components(), caller, ); @@ -2400,7 +2400,7 @@ impl<'w> EntityWorldMut<'w> { if archetype.has_remove_observer() { deferred_world.trigger_observers( ON_REMOVE, - self.entity, + Some(self.entity), archetype.components(), caller, ); @@ -5749,7 +5749,9 @@ mod tests { let entity = world .spawn_empty() .observe(|trigger: Trigger, mut commands: Commands| { - commands.entity(trigger.target()).insert(TestComponent(0)); + commands + .entity(trigger.target().unwrap()) + .insert(TestComponent(0)); }) .id(); @@ -5769,7 +5771,7 @@ mod tests { let mut world = World::new(); world.add_observer( |trigger: Trigger, mut commands: Commands| { - commands.entity(trigger.target()).despawn(); + commands.entity(trigger.target().unwrap()).despawn(); }, ); let entity = world.spawn_empty().id(); diff --git a/crates/bevy_input_focus/src/lib.rs b/crates/bevy_input_focus/src/lib.rs index da3a7bc8e9..e8879d7a9c 100644 --- a/crates/bevy_input_focus/src/lib.rs +++ b/crates/bevy_input_focus/src/lib.rs @@ -394,7 +394,7 @@ mod tests { trigger: Trigger>, mut query: Query<&mut GatherKeyboardEvents>, ) { - if let Ok(mut gather) = query.get_mut(trigger.target()) { + if let Ok(mut gather) = query.get_mut(trigger.target().unwrap()) { if let Key::Character(c) = &trigger.input.logical_key { gather.0.push_str(c.as_str()); } diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index e779dbc841..2592936ddd 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -551,7 +551,7 @@ pub(crate) fn add_light_view_entities( trigger: Trigger, mut commands: Commands, ) { - if let Ok(mut v) = commands.get_entity(trigger.target()) { + if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) { v.insert(LightViewEntities::default()); } } @@ -561,7 +561,7 @@ pub(crate) fn extracted_light_removed( trigger: Trigger, mut commands: Commands, ) { - if let Ok(mut v) = commands.get_entity(trigger.target()) { + if let Ok(mut v) = commands.get_entity(trigger.target().unwrap()) { v.try_remove::(); } } @@ -571,7 +571,7 @@ pub(crate) fn remove_light_view_entities( query: Query<&LightViewEntities>, mut commands: Commands, ) { - if let Ok(entities) = query.get(trigger.target()) { + if let Ok(entities) = query.get(trigger.target().unwrap()) { for v in entities.0.values() { for e in v.iter().copied() { if let Ok(mut v) = commands.get_entity(e) { diff --git a/crates/bevy_picking/src/lib.rs b/crates/bevy_picking/src/lib.rs index 53387e84c8..70a5714581 100644 --- a/crates/bevy_picking/src/lib.rs +++ b/crates/bevy_picking/src/lib.rs @@ -55,13 +55,13 @@ //! // Spawn your entity here, e.g. a Mesh. //! // When dragged, mutate the `Transform` component on the dragged target entity: //! .observe(|trigger: Trigger>, mut transforms: Query<&mut Transform>| { -//! let mut transform = transforms.get_mut(trigger.target()).unwrap(); +//! let mut transform = transforms.get_mut(trigger.target().unwrap()).unwrap(); //! let drag = trigger.event(); //! transform.rotate_local_y(drag.delta.x / 50.0); //! }) //! .observe(|trigger: Trigger>, mut commands: Commands| { -//! println!("Entity {} goes BOOM!", trigger.target()); -//! commands.entity(trigger.target()).despawn(); +//! println!("Entity {} goes BOOM!", trigger.target().unwrap()); +//! commands.entity(trigger.target().unwrap()).despawn(); //! }) //! .observe(|trigger: Trigger>, mut events: EventWriter| { //! events.write(Greeting); diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index f6c2f87593..90613451dd 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -94,14 +94,14 @@ impl Plugin for SyncWorldPlugin { app.init_resource::(); app.add_observer( |trigger: Trigger, mut pending: ResMut| { - pending.push(EntityRecord::Added(trigger.target())); + pending.push(EntityRecord::Added(trigger.target().unwrap())); }, ); app.add_observer( |trigger: Trigger, mut pending: ResMut, query: Query<&RenderEntity>| { - if let Ok(e) = query.get(trigger.target()) { + if let Ok(e) = query.get(trigger.target().unwrap()) { pending.push(EntityRecord::Removed(*e)); }; }, @@ -512,14 +512,14 @@ mod tests { main_world.add_observer( |trigger: Trigger, mut pending: ResMut| { - pending.push(EntityRecord::Added(trigger.target())); + pending.push(EntityRecord::Added(trigger.target().unwrap())); }, ); main_world.add_observer( |trigger: Trigger, mut pending: ResMut, query: Query<&RenderEntity>| { - if let Ok(e) = query.get(trigger.target()) { + if let Ok(e) = query.get(trigger.target().unwrap()) { pending.push(EntityRecord::Removed(*e)); }; }, diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 1daa0158b3..456cb62225 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -721,7 +721,7 @@ mod tests { .expect("Failed to run dynamic scene builder system.") } - fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Entity) { + fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Option) { // Add observer app.world_mut().add_observer( move |trigger: Trigger, @@ -773,7 +773,7 @@ mod tests { .unwrap(); // Check trigger. - observe_trigger(&mut app, scene_id, Entity::PLACEHOLDER); + observe_trigger(&mut app, scene_id, None); } #[test] @@ -792,7 +792,7 @@ mod tests { .unwrap(); // Check trigger. - observe_trigger(&mut app, scene_id, Entity::PLACEHOLDER); + observe_trigger(&mut app, scene_id, None); } #[test] @@ -816,7 +816,7 @@ mod tests { .unwrap(); // Check trigger. - observe_trigger(&mut app, scene_id, scene_entity); + observe_trigger(&mut app, scene_id, Some(scene_entity)); } #[test] @@ -840,7 +840,7 @@ mod tests { .unwrap(); // Check trigger. - observe_trigger(&mut app, scene_id, scene_entity); + observe_trigger(&mut app, scene_id, Some(scene_entity)); } #[test] diff --git a/crates/bevy_winit/src/cursor.rs b/crates/bevy_winit/src/cursor.rs index bdca3f8585..63c8a2bb2d 100644 --- a/crates/bevy_winit/src/cursor.rs +++ b/crates/bevy_winit/src/cursor.rs @@ -194,7 +194,7 @@ fn update_cursors( fn on_remove_cursor_icon(trigger: Trigger, mut commands: Commands) { // Use `try_insert` to avoid panic if the window is being destroyed. commands - .entity(trigger.target()) + .entity(trigger.target().unwrap()) .try_insert(PendingCursor(Some(CursorSource::System( convert_system_cursor_icon(SystemCursorIcon::Default), )))); diff --git a/examples/3d/edit_material_on_gltf.rs b/examples/3d/edit_material_on_gltf.rs index f9de5842a9..029ec6bf1e 100644 --- a/examples/3d/edit_material_on_gltf.rs +++ b/examples/3d/edit_material_on_gltf.rs @@ -65,12 +65,12 @@ fn change_material( mut asset_materials: ResMut>, ) { // Get the `ColorOverride` of the entity, if it does not have a color override, skip - let Ok(color_override) = color_override.get(trigger.target()) else { + let Ok(color_override) = color_override.get(trigger.target().unwrap()) else { return; }; // Iterate over all children recursively - for descendants in children.iter_descendants(trigger.target()) { + for descendants in children.iter_descendants(trigger.target().unwrap()) { // Get the material of the descendant if let Some(material) = mesh_materials .get(descendants) diff --git a/examples/animation/animated_mesh.rs b/examples/animation/animated_mesh.rs index ecea86fb17..d2c4fd8443 100644 --- a/examples/animation/animated_mesh.rs +++ b/examples/animation/animated_mesh.rs @@ -70,12 +70,12 @@ fn play_animation_when_ready( ) { // The entity we spawned in `setup_mesh_and_animation` is the trigger's target. // Start by finding the AnimationToPlay component we added to that entity. - if let Ok(animation_to_play) = animations_to_play.get(trigger.target()) { + if let Ok(animation_to_play) = animations_to_play.get(trigger.target().unwrap()) { // The SceneRoot component will have spawned the scene as a hierarchy // of entities parented to our entity. Since the asset contained a skinned // mesh and animations, it will also have spawned an animation player // component. Search our entity's descendants to find the animation player. - for child in children.iter_descendants(trigger.target()) { + for child in children.iter_descendants(trigger.target().unwrap()) { if let Ok(mut player) = players.get_mut(child) { // Tell the animation player to start the animation and keep // repeating it. diff --git a/examples/animation/animated_mesh_events.rs b/examples/animation/animated_mesh_events.rs index 2048f573fd..c0f261752e 100644 --- a/examples/animation/animated_mesh_events.rs +++ b/examples/animation/animated_mesh_events.rs @@ -47,7 +47,10 @@ fn observe_on_step( transforms: Query<&GlobalTransform>, mut seeded_rng: ResMut, ) { - let translation = transforms.get(trigger.target()).unwrap().translation(); + let translation = transforms + .get(trigger.target().unwrap()) + .unwrap() + .translation(); // Spawn a bunch of particles. for _ in 0..14 { let horizontal = seeded_rng.0.r#gen::() * seeded_rng.0.gen_range(8.0..12.0); diff --git a/examples/ecs/entity_disabling.rs b/examples/ecs/entity_disabling.rs index 86cf8e9d6c..f9cebede22 100644 --- a/examples/ecs/entity_disabling.rs +++ b/examples/ecs/entity_disabling.rs @@ -40,7 +40,7 @@ fn disable_entities_on_click( valid_query: Query<&DisableOnClick>, mut commands: Commands, ) { - let clicked_entity = trigger.target(); + let clicked_entity = trigger.target().unwrap(); // Windows and text are entities and can be clicked! // We definitely don't want to disable the window itself, // because that would cause the app to close! diff --git a/examples/ecs/observer_propagation.rs b/examples/ecs/observer_propagation.rs index 1acf5efa90..6793364a29 100644 --- a/examples/ecs/observer_propagation.rs +++ b/examples/ecs/observer_propagation.rs @@ -78,14 +78,14 @@ fn attack_armor(entities: Query>, mut commands: Commands) { } fn attack_hits(trigger: Trigger, name: Query<&Name>) { - if let Ok(name) = name.get(trigger.target()) { + if let Ok(name) = name.get(trigger.target().unwrap()) { info!("Attack hit {}", name); } } /// A callback placed on [`Armor`], checking if it absorbed all the [`Attack`] damage. fn block_attack(mut trigger: Trigger, armor: Query<(&Armor, &Name)>) { - let (armor, name) = armor.get(trigger.target()).unwrap(); + let (armor, name) = armor.get(trigger.target().unwrap()).unwrap(); let attack = trigger.event_mut(); let damage = attack.damage.saturating_sub(**armor); if damage > 0 { @@ -110,14 +110,14 @@ fn take_damage( mut app_exit: EventWriter, ) { let attack = trigger.event(); - let (mut hp, name) = hp.get_mut(trigger.target()).unwrap(); + let (mut hp, name) = hp.get_mut(trigger.target().unwrap()).unwrap(); **hp = hp.saturating_sub(attack.damage); if **hp > 0 { info!("{} has {:.1} HP", name, hp.0); } else { warn!("💀 {} has died a gruesome death", name); - commands.entity(trigger.target()).despawn(); + commands.entity(trigger.target().unwrap()).despawn(); app_exit.write(AppExit::Success); } diff --git a/examples/ecs/observers.rs b/examples/ecs/observers.rs index 41a2e5dd07..d7bf067d4c 100644 --- a/examples/ecs/observers.rs +++ b/examples/ecs/observers.rs @@ -117,12 +117,16 @@ fn on_add_mine( query: Query<&Mine>, mut index: ResMut, ) { - let mine = query.get(trigger.target()).unwrap(); + let mine = query.get(trigger.target().unwrap()).unwrap(); let tile = ( (mine.pos.x / CELL_SIZE).floor() as i32, (mine.pos.y / CELL_SIZE).floor() as i32, ); - index.map.entry(tile).or_default().insert(trigger.target()); + index + .map + .entry(tile) + .or_default() + .insert(trigger.target().unwrap()); } // Remove despawned mines from our index @@ -131,19 +135,19 @@ fn on_remove_mine( query: Query<&Mine>, mut index: ResMut, ) { - let mine = query.get(trigger.target()).unwrap(); + let mine = query.get(trigger.target().unwrap()).unwrap(); let tile = ( (mine.pos.x / CELL_SIZE).floor() as i32, (mine.pos.y / CELL_SIZE).floor() as i32, ); index.map.entry(tile).and_modify(|set| { - set.remove(&trigger.target()); + set.remove(&trigger.target().unwrap()); }); } fn explode_mine(trigger: Trigger, query: Query<&Mine>, mut commands: Commands) { // If a triggered event is targeting a specific entity you can access it with `.target()` - let id = trigger.target(); + let id = trigger.target().unwrap(); let Ok(mut entity) = commands.get_entity(id) else { return; }; diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index 782576aae0..f60452f4ea 100644 --- a/examples/ecs/removal_detection.rs +++ b/examples/ecs/removal_detection.rs @@ -50,7 +50,7 @@ fn remove_component( fn react_on_removal(trigger: Trigger, mut query: Query<&mut Sprite>) { // The `OnRemove` trigger was automatically called on the `Entity` that had its `MyComponent` removed. - let entity = trigger.target(); + let entity = trigger.target().unwrap(); if let Ok(mut sprite) = query.get_mut(entity) { sprite.color = Color::srgb(0.5, 1., 1.); } diff --git a/examples/no_std/library/src/lib.rs b/examples/no_std/library/src/lib.rs index c8aa35d799..ec767b763d 100644 --- a/examples/no_std/library/src/lib.rs +++ b/examples/no_std/library/src/lib.rs @@ -127,7 +127,10 @@ fn tick_timers( } fn unwrap(trigger: Trigger, world: &mut World) { - if let Ok(mut target) = world.get_entity_mut(trigger.target()) { + if let Some(mut target) = trigger + .target() + .and_then(|target| world.get_entity_mut(target).ok()) + { if let Some(DelayedComponent(bundle)) = target.take::>() { target.insert(bundle); } diff --git a/examples/picking/debug_picking.rs b/examples/picking/debug_picking.rs index 4213775ba7..5461d9e4c9 100644 --- a/examples/picking/debug_picking.rs +++ b/examples/picking/debug_picking.rs @@ -48,13 +48,13 @@ fn setup_scene( .observe(on_click_spawn_cube) .observe( |out: Trigger>, mut texts: Query<&mut TextColor>| { - let mut text_color = texts.get_mut(out.target()).unwrap(); + let mut text_color = texts.get_mut(out.target().unwrap()).unwrap(); text_color.0 = Color::WHITE; }, ) .observe( |over: Trigger>, mut texts: Query<&mut TextColor>| { - let mut color = texts.get_mut(over.target()).unwrap(); + let mut color = texts.get_mut(over.target().unwrap()).unwrap(); color.0 = bevy::color::palettes::tailwind::CYAN_400.into(); }, ); @@ -102,7 +102,7 @@ fn on_click_spawn_cube( } fn on_drag_rotate(drag: Trigger>, mut transforms: Query<&mut Transform>) { - if let Ok(mut transform) = transforms.get_mut(drag.target()) { + if let Ok(mut transform) = transforms.get_mut(drag.target().unwrap()) { transform.rotate_y(drag.delta.x * 0.02); transform.rotate_x(drag.delta.y * 0.02); } diff --git a/examples/picking/mesh_picking.rs b/examples/picking/mesh_picking.rs index 3c0d3bf09f..4c247aa62a 100644 --- a/examples/picking/mesh_picking.rs +++ b/examples/picking/mesh_picking.rs @@ -164,7 +164,7 @@ fn update_material_on( // versions of this observer, each triggered by a different event and with a different hardcoded // material. Instead, the event type is a generic, and the material is passed in. move |trigger, mut query| { - if let Ok(mut material) = query.get_mut(trigger.target()) { + if let Ok(mut material) = query.get_mut(trigger.target().unwrap()) { material.0 = new_material.clone(); } } @@ -191,7 +191,7 @@ fn rotate(mut query: Query<&mut Transform, With>, time: Res