diff --git a/crates/bevy_ecs/Cargo.toml b/crates/bevy_ecs/Cargo.toml index bcc075354f..2769500584 100644 --- a/crates/bevy_ecs/Cargo.toml +++ b/crates/bevy_ecs/Cargo.toml @@ -41,7 +41,7 @@ derive_more = { version = "1", default-features = false, features = [ ] } nonmax = "0.5" arrayvec = { version = "0.7.4", optional = true } -smallvec = "1" +smallvec = { version = "1", features = ["union"] } [dev-dependencies] rand = "0.8" diff --git a/crates/bevy_ecs/src/archetype.rs b/crates/bevy_ecs/src/archetype.rs index 3f54563702..2c2241e29e 100644 --- a/crates/bevy_ecs/src/archetype.rs +++ b/crates/bevy_ecs/src/archetype.rs @@ -135,15 +135,15 @@ pub(crate) struct AddBundle { } impl AddBundle { - pub(crate) fn iter_inserted(&self) -> impl Iterator + '_ { + pub(crate) fn iter_inserted(&self) -> impl Iterator + Clone + '_ { self.added.iter().chain(self.existing.iter()).copied() } - pub(crate) fn iter_added(&self) -> impl Iterator + '_ { + pub(crate) fn iter_added(&self) -> impl Iterator + Clone + '_ { self.added.iter().copied() } - pub(crate) fn iter_existing(&self) -> impl Iterator + '_ { + pub(crate) fn iter_existing(&self) -> impl Iterator + Clone + '_ { self.existing.iter().copied() } } @@ -489,7 +489,7 @@ impl Archetype { /// /// All of the IDs are unique. #[inline] - pub fn components(&self) -> impl Iterator + '_ { + pub fn components(&self) -> impl Iterator + Clone + '_ { self.components.indices() } diff --git a/crates/bevy_ecs/src/bundle.rs b/crates/bevy_ecs/src/bundle.rs index 20153099fc..fcefd68b0a 100644 --- a/crates/bevy_ecs/src/bundle.rs +++ b/crates/bevy_ecs/src/bundle.rs @@ -463,7 +463,7 @@ impl BundleInfo { /// Returns an iterator over the [ID](ComponentId) of each component explicitly defined in this bundle (ex: this excludes Required Components). /// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`] #[inline] - pub fn iter_explicit_components(&self) -> impl Iterator + '_ { + pub fn iter_explicit_components(&self) -> impl Iterator + Clone + '_ { self.explicit_components().iter().copied() } @@ -471,7 +471,7 @@ impl BundleInfo { /// /// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`] #[inline] - pub fn iter_contributed_components(&self) -> impl Iterator + '_ { + pub fn iter_contributed_components(&self) -> impl Iterator + Clone + '_ { self.component_ids.iter().copied() } diff --git a/crates/bevy_ecs/src/observer/mod.rs b/crates/bevy_ecs/src/observer/mod.rs index 5e87a9d01c..674f98f650 100644 --- a/crates/bevy_ecs/src/observer/mod.rs +++ b/crates/bevy_ecs/src/observer/mod.rs @@ -23,6 +23,7 @@ use core::{ marker::PhantomData, ops::{Deref, DerefMut}, }; +use smallvec::SmallVec; /// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the /// [`Event`] data itself. If it was triggered for a specific [`Entity`], it includes that as well. It also @@ -70,6 +71,13 @@ impl<'w, E, B: Bundle> Trigger<'w, E, B> { self.trigger.entity } + /// Returns the components that triggered the observer, out of the + /// components defined in `B`. Does not necessarily include all of them as + /// `B` acts like an `OR` filter rather than an `AND` filter. + pub fn components(&self) -> &[ComponentId] { + &self.trigger.components + } + /// Returns the [`Entity`] that observed the triggered event. /// This allows you to despawn the observer, ceasing observation. /// @@ -193,14 +201,21 @@ impl ObserverDescriptor { pub struct ObserverTrigger { /// The [`Entity`] of the observer handling the trigger. pub observer: Entity, - - /// The [`ComponentId`] the trigger targeted. + /// The [`Event`] the trigger targeted. pub event_type: ComponentId, - + /// The [`ComponentId`]s the trigger targeted. + components: SmallVec<[ComponentId; 2]>, /// The entity the trigger targeted. pub entity: Entity, } +impl ObserverTrigger { + /// Returns the components that the trigger targeted. + pub fn components(&self) -> &[ComponentId] { + &self.components + } +} + // Map between an observer entity and its runner type ObserverMap = EntityHashMap; @@ -262,7 +277,7 @@ impl Observers { mut world: DeferredWorld, event_type: ComponentId, entity: Entity, - components: impl Iterator, + components: impl Iterator + Clone, data: &mut T, propagate: &mut bool, ) { @@ -279,12 +294,15 @@ impl Observers { (world.into_deferred(), observers) }; + let trigger_for_components = components.clone(); + let mut trigger_observer = |(&observer, runner): (&Entity, &ObserverRunner)| { (runner)( world.reborrow(), ObserverTrigger { observer, event_type, + components: components.clone().collect(), entity, }, data.into(), @@ -302,7 +320,7 @@ impl Observers { } // Trigger observers listening to this trigger targeting a specific component - components.for_each(|id| { + trigger_for_components.for_each(|id| { if let Some(component_observers) = observers.component_observers.get(&id) { component_observers .map @@ -552,8 +570,10 @@ mod tests { use alloc::vec; use bevy_ptr::OwningPtr; + use bevy_utils::HashMap; use crate as bevy_ecs; + use crate::component::ComponentId; use crate::{ observer::{EmitDynamicTrigger, Observer, ObserverDescriptor, ObserverState, OnReplace}, prelude::*, @@ -1268,9 +1288,6 @@ mod tests { #[test] fn observer_invalid_params() { - #[derive(Event)] - struct EventA; - #[derive(Resource)] struct ResA; @@ -1289,9 +1306,6 @@ mod tests { #[test] fn observer_apply_deferred_from_param_set() { - #[derive(Event)] - struct EventA; - #[derive(Resource)] struct ResA; @@ -1309,4 +1323,35 @@ mod tests { assert!(world.get_resource::().is_some()); } + + #[test] + fn observer_triggered_components() { + #[derive(Resource, Default)] + struct Counter(HashMap); + + let mut world = World::new(); + world.init_resource::(); + let a_id = world.register_component::(); + let b_id = world.register_component::(); + + world.add_observer( + |trigger: Trigger, mut counter: ResMut| { + for &component in trigger.components() { + *counter.0.entry(component).or_default() += 1; + } + }, + ); + world.flush(); + + world.trigger_targets(EventA, [a_id, b_id]); + world.trigger_targets(EventA, a_id); + world.trigger_targets(EventA, b_id); + world.trigger_targets(EventA, [a_id, b_id]); + world.trigger_targets(EventA, a_id); + world.flush(); + + let counter = world.resource::(); + assert_eq!(4, *counter.0.get(&a_id).unwrap()); + assert_eq!(3, *counter.0.get(&b_id).unwrap()); + } } diff --git a/crates/bevy_ecs/src/storage/sparse_set.rs b/crates/bevy_ecs/src/storage/sparse_set.rs index ff740d755b..e3d76547a9 100644 --- a/crates/bevy_ecs/src/storage/sparse_set.rs +++ b/crates/bevy_ecs/src/storage/sparse_set.rs @@ -423,7 +423,7 @@ macro_rules! impl_sparse_set { } /// Returns an iterator visiting all keys (indices) in arbitrary order. - pub fn indices(&self) -> impl Iterator + '_ { + pub fn indices(&self) -> impl Iterator + Clone + '_ { self.indices.iter().cloned() } diff --git a/crates/bevy_ecs/src/world/deferred_world.rs b/crates/bevy_ecs/src/world/deferred_world.rs index 45e7a7cf87..0ffd94e01e 100644 --- a/crates/bevy_ecs/src/world/deferred_world.rs +++ b/crates/bevy_ecs/src/world/deferred_world.rs @@ -501,7 +501,7 @@ impl<'w> DeferredWorld<'w> { &mut self, event: ComponentId, entity: Entity, - components: impl Iterator, + components: impl Iterator + Clone, ) { Observers::invoke::<_>( self.reborrow(),