Add Trigger::components, which lists the component targets that were triggered (#15811)

# Objective

- Closes #14774 

## Solution

Added:

```rust
impl<'w, E, B: Bundle> Trigger<'w, E, B> {
    pub fn components(&self) -> &[ComponentId];
}
```

I went with storing it in the trigger as a `SmallVec<[Component; 1]>`
because a singular target component will be the most common case, and it
remains the same size as `Vec<ComponentId>`.

## Testing

Added a test.
This commit is contained in:
Christian Hughes 2024-10-14 19:17:03 -07:00 committed by GitHub
parent 9f5f5d3d41
commit 345f935b1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 65 additions and 20 deletions

View file

@ -41,7 +41,7 @@ derive_more = { version = "1", default-features = false, features = [
] } ] }
nonmax = "0.5" nonmax = "0.5"
arrayvec = { version = "0.7.4", optional = true } arrayvec = { version = "0.7.4", optional = true }
smallvec = "1" smallvec = { version = "1", features = ["union"] }
[dev-dependencies] [dev-dependencies]
rand = "0.8" rand = "0.8"

View file

@ -135,15 +135,15 @@ pub(crate) struct AddBundle {
} }
impl AddBundle { impl AddBundle {
pub(crate) fn iter_inserted(&self) -> impl Iterator<Item = ComponentId> + '_ { pub(crate) fn iter_inserted(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.added.iter().chain(self.existing.iter()).copied() self.added.iter().chain(self.existing.iter()).copied()
} }
pub(crate) fn iter_added(&self) -> impl Iterator<Item = ComponentId> + '_ { pub(crate) fn iter_added(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.added.iter().copied() self.added.iter().copied()
} }
pub(crate) fn iter_existing(&self) -> impl Iterator<Item = ComponentId> + '_ { pub(crate) fn iter_existing(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.existing.iter().copied() self.existing.iter().copied()
} }
} }
@ -489,7 +489,7 @@ impl Archetype {
/// ///
/// All of the IDs are unique. /// All of the IDs are unique.
#[inline] #[inline]
pub fn components(&self) -> impl Iterator<Item = ComponentId> + '_ { pub fn components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.components.indices() self.components.indices()
} }

View file

@ -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). /// 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`] /// To iterate all components contributed by this bundle (including Required Components), see [`BundleInfo::iter_contributed_components`]
#[inline] #[inline]
pub fn iter_explicit_components(&self) -> impl Iterator<Item = ComponentId> + '_ { pub fn iter_explicit_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.explicit_components().iter().copied() 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`] /// To iterate only components explicitly defined in this bundle, see [`BundleInfo::iter_explicit_components`]
#[inline] #[inline]
pub fn iter_contributed_components(&self) -> impl Iterator<Item = ComponentId> + '_ { pub fn iter_contributed_components(&self) -> impl Iterator<Item = ComponentId> + Clone + '_ {
self.component_ids.iter().copied() self.component_ids.iter().copied()
} }

View file

@ -23,6 +23,7 @@ use core::{
marker::PhantomData, marker::PhantomData,
ops::{Deref, DerefMut}, ops::{Deref, DerefMut},
}; };
use smallvec::SmallVec;
/// Type containing triggered [`Event`] information for a given run of an [`Observer`]. This contains the /// 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 /// [`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 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. /// Returns the [`Entity`] that observed the triggered event.
/// This allows you to despawn the observer, ceasing observation. /// This allows you to despawn the observer, ceasing observation.
/// ///
@ -193,14 +201,21 @@ impl ObserverDescriptor {
pub struct ObserverTrigger { pub struct ObserverTrigger {
/// The [`Entity`] of the observer handling the trigger. /// The [`Entity`] of the observer handling the trigger.
pub observer: Entity, pub observer: Entity,
/// The [`Event`] the trigger targeted.
/// The [`ComponentId`] the trigger targeted.
pub event_type: ComponentId, pub event_type: ComponentId,
/// The [`ComponentId`]s the trigger targeted.
components: SmallVec<[ComponentId; 2]>,
/// The entity the trigger targeted. /// The entity the trigger targeted.
pub entity: Entity, 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 // Map between an observer entity and its runner
type ObserverMap = EntityHashMap<ObserverRunner>; type ObserverMap = EntityHashMap<ObserverRunner>;
@ -262,7 +277,7 @@ impl Observers {
mut world: DeferredWorld, mut world: DeferredWorld,
event_type: ComponentId, event_type: ComponentId,
entity: Entity, entity: Entity,
components: impl Iterator<Item = ComponentId>, components: impl Iterator<Item = ComponentId> + Clone,
data: &mut T, data: &mut T,
propagate: &mut bool, propagate: &mut bool,
) { ) {
@ -279,12 +294,15 @@ impl Observers {
(world.into_deferred(), observers) (world.into_deferred(), observers)
}; };
let trigger_for_components = components.clone();
let mut trigger_observer = |(&observer, runner): (&Entity, &ObserverRunner)| { let mut trigger_observer = |(&observer, runner): (&Entity, &ObserverRunner)| {
(runner)( (runner)(
world.reborrow(), world.reborrow(),
ObserverTrigger { ObserverTrigger {
observer, observer,
event_type, event_type,
components: components.clone().collect(),
entity, entity,
}, },
data.into(), data.into(),
@ -302,7 +320,7 @@ impl Observers {
} }
// Trigger observers listening to this trigger targeting a specific component // 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) { if let Some(component_observers) = observers.component_observers.get(&id) {
component_observers component_observers
.map .map
@ -552,8 +570,10 @@ mod tests {
use alloc::vec; use alloc::vec;
use bevy_ptr::OwningPtr; use bevy_ptr::OwningPtr;
use bevy_utils::HashMap;
use crate as bevy_ecs; use crate as bevy_ecs;
use crate::component::ComponentId;
use crate::{ use crate::{
observer::{EmitDynamicTrigger, Observer, ObserverDescriptor, ObserverState, OnReplace}, observer::{EmitDynamicTrigger, Observer, ObserverDescriptor, ObserverState, OnReplace},
prelude::*, prelude::*,
@ -1268,9 +1288,6 @@ mod tests {
#[test] #[test]
fn observer_invalid_params() { fn observer_invalid_params() {
#[derive(Event)]
struct EventA;
#[derive(Resource)] #[derive(Resource)]
struct ResA; struct ResA;
@ -1289,9 +1306,6 @@ mod tests {
#[test] #[test]
fn observer_apply_deferred_from_param_set() { fn observer_apply_deferred_from_param_set() {
#[derive(Event)]
struct EventA;
#[derive(Resource)] #[derive(Resource)]
struct ResA; struct ResA;
@ -1309,4 +1323,35 @@ mod tests {
assert!(world.get_resource::<ResA>().is_some()); assert!(world.get_resource::<ResA>().is_some());
} }
#[test]
fn observer_triggered_components() {
#[derive(Resource, Default)]
struct Counter(HashMap<ComponentId, usize>);
let mut world = World::new();
world.init_resource::<Counter>();
let a_id = world.register_component::<A>();
let b_id = world.register_component::<B>();
world.add_observer(
|trigger: Trigger<EventA, (A, B)>, mut counter: ResMut<Counter>| {
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::<Counter>();
assert_eq!(4, *counter.0.get(&a_id).unwrap());
assert_eq!(3, *counter.0.get(&b_id).unwrap());
}
} }

View file

@ -423,7 +423,7 @@ macro_rules! impl_sparse_set {
} }
/// Returns an iterator visiting all keys (indices) in arbitrary order. /// Returns an iterator visiting all keys (indices) in arbitrary order.
pub fn indices(&self) -> impl Iterator<Item = I> + '_ { pub fn indices(&self) -> impl Iterator<Item = I> + Clone + '_ {
self.indices.iter().cloned() self.indices.iter().cloned()
} }

View file

@ -501,7 +501,7 @@ impl<'w> DeferredWorld<'w> {
&mut self, &mut self,
event: ComponentId, event: ComponentId,
entity: Entity, entity: Entity,
components: impl Iterator<Item = ComponentId>, components: impl Iterator<Item = ComponentId> + Clone,
) { ) {
Observers::invoke::<_>( Observers::invoke::<_>(
self.reborrow(), self.reborrow(),