Skip to content

Commit

Permalink
handle TriggerTargets that are combinations for components/entities
Browse files Browse the repository at this point in the history
  • Loading branch information
cBournhonesque committed Jul 31, 2024
1 parent bfcb19a commit d1c3b03
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 40 deletions.
52 changes: 52 additions & 0 deletions crates/bevy_ecs/src/observer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,58 @@ mod tests {
assert_eq!(2, world.resource::<R>().0);
}

#[test]
fn observer_multiple_targets() {
let mut world = World::new();
let component_a = world.init_component::<A>();
let component_b = world.init_component::<B>();
world.init_resource::<R>();

// targets (entity_1, A)
let entity_1 = world
.spawn_empty()
.observe(|_: Trigger<EventA, A>, mut res: ResMut<R>| res.0 += 1)
.id();
// targets (entity_2, B)
let entity_2 = world
.spawn_empty()
.observe(|_: Trigger<EventA, B>, mut res: ResMut<R>| res.0 += 10)
.id();
// targets any entity or component
world.observe(|_: Trigger<EventA>, mut res: ResMut<R>| res.0 += 100);
// targets any entity, and components A or B
world.observe(|_: Trigger<EventA, (A, B)>, mut res: ResMut<R>| res.0 += 1000);

// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
// and therefore does not automatically flush.
world.flush();

// trigger for an entity and a component
world.trigger_targets(EventA, (entity_1, component_a));
world.flush();
assert_eq!(1101, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0;

// trigger for both entities, but no components: trigger once per entity target
world.trigger_targets(EventA, (entity_1, entity_2));
world.flush();
assert_eq!(200, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0;

// trigger for both components, but no entities: trigger once
world.trigger_targets(EventA, (component_a, component_b));
world.flush();
assert_eq!(1100, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0;

// trigger for both entities and both components: trigger once per entity target
// we only get 2211 and not 4211 because a given observer can trigger only once per entity target
world.trigger_targets(EventA, ((component_a, component_b), (entity_1, entity_2)));
world.flush();
assert_eq!(2211, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0;
}

#[test]
fn observer_dynamic_component() {
let mut world = World::new();
Expand Down
77 changes: 39 additions & 38 deletions crates/bevy_ecs/src/observer/trigger_event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,8 @@ fn trigger_event<E: Event, Targets: TriggerTargets>(
targets: Targets,
) {
let mut world = DeferredWorld::from(world);
if targets.entities().is_empty() {
let mut entity_targets = targets.entities().peekable();
if entity_targets.peek().is_none() {
// SAFETY: T is accessible as the type represented by self.trigger, ensured in `Self::new`
unsafe {
world.trigger_observers_with_data::<_, E::Traversal>(
Expand All @@ -77,12 +78,12 @@ fn trigger_event<E: Event, Targets: TriggerTargets>(
);
};
} else {
for target in targets.entities() {
for target_entity in entity_targets {
// SAFETY: T is accessible as the type represented by self.trigger, ensured in `Self::new`
unsafe {
world.trigger_observers_with_data::<_, E::Traversal>(
event_type,
*target,
target_entity,
targets.components(),
event_data,
E::AUTO_PROPAGATE,
Expand All @@ -100,88 +101,88 @@ fn trigger_event<E: Event, Targets: TriggerTargets>(
/// [`Observer`]: crate::observer::Observer
pub trait TriggerTargets {
/// The components the trigger should target.
fn components(&self) -> &[ComponentId];
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone;

/// The entities the trigger should target.
fn entities(&self) -> &[Entity];
fn entities(&self) -> impl Iterator<Item = Entity>;
}

impl TriggerTargets for () {
fn components(&self) -> &[ComponentId] {
&[]
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
[].into_iter()
}

fn entities(&self) -> &[Entity] {
&[]
fn entities(&self) -> impl Iterator<Item = Entity> {
[].into_iter()
}
}

impl TriggerTargets for Entity {
fn components(&self) -> &[ComponentId] {
&[]
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
[].into_iter()
}

fn entities(&self) -> &[Entity] {
std::slice::from_ref(self)
fn entities(&self) -> impl Iterator<Item = Entity> {
std::iter::once(*self)
}
}

impl TriggerTargets for Vec<Entity> {
fn components(&self) -> &[ComponentId] {
&[]
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
[].into_iter()
}

fn entities(&self) -> &[Entity] {
self.as_slice()
fn entities(&self) -> impl Iterator<Item = Entity> {
self.iter().copied()
}
}

impl<const N: usize> TriggerTargets for [Entity; N] {
fn components(&self) -> &[ComponentId] {
&[]
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
[].into_iter()
}

fn entities(&self) -> &[Entity] {
self.as_slice()
fn entities(&self) -> impl Iterator<Item = Entity> {
self.iter().copied()
}
}

impl TriggerTargets for ComponentId {
fn components(&self) -> &[ComponentId] {
std::slice::from_ref(self)
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
std::iter::once(*self)
}

fn entities(&self) -> &[Entity] {
&[]
fn entities(&self) -> impl Iterator<Item = Entity> {
[].into_iter()
}
}

impl TriggerTargets for Vec<ComponentId> {
fn components(&self) -> &[ComponentId] {
self.as_slice()
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
self.iter().copied()
}

fn entities(&self) -> &[Entity] {
&[]
fn entities(&self) -> impl Iterator<Item = Entity> {
[].into_iter()
}
}

impl<const N: usize> TriggerTargets for [ComponentId; N] {
fn components(&self) -> &[ComponentId] {
self.as_slice()
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
self.iter().copied()
}

fn entities(&self) -> &[Entity] {
&[]
fn entities(&self) -> impl Iterator<Item = Entity> {
[].into_iter()
}
}

impl TriggerTargets for &Vec<Entity> {
fn components(&self) -> &[ComponentId] {
&[]
impl<T1: TriggerTargets, T2: TriggerTargets> TriggerTargets for (T1, T2) {
fn components(&self) -> impl Iterator<Item = ComponentId> + Clone {
self.0.components().chain(self.1.components())
}

fn entities(&self) -> &[Entity] {
self.as_slice()
fn entities(&self) -> impl Iterator<Item = Entity> {
self.0.entities().chain(self.1.entities())
}
}
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/world/deferred_world.rs
Original file line number Diff line number Diff line change
Expand Up @@ -391,7 +391,7 @@ impl<'w> DeferredWorld<'w> {
&mut self,
event: ComponentId,
mut entity: Entity,
components: &[ComponentId],
components: impl Iterator<Item = ComponentId> + Clone,
data: &mut E,
mut propagate: bool,
) where
Expand All @@ -402,7 +402,7 @@ impl<'w> DeferredWorld<'w> {
self.reborrow(),
event,
entity,
components.iter().copied(),
components.clone(),
data,
&mut propagate,
);
Expand Down

0 comments on commit d1c3b03

Please sign in to comment.