From 61b98ec80fd2c1db6b0a30010f93289ddd439522 Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Sun, 8 Dec 2024 13:55:09 -0800 Subject: [PATCH] Rename `trigger.entity()` to `trigger.target()` (#16716) # Objective - A `Trigger` has multiple associated `Entity`s - the entity observing the event, and the entity that was targeted by the event. - The field `entity: Entity` encodes no semantic information about what the entity is used for, you can already tell that it's an `Entity` by the type signature! ## Solution - Rename `trigger.entity()` to `trigger.target()` --- ## Changelog - `Trigger`s are associated with multiple entities. `Trigger::entity()` has been renamed to `Trigger::target()` to reflect the semantics of the entity being returned. ## Migration Guide - Rename `Trigger::entity()` to `Trigger::target()`. - Rename `ObserverTrigger::entity` to `ObserverTrigger::target` --- crates/bevy_ecs/README.md | 4 +- crates/bevy_ecs/src/observer/mod.rs | 45 +++++++++++++-------- crates/bevy_ecs/src/observer/runner.rs | 6 +-- crates/bevy_ecs/src/world/deferred_world.rs | 12 +++--- crates/bevy_ecs/src/world/entity_ref.rs | 4 +- 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 | 2 +- crates/bevy_winit/src/cursor.rs | 2 +- examples/animation/animated_fox.rs | 2 +- examples/ecs/observer_propagation.rs | 8 ++-- examples/ecs/observers.rs | 10 ++--- examples/ecs/removal_detection.rs | 2 +- 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/scroll.rs | 2 +- 19 files changed, 73 insertions(+), 60 deletions(-) diff --git a/crates/bevy_ecs/README.md b/crates/bevy_ecs/README.md index a8a396ef96..b947644bcd 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.entity()); - commands.entity(trigger.entity()).despawn(); + println!("Entity {:?} goes BOOM!", trigger.target()); + commands.entity(trigger.target()).despawn(); }); world.flush(); diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 7ae876307c..0cbb1b80b2 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -67,9 +67,22 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { Ptr::from(&self.event) } - /// Returns the [`Entity`] that triggered the observer, could be [`Entity::PLACEHOLDER`]. - pub fn entity(&self) -> Entity { - self.trigger.entity + /// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. It may + /// be [`Entity::PLACEHOLDER`]. + /// + /// 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 + /// the same, because the observer that was triggered is attached to the entity that was + /// targeted by the event. + /// + /// However, it is also possible for those events to bubble up the entity hierarchy and trigger + /// observers on *different* entities, or trigger a global observer. In these cases, the + /// observing entity is *different* from the entity being targeted by the event. + /// + /// 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 { + self.trigger.target } /// Returns the components that triggered the observer, out of the @@ -207,7 +220,7 @@ pub struct ObserverTrigger { /// The [`ComponentId`]s the trigger targeted. components: SmallVec<[ComponentId; 2]>, /// The entity the trigger targeted. - pub entity: Entity, + pub target: Entity, } impl ObserverTrigger { @@ -277,7 +290,7 @@ impl Observers { pub(crate) fn invoke( mut world: DeferredWorld, event_type: ComponentId, - entity: Entity, + target: Entity, components: impl Iterator + Clone, data: &mut T, propagate: &mut bool, @@ -304,7 +317,7 @@ impl Observers { observer, event_type, components: components.clone().collect(), - entity, + target, }, data.into(), propagate, @@ -314,8 +327,8 @@ impl Observers { observers.map.iter().for_each(&mut trigger_observer); // Trigger entity observers listening for this kind of trigger - if entity != Entity::PLACEHOLDER { - if let Some(map) = observers.entity_observers.get(&entity) { + if target != Entity::PLACEHOLDER { + if let Some(map) = observers.entity_observers.get(&target) { map.iter().for_each(&mut trigger_observer); } } @@ -328,8 +341,8 @@ impl Observers { .iter() .for_each(&mut trigger_observer); - if entity != Entity::PLACEHOLDER { - if let Some(map) = component_observers.entity_map.get(&entity) { + if target != Entity::PLACEHOLDER { + if let Some(map) = component_observers.entity_map.get(&target) { map.iter().for_each(&mut trigger_observer); } } @@ -733,20 +746,20 @@ mod tests { world.add_observer( |obs: Trigger, mut res: ResMut, mut commands: Commands| { res.observed("add_a"); - commands.entity(obs.entity()).insert(B); + commands.entity(obs.target()).insert(B); }, ); world.add_observer( |obs: Trigger, mut res: ResMut, mut commands: Commands| { res.observed("remove_a"); - commands.entity(obs.entity()).remove::(); + commands.entity(obs.target()).remove::(); }, ); world.add_observer( |obs: Trigger, mut res: ResMut, mut commands: Commands| { res.observed("add_b"); - commands.entity(obs.entity()).remove::(); + commands.entity(obs.target()).remove::(); }, ); world.add_observer(|_: Trigger, mut res: ResMut| { @@ -915,7 +928,7 @@ mod tests { .spawn_empty() .observe(|_: Trigger| panic!("Trigger routed to non-targeted entity.")); world.add_observer(move |obs: Trigger, mut res: ResMut| { - assert_eq!(obs.entity(), Entity::PLACEHOLDER); + assert_eq!(obs.target(), Entity::PLACEHOLDER); res.observed("event_a"); }); @@ -940,7 +953,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.entity(), entity); + assert_eq!(obs.target(), entity); res.observed("a_2"); }); @@ -1247,7 +1260,7 @@ mod tests { world.add_observer( |trigger: Trigger, query: Query<&A>, mut res: ResMut| { - if query.get(trigger.entity()).is_ok() { + if query.get(trigger.target()).is_ok() { res.observed("event"); } }, diff --git a/crates/bevy_ecs/src/observer/runner.rs b/crates/bevy_ecs/src/observer/runner.rs index 64aebd33cd..f7ee95972d 100644 --- a/crates/bevy_ecs/src/observer/runner.rs +++ b/crates/bevy_ecs/src/observer/runner.rs @@ -197,8 +197,8 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate: /// struct Explode; /// /// world.add_observer(|trigger: Trigger, mut commands: Commands| { -/// println!("Entity {:?} goes BOOM!", trigger.entity()); -/// commands.entity(trigger.entity()).despawn(); +/// println!("Entity {:?} goes BOOM!", trigger.target()); +/// commands.entity(trigger.target()).despawn(); /// }); /// /// world.flush(); @@ -231,7 +231,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.entity()).despawn(); +/// commands.entity(trigger.target()).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 54bacb13fd..3002a11f58 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -508,13 +508,13 @@ impl<'w> DeferredWorld<'w> { pub(crate) unsafe fn trigger_observers( &mut self, event: ComponentId, - entity: Entity, + target: Entity, components: impl Iterator + Clone, ) { Observers::invoke::<_>( self.reborrow(), event, - entity, + target, components, &mut (), &mut false, @@ -529,7 +529,7 @@ impl<'w> DeferredWorld<'w> { pub(crate) unsafe fn trigger_observers_with_data( &mut self, event: ComponentId, - mut entity: Entity, + mut target: Entity, components: &[ComponentId], data: &mut E, mut propagate: bool, @@ -540,7 +540,7 @@ impl<'w> DeferredWorld<'w> { Observers::invoke::<_>( self.reborrow(), event, - entity, + target, components.iter().copied(), data, &mut propagate, @@ -549,12 +549,12 @@ impl<'w> DeferredWorld<'w> { break; } if let Some(traverse_to) = self - .get_entity(entity) + .get_entity(target) .ok() .and_then(|entity| entity.get_components::()) .and_then(|item| T::traverse(item, data)) { - entity = traverse_to; + target = traverse_to; } else { break; } diff --git a/crates/bevy_ecs/src/world/entity_ref.rs b/crates/bevy_ecs/src/world/entity_ref.rs index 87e28c2fa6..0ac19e312b 100644 --- a/crates/bevy_ecs/src/world/entity_ref.rs +++ b/crates/bevy_ecs/src/world/entity_ref.rs @@ -4703,7 +4703,7 @@ mod tests { let entity = world .spawn_empty() .observe(|trigger: Trigger, mut commands: Commands| { - commands.entity(trigger.entity()).insert(TestComponent(0)); + commands.entity(trigger.target()).insert(TestComponent(0)); }) .id(); @@ -4725,7 +4725,7 @@ mod tests { let mut world = World::new(); world.add_observer( |trigger: Trigger, mut commands: Commands| { - commands.entity(trigger.entity()).despawn(); + commands.entity(trigger.target()).despawn(); }, ); let entity = world.spawn_empty().id(); diff --git a/crates/bevy_pbr/src/render/light.rs b/crates/bevy_pbr/src/render/light.rs index c2a61d16e0..57ed8f2686 100644 --- a/crates/bevy_pbr/src/render/light.rs +++ b/crates/bevy_pbr/src/render/light.rs @@ -496,7 +496,7 @@ pub(crate) fn add_light_view_entities( trigger: Trigger, mut commands: Commands, ) { - if let Some(mut v) = commands.get_entity(trigger.entity()) { + if let Some(mut v) = commands.get_entity(trigger.target()) { v.insert(LightViewEntities::default()); } } @@ -506,7 +506,7 @@ pub(crate) fn extracted_light_removed( trigger: Trigger, mut commands: Commands, ) { - if let Some(mut v) = commands.get_entity(trigger.entity()) { + if let Some(mut v) = commands.get_entity(trigger.target()) { v.remove::(); } } @@ -516,7 +516,7 @@ pub(crate) fn remove_light_view_entities( query: Query<&LightViewEntities>, mut commands: Commands, ) { - if let Ok(entities) = query.get(trigger.entity()) { + if let Ok(entities) = query.get(trigger.target()) { for v in entities.0.values() { for e in v.iter().copied() { if let Some(mut v) = commands.get_entity(e) { diff --git a/crates/bevy_picking/src/lib.rs b/crates/bevy_picking/src/lib.rs index 1933bd7a7a..3ff6f88d5b 100644 --- a/crates/bevy_picking/src/lib.rs +++ b/crates/bevy_picking/src/lib.rs @@ -49,13 +49,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.entity()).unwrap(); +//! let mut transform = transforms.get_mut(trigger.target()).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.entity()); -//! commands.entity(trigger.entity()).despawn(); +//! println!("Entity {:?} goes BOOM!", trigger.target()); +//! commands.entity(trigger.target()).despawn(); //! }) //! .observe(|trigger: Trigger>, mut events: EventWriter| { //! events.send(Greeting); diff --git a/crates/bevy_render/src/sync_world.rs b/crates/bevy_render/src/sync_world.rs index 69a8050ed7..ebff81507a 100644 --- a/crates/bevy_render/src/sync_world.rs +++ b/crates/bevy_render/src/sync_world.rs @@ -93,14 +93,14 @@ impl Plugin for SyncWorldPlugin { app.init_resource::(); app.add_observer( |trigger: Trigger, mut pending: ResMut| { - pending.push(EntityRecord::Added(trigger.entity())); + pending.push(EntityRecord::Added(trigger.target())); }, ); app.add_observer( |trigger: Trigger, mut pending: ResMut, query: Query<&RenderEntity>| { - if let Ok(e) = query.get(trigger.entity()) { + if let Ok(e) = query.get(trigger.target()) { pending.push(EntityRecord::Removed(*e)); }; }, @@ -487,14 +487,14 @@ mod tests { main_world.add_observer( |trigger: Trigger, mut pending: ResMut| { - pending.push(EntityRecord::Added(trigger.entity())); + pending.push(EntityRecord::Added(trigger.target())); }, ); main_world.add_observer( |trigger: Trigger, mut pending: ResMut, query: Query<&RenderEntity>| { - if let Ok(e) = query.get(trigger.entity()) { + if let Ok(e) = query.get(trigger.target()) { pending.push(EntityRecord::Removed(*e)); }; }, diff --git a/crates/bevy_scene/src/scene_spawner.rs b/crates/bevy_scene/src/scene_spawner.rs index 74e4918c22..2f660c9903 100644 --- a/crates/bevy_scene/src/scene_spawner.rs +++ b/crates/bevy_scene/src/scene_spawner.rs @@ -594,7 +594,7 @@ mod tests { "`SceneInstanceReady` contains the wrong `InstanceId`" ); assert_eq!( - trigger.entity(), + trigger.target(), scene_entity, "`SceneInstanceReady` triggered on the wrong parent entity" ); diff --git a/crates/bevy_winit/src/cursor.rs b/crates/bevy_winit/src/cursor.rs index 9cc2639091..6a121dae22 100644 --- a/crates/bevy_winit/src/cursor.rs +++ b/crates/bevy_winit/src/cursor.rs @@ -185,7 +185,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.entity()) + .entity(trigger.target()) .try_insert(PendingCursor(Some(CursorSource::System( convert_system_cursor_icon(SystemCursorIcon::Default), )))); diff --git a/examples/animation/animated_fox.rs b/examples/animation/animated_fox.rs index fe9528ec35..0de7389569 100644 --- a/examples/animation/animated_fox.rs +++ b/examples/animation/animated_fox.rs @@ -48,7 +48,7 @@ fn observe_on_step( transforms: Query<&GlobalTransform>, mut seeded_rng: ResMut, ) { - let translation = transforms.get(trigger.entity()).unwrap().translation(); + let translation = transforms.get(trigger.target()).unwrap().translation(); // Spawn a bunch of particles. for _ in 0..14 { let horizontal = seeded_rng.0.gen::() * seeded_rng.0.gen_range(8.0..12.0); diff --git a/examples/ecs/observer_propagation.rs b/examples/ecs/observer_propagation.rs index ce5911ebb2..15f1ca5483 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.entity()) { + if let Ok(name) = name.get(trigger.target()) { 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.entity()).unwrap(); + let (armor, name) = armor.get(trigger.target()).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.entity()).unwrap(); + let (mut hp, name) = hp.get_mut(trigger.target()).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.entity()).despawn_recursive(); + commands.entity(trigger.target()).despawn_recursive(); app_exit.send(AppExit::Success); } diff --git a/examples/ecs/observers.rs b/examples/ecs/observers.rs index 6c9b7d478c..fdc91a7a56 100644 --- a/examples/ecs/observers.rs +++ b/examples/ecs/observers.rs @@ -117,12 +117,12 @@ fn on_add_mine( query: Query<&Mine>, mut index: ResMut, ) { - let mine = query.get(trigger.entity()).unwrap(); + let mine = query.get(trigger.target()).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.entity()); + index.map.entry(tile).or_default().insert(trigger.target()); } // Remove despawned mines from our index @@ -131,19 +131,19 @@ fn on_remove_mine( query: Query<&Mine>, mut index: ResMut, ) { - let mine = query.get(trigger.entity()).unwrap(); + let mine = query.get(trigger.target()).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.entity()); + set.remove(&trigger.target()); }); } 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 `.entity()` - let id = trigger.entity(); + let id = trigger.target(); let Some(mut entity) = commands.get_entity(id) else { return; }; diff --git a/examples/ecs/removal_detection.rs b/examples/ecs/removal_detection.rs index 62d0a12e18..782576aae0 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.entity(); + let entity = trigger.target(); if let Ok(mut sprite) = query.get_mut(entity) { sprite.color = Color::srgb(0.5, 1., 1.); } diff --git a/examples/picking/mesh_picking.rs b/examples/picking/mesh_picking.rs index 40ba4806b3..a273e5f7fa 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.entity()) { + if let Ok(mut material) = query.get_mut(trigger.target()) { material.0 = new_material.clone(); } } @@ -191,7 +191,7 @@ fn rotate(mut query: Query<&mut Transform, With>, time: Res