Rename App/World::observe to add_observer, EntityWorldMut::observe_entity to observe. (#15754)

# Objective

- Closes #15752

Calling the functions `App::observe` and `World::observe` doesn't make
sense because you're not "observing" the `App` or `World`, you're adding
an observer that listens for an event that occurs *within* the `World`.
We should rename them to better fit this.

## Solution

Renames:
- `App::observe` -> `App::add_observer`
- `World::observe` -> `World::add_observer`
- `Commands::observe` -> `Commands::add_observer`
- `EntityWorldMut::observe_entity` -> `EntityWorldMut::observe`

(Note this isn't a breaking change as the original rename was introduced
earlier this cycle.)

## Testing

Reusing current tests.
This commit is contained in:
Christian Hughes 2024-10-09 08:39:29 -07:00 committed by GitHub
parent a2b53d46e7
commit 219b5930f1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 168 additions and 106 deletions

View file

@ -106,15 +106,15 @@ fn add_listeners_to_hierarchy<const DENSITY: usize, const N: usize>(
world: &mut World, world: &mut World,
) { ) {
for e in roots.iter() { for e in roots.iter() {
world.entity_mut(*e).observe_entity(empty_listener::<N>); world.entity_mut(*e).observe(empty_listener::<N>);
} }
for e in leaves.iter() { for e in leaves.iter() {
world.entity_mut(*e).observe_entity(empty_listener::<N>); world.entity_mut(*e).observe(empty_listener::<N>);
} }
let mut rng = deterministic_rand(); let mut rng = deterministic_rand();
for e in nodes.iter() { for e in nodes.iter() {
if rng.gen_bool(DENSITY as f64 / 100.0) { if rng.gen_bool(DENSITY as f64 / 100.0) {
world.entity_mut(*e).observe_entity(empty_listener::<N>); world.entity_mut(*e).observe(empty_listener::<N>);
} }
} }
} }

View file

@ -17,7 +17,7 @@ pub fn observe_simple(criterion: &mut Criterion) {
group.bench_function("trigger_simple", |bencher| { group.bench_function("trigger_simple", |bencher| {
let mut world = World::new(); let mut world = World::new();
world.observe(empty_listener_base); world.add_observer(empty_listener_base);
bencher.iter(|| { bencher.iter(|| {
for _ in 0..10000 { for _ in 0..10000 {
world.trigger(EventBase) world.trigger(EventBase)
@ -29,7 +29,7 @@ pub fn observe_simple(criterion: &mut Criterion) {
let mut world = World::new(); let mut world = World::new();
let mut entities = vec![]; let mut entities = vec![];
for _ in 0..10000 { for _ in 0..10000 {
entities.push(world.spawn_empty().observe_entity(empty_listener_base).id()); entities.push(world.spawn_empty().observe(empty_listener_base).id());
} }
entities.shuffle(&mut deterministic_rand()); entities.shuffle(&mut deterministic_rand());
bencher.iter(|| { bencher.iter(|| {

View file

@ -1267,7 +1267,7 @@ impl App {
/// # struct Friend; /// # struct Friend;
/// # /// #
/// // An observer system can be any system where the first parameter is a trigger /// // An observer system can be any system where the first parameter is a trigger
/// app.observe(|trigger: Trigger<Party>, friends: Query<Entity, With<Friend>>, mut commands: Commands| { /// app.add_observer(|trigger: Trigger<Party>, friends: Query<Entity, With<Friend>>, mut commands: Commands| {
/// if trigger.event().friends_allowed { /// if trigger.event().friends_allowed {
/// for friend in friends.iter() { /// for friend in friends.iter() {
/// commands.trigger_targets(Invite, friend); /// commands.trigger_targets(Invite, friend);
@ -1275,11 +1275,11 @@ impl App {
/// } /// }
/// }); /// });
/// ``` /// ```
pub fn observe<E: Event, B: Bundle, M>( pub fn add_observer<E: Event, B: Bundle, M>(
&mut self, &mut self,
observer: impl IntoObserverSystem<E, B, M>, observer: impl IntoObserverSystem<E, B, M>,
) -> &mut Self { ) -> &mut Self {
self.world_mut().observe(observer); self.world_mut().add_observer(observer);
self self
} }
} }

View file

@ -25,7 +25,7 @@ pub(crate) fn send_events(world: &mut World, mut current_frame: Local<u32>) {
let path = format!("./screenshot-{}.png", *current_frame); let path = format!("./screenshot-{}.png", *current_frame);
world world
.spawn(Screenshot::primary_window()) .spawn(Screenshot::primary_window())
.observe_entity(save_to_disk(path)); .observe(save_to_disk(path));
info!("Took a screenshot at frame {}.", *current_frame); info!("Took a screenshot at frame {}.", *current_frame);
} }
// Custom events are forwarded to the world. // Custom events are forwarded to the world.

View file

@ -315,7 +315,7 @@ struct MyEvent {
let mut world = World::new(); let mut world = World::new();
world.observe(|trigger: Trigger<MyEvent>| { world.add_observer(|trigger: Trigger<MyEvent>| {
println!("{}", trigger.event().message); println!("{}", trigger.event().message);
}); });
@ -339,7 +339,7 @@ struct Explode;
let mut world = World::new(); let mut world = World::new();
let entity = world.spawn_empty().id(); let entity = world.spawn_empty().id();
world.observe(|trigger: Trigger<Explode>, mut commands: Commands| { world.add_observer(|trigger: Trigger<Explode>, mut commands: Commands| {
println!("Entity {:?} goes BOOM!", trigger.entity()); println!("Entity {:?} goes BOOM!", trigger.entity());
commands.entity(trigger.entity()).despawn(); commands.entity(trigger.entity()).despawn();
}); });

View file

@ -364,8 +364,29 @@ impl Observers {
} }
impl World { impl World {
/// Spawns a "global" [`Observer`] and returns its [`Entity`]. /// Spawns a "global" [`Observer`] which will watch for the given event.
pub fn observe<E: Event, B: Bundle, M>( /// Returns its [`Entity`] as a [`EntityWorldMut`].
///
/// **Calling [`observe`](EntityWorldMut::observe) on the returned
/// [`EntityWorldMut`] will observe the observer itself, which you very
/// likely do not want.**
///
/// # Example
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #[derive(Component)]
/// struct A;
///
/// # let mut world = World::new();
/// world.add_observer(|_: Trigger<OnAdd, A>| {
/// // ...
/// });
/// world.add_observer(|_: Trigger<OnRemove, A>| {
/// // ...
/// });
/// ```
pub fn add_observer<E: Event, B: Bundle, M>(
&mut self, &mut self,
system: impl IntoObserverSystem<E, B, M>, system: impl IntoObserverSystem<E, B, M>,
) -> EntityWorldMut { ) -> EntityWorldMut {
@ -593,10 +614,14 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add")); world.add_observer(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert")); world
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| res.observed("replace")); .add_observer(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove")); world.add_observer(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| {
res.observed("replace");
});
world
.add_observer(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove"));
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.despawn(entity); world.despawn(entity);
@ -611,10 +636,14 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add")); world.add_observer(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert")); world
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| res.observed("replace")); .add_observer(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove")); world.add_observer(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| {
res.observed("replace");
});
world
.add_observer(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove"));
let mut entity = world.spawn_empty(); let mut entity = world.spawn_empty();
entity.insert(A); entity.insert(A);
@ -631,10 +660,14 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, S>, mut res: ResMut<Order>| res.observed("add")); world.add_observer(|_: Trigger<OnAdd, S>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, S>, mut res: ResMut<Order>| res.observed("insert")); world
world.observe(|_: Trigger<OnReplace, S>, mut res: ResMut<Order>| res.observed("replace")); .add_observer(|_: Trigger<OnInsert, S>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnRemove, S>, mut res: ResMut<Order>| res.observed("remove")); world.add_observer(|_: Trigger<OnReplace, S>, mut res: ResMut<Order>| {
res.observed("replace");
});
world
.add_observer(|_: Trigger<OnRemove, S>, mut res: ResMut<Order>| res.observed("remove"));
let mut entity = world.spawn_empty(); let mut entity = world.spawn_empty();
entity.insert(S); entity.insert(S);
@ -653,10 +686,14 @@ mod tests {
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add")); world.add_observer(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add"));
world.observe(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert")); world
world.observe(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| res.observed("replace")); .add_observer(|_: Trigger<OnInsert, A>, mut res: ResMut<Order>| res.observed("insert"));
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove")); world.add_observer(|_: Trigger<OnReplace, A>, mut res: ResMut<Order>| {
res.observed("replace");
});
world
.add_observer(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove"));
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
// and therefore does not automatically flush. // and therefore does not automatically flush.
@ -672,26 +709,26 @@ mod tests {
fn observer_order_recursive() { fn observer_order_recursive() {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe( world.add_observer(
|obs: Trigger<OnAdd, A>, mut res: ResMut<Order>, mut commands: Commands| { |obs: Trigger<OnAdd, A>, mut res: ResMut<Order>, mut commands: Commands| {
res.observed("add_a"); res.observed("add_a");
commands.entity(obs.entity()).insert(B); commands.entity(obs.entity()).insert(B);
}, },
); );
world.observe( world.add_observer(
|obs: Trigger<OnRemove, A>, mut res: ResMut<Order>, mut commands: Commands| { |obs: Trigger<OnRemove, A>, mut res: ResMut<Order>, mut commands: Commands| {
res.observed("remove_a"); res.observed("remove_a");
commands.entity(obs.entity()).remove::<B>(); commands.entity(obs.entity()).remove::<B>();
}, },
); );
world.observe( world.add_observer(
|obs: Trigger<OnAdd, B>, mut res: ResMut<Order>, mut commands: Commands| { |obs: Trigger<OnAdd, B>, mut res: ResMut<Order>, mut commands: Commands| {
res.observed("add_b"); res.observed("add_b");
commands.entity(obs.entity()).remove::<A>(); commands.entity(obs.entity()).remove::<A>();
}, },
); );
world.observe(|_: Trigger<OnRemove, B>, mut res: ResMut<Order>| { world.add_observer(|_: Trigger<OnRemove, B>, mut res: ResMut<Order>| {
res.observed("remove_b"); res.observed("remove_b");
}); });
@ -709,11 +746,11 @@ mod tests {
fn observer_trigger_ref() { fn observer_trigger_ref() {
let mut world = World::new(); let mut world = World::new();
world.observe(|mut trigger: Trigger<EventWithData>| trigger.event_mut().counter += 1); world.add_observer(|mut trigger: Trigger<EventWithData>| trigger.event_mut().counter += 1);
world.observe(|mut trigger: Trigger<EventWithData>| trigger.event_mut().counter += 2); world.add_observer(|mut trigger: Trigger<EventWithData>| trigger.event_mut().counter += 2);
world.observe(|mut trigger: Trigger<EventWithData>| trigger.event_mut().counter += 4); world.add_observer(|mut trigger: Trigger<EventWithData>| trigger.event_mut().counter += 4);
// This flush is required for the last observer to be called when triggering the event, // This flush is required for the last observer to be called when triggering the event,
// due to `World::observe` returning `WorldEntityMut`. // due to `World::add_observer` returning `WorldEntityMut`.
world.flush(); world.flush();
let mut event = EventWithData { counter: 0 }; let mut event = EventWithData { counter: 0 };
@ -725,11 +762,17 @@ mod tests {
fn observer_trigger_targets_ref() { fn observer_trigger_targets_ref() {
let mut world = World::new(); let mut world = World::new();
world.observe(|mut trigger: Trigger<EventWithData, A>| trigger.event_mut().counter += 1); world.add_observer(|mut trigger: Trigger<EventWithData, A>| {
world.observe(|mut trigger: Trigger<EventWithData, B>| trigger.event_mut().counter += 2); trigger.event_mut().counter += 1;
world.observe(|mut trigger: Trigger<EventWithData, A>| trigger.event_mut().counter += 4); });
world.add_observer(|mut trigger: Trigger<EventWithData, B>| {
trigger.event_mut().counter += 2;
});
world.add_observer(|mut trigger: Trigger<EventWithData, A>| {
trigger.event_mut().counter += 4;
});
// This flush is required for the last observer to be called when triggering the event, // This flush is required for the last observer to be called when triggering the event,
// due to `World::observe` returning `WorldEntityMut`. // due to `World::add_observer` returning `WorldEntityMut`.
world.flush(); world.flush();
let mut event = EventWithData { counter: 0 }; let mut event = EventWithData { counter: 0 };
@ -743,8 +786,8 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add_1")); world.add_observer(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add_1"));
world.observe(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add_2")); world.add_observer(|_: Trigger<OnAdd, A>, mut res: ResMut<Order>| res.observed("add_2"));
world.spawn(A).flush(); world.spawn(A).flush();
assert_eq!(vec!["add_1", "add_2"], world.resource::<Order>().0); assert_eq!(vec!["add_1", "add_2"], world.resource::<Order>().0);
@ -782,7 +825,9 @@ mod tests {
world.register_component::<A>(); world.register_component::<A>();
world.register_component::<B>(); world.register_component::<B>();
world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<Order>| res.observed("add_ab")); world.add_observer(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<Order>| {
res.observed("add_ab");
});
let entity = world.spawn(A).id(); let entity = world.spawn(A).id();
world.entity_mut(entity).insert(B); world.entity_mut(entity).insert(B);
@ -795,7 +840,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
let observer = world let observer = world
.observe(|_: Trigger<OnAdd, A>| panic!("Observer triggered after being despawned.")) .add_observer(|_: Trigger<OnAdd, A>| {
panic!("Observer triggered after being despawned.")
})
.id(); .id();
world.despawn(observer); world.despawn(observer);
world.spawn(A).flush(); world.spawn(A).flush();
@ -809,10 +856,14 @@ mod tests {
let entity = world.spawn((A, B)).flush(); let entity = world.spawn((A, B)).flush();
world.observe(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| res.observed("remove_a")); world.add_observer(|_: Trigger<OnRemove, A>, mut res: ResMut<Order>| {
res.observed("remove_a");
});
let observer = world let observer = world
.observe(|_: Trigger<OnRemove, B>| panic!("Observer triggered after being despawned.")) .add_observer(|_: Trigger<OnRemove, B>| {
panic!("Observer triggered after being despawned.")
})
.flush(); .flush();
world.despawn(observer); world.despawn(observer);
@ -826,7 +877,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<Order>| res.observed("add_ab")); world.add_observer(|_: Trigger<OnAdd, (A, B)>, mut res: ResMut<Order>| {
res.observed("add_ab");
});
world.spawn((A, B)).flush(); world.spawn((A, B)).flush();
assert_eq!(vec!["add_ab"], world.resource::<Order>().0); assert_eq!(vec!["add_ab"], world.resource::<Order>().0);
@ -839,8 +892,8 @@ mod tests {
world world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity.")); .observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity."));
world.observe(move |obs: Trigger<EventA>, mut res: ResMut<Order>| { world.add_observer(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
assert_eq!(obs.entity(), Entity::PLACEHOLDER); assert_eq!(obs.entity(), Entity::PLACEHOLDER);
res.observed("event_a"); res.observed("event_a");
}); });
@ -860,12 +913,12 @@ mod tests {
world world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity.")); .observe(|_: Trigger<EventA>| panic!("Trigger routed to non-targeted entity."));
let entity = world let entity = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventA>, mut res: ResMut<Order>| res.observed("a_1")) .observe(|_: Trigger<EventA>, mut res: ResMut<Order>| res.observed("a_1"))
.id(); .id();
world.observe(move |obs: Trigger<EventA>, mut res: ResMut<Order>| { world.add_observer(move |obs: Trigger<EventA>, mut res: ResMut<Order>| {
assert_eq!(obs.entity(), entity); assert_eq!(obs.entity(), entity);
res.observed("a_2"); res.observed("a_2");
}); });
@ -931,14 +984,14 @@ mod tests {
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent"); res.observed("parent");
}) })
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child"); res.observed("child");
}) })
.id(); .id();
@ -958,14 +1011,14 @@ mod tests {
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent"); res.observed("parent");
}) })
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child"); res.observed("child");
}) })
.id(); .id();
@ -988,14 +1041,14 @@ mod tests {
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent"); res.observed("parent");
}) })
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child"); res.observed("child");
}) })
.id(); .id();
@ -1018,14 +1071,14 @@ mod tests {
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent"); res.observed("parent");
}) })
.id(); .id();
let child = world let child = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe_entity( .observe(
|mut trigger: Trigger<EventPropagating>, mut res: ResMut<Order>| { |mut trigger: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child"); res.observed("child");
trigger.propagate(false); trigger.propagate(false);
@ -1048,21 +1101,21 @@ mod tests {
let parent = world let parent = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent"); res.observed("parent");
}) })
.id(); .id();
let child_a = world let child_a = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child_a"); res.observed("child_a");
}) })
.id(); .id();
let child_b = world let child_b = world
.spawn(Parent(parent)) .spawn(Parent(parent))
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child_b"); res.observed("child_b");
}) })
.id(); .id();
@ -1085,7 +1138,7 @@ mod tests {
let entity = world let entity = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("event"); res.observed("event");
}) })
.id(); .id();
@ -1105,14 +1158,14 @@ mod tests {
let parent_a = world let parent_a = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent_a"); res.observed("parent_a");
}) })
.id(); .id();
let child_a = world let child_a = world
.spawn(Parent(parent_a)) .spawn(Parent(parent_a))
.observe_entity( .observe(
|mut trigger: Trigger<EventPropagating>, mut res: ResMut<Order>| { |mut trigger: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child_a"); res.observed("child_a");
trigger.propagate(false); trigger.propagate(false);
@ -1122,14 +1175,14 @@ mod tests {
let parent_b = world let parent_b = world
.spawn_empty() .spawn_empty()
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent_b"); res.observed("parent_b");
}) })
.id(); .id();
let child_b = world let child_b = world
.spawn(Parent(parent_b)) .spawn(Parent(parent_b))
.observe_entity(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| { .observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child_b"); res.observed("child_b");
}) })
.id(); .id();
@ -1150,7 +1203,9 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| res.observed("event")); world.add_observer(|_: Trigger<EventPropagating>, mut res: ResMut<Order>| {
res.observed("event");
});
let grandparent = world.spawn_empty().id(); let grandparent = world.spawn_empty().id();
let parent = world.spawn(Parent(grandparent)).id(); let parent = world.spawn(Parent(grandparent)).id();
@ -1169,7 +1224,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
world.observe( world.add_observer(
|trigger: Trigger<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| { |trigger: Trigger<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| {
if query.get(trigger.entity()).is_ok() { if query.get(trigger.entity()).is_ok() {
res.observed("event"); res.observed("event");
@ -1196,7 +1251,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
// Observe the removal of A - this will run during despawn // Observe the removal of A - this will run during despawn
world.observe(|_: Trigger<OnRemove, A>, mut cmd: Commands| { world.add_observer(|_: Trigger<OnRemove, A>, mut cmd: Commands| {
// Spawn a new entity - this reserves a new ID and requires a flush // Spawn a new entity - this reserves a new ID and requires a flush
// afterward before Entities::free can be called. // afterward before Entities::free can be called.
cmd.spawn_empty(); cmd.spawn_empty();
@ -1224,7 +1279,7 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
// This fails because `ResA` is not present in the world // This fails because `ResA` is not present in the world
world.observe(|_: Trigger<EventA>, _: Res<ResA>, mut commands: Commands| { world.add_observer(|_: Trigger<EventA>, _: Res<ResA>, mut commands: Commands| {
commands.insert_resource(ResB); commands.insert_resource(ResB);
}); });
world.trigger(EventA); world.trigger(EventA);
@ -1241,7 +1296,7 @@ mod tests {
struct ResA; struct ResA;
let mut world = World::new(); let mut world = World::new();
world.observe( world.add_observer(
|_: Trigger<EventA>, mut params: ParamSet<(Query<Entity>, Commands)>| { |_: Trigger<EventA>, mut params: ParamSet<(Query<Entity>, Commands)>| {
params.p1().insert_resource(ResA); params.p1().insert_resource(ResA);
}, },

View file

@ -111,7 +111,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// message: String, /// message: String,
/// } /// }
/// ///
/// world.observe(|trigger: Trigger<Speak>| { /// world.add_observer(|trigger: Trigger<Speak>| {
/// println!("{}", trigger.event().message); /// println!("{}", trigger.event().message);
/// }); /// });
/// ///
@ -124,7 +124,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// }); /// });
/// ``` /// ```
/// ///
/// Notice that we used [`World::observe`]. This is just a shorthand for spawning an [`Observer`] manually: /// Notice that we used [`World::add_observer`]. This is just a shorthand for spawning an [`Observer`] manually:
/// ///
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
@ -132,7 +132,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// # #[derive(Event)] /// # #[derive(Event)]
/// # struct Speak; /// # struct Speak;
/// // These are functionally the same: /// // These are functionally the same:
/// world.observe(|trigger: Trigger<Speak>| {}); /// world.add_observer(|trigger: Trigger<Speak>| {});
/// world.spawn(Observer::new(|trigger: Trigger<Speak>| {})); /// world.spawn(Observer::new(|trigger: Trigger<Speak>| {}));
/// ``` /// ```
/// ///
@ -145,7 +145,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// # struct PrintNames; /// # struct PrintNames;
/// # #[derive(Component, Debug)] /// # #[derive(Component, Debug)]
/// # struct Name; /// # struct Name;
/// world.observe(|trigger: Trigger<PrintNames>, names: Query<&Name>| { /// world.add_observer(|trigger: Trigger<PrintNames>, names: Query<&Name>| {
/// for name in &names { /// for name in &names {
/// println!("{name:?}"); /// println!("{name:?}");
/// } /// }
@ -163,7 +163,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// # struct SpawnThing; /// # struct SpawnThing;
/// # #[derive(Component, Debug)] /// # #[derive(Component, Debug)]
/// # struct Thing; /// # struct Thing;
/// world.observe(|trigger: Trigger<SpawnThing>, mut commands: Commands| { /// world.add_observer(|trigger: Trigger<SpawnThing>, mut commands: Commands| {
/// commands.spawn(Thing); /// commands.spawn(Thing);
/// }); /// });
/// ``` /// ```
@ -177,7 +177,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// # struct A; /// # struct A;
/// # #[derive(Event)] /// # #[derive(Event)]
/// # struct B; /// # struct B;
/// world.observe(|trigger: Trigger<A>, mut commands: Commands| { /// world.add_observer(|trigger: Trigger<A>, mut commands: Commands| {
/// commands.trigger(B); /// commands.trigger(B);
/// }); /// });
/// ``` /// ```
@ -195,7 +195,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// #[derive(Event)] /// #[derive(Event)]
/// struct Explode; /// struct Explode;
/// ///
/// world.observe(|trigger: Trigger<Explode>, mut commands: Commands| { /// world.add_observer(|trigger: Trigger<Explode>, mut commands: Commands| {
/// println!("Entity {:?} goes BOOM!", trigger.entity()); /// println!("Entity {:?} goes BOOM!", trigger.entity());
/// commands.entity(trigger.entity()).despawn(); /// commands.entity(trigger.entity()).despawn();
/// }); /// });
@ -228,12 +228,12 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// # let e2 = world.spawn_empty().id(); /// # let e2 = world.spawn_empty().id();
/// # #[derive(Event)] /// # #[derive(Event)]
/// # struct Explode; /// # struct Explode;
/// world.entity_mut(e1).observe_entity(|trigger: Trigger<Explode>, mut commands: Commands| { /// world.entity_mut(e1).observe(|trigger: Trigger<Explode>, mut commands: Commands| {
/// println!("Boom!"); /// println!("Boom!");
/// commands.entity(trigger.entity()).despawn(); /// commands.entity(trigger.entity()).despawn();
/// }); /// });
/// ///
/// world.entity_mut(e2).observe_entity(|trigger: Trigger<Explode>, mut commands: Commands| { /// world.entity_mut(e2).observe(|trigger: Trigger<Explode>, mut commands: Commands| {
/// println!("The explosion fizzles! This entity is immune!"); /// println!("The explosion fizzles! This entity is immune!");
/// }); /// });
/// ``` /// ```
@ -241,7 +241,7 @@ pub type ObserverRunner = fn(DeferredWorld, ObserverTrigger, PtrMut, propagate:
/// If all entities watched by a given [`Observer`] are despawned, the [`Observer`] entity will also be despawned. /// If all entities watched by a given [`Observer`] are despawned, the [`Observer`] entity will also be despawned.
/// This protects against observer "garbage" building up over time. /// This protects against observer "garbage" building up over time.
/// ///
/// The examples above calling [`EntityWorldMut::observe_entity`] to add entity-specific observer logic are (once again) /// The examples above calling [`EntityWorldMut::observe`] to add entity-specific observer logic are (once again)
/// just shorthand for spawning an [`Observer`] directly: /// just shorthand for spawning an [`Observer`] directly:
/// ///
/// ``` /// ```

View file

@ -832,8 +832,13 @@ impl<'w, 's> Commands<'w, 's> {
self.queue(TriggerEvent { event, targets }); self.queue(TriggerEvent { event, targets });
} }
/// Spawns an [`Observer`] and returns the [`EntityCommands`] associated with the entity that stores the observer. /// Spawns an [`Observer`] and returns the [`EntityCommands`] associated
pub fn observe<E: Event, B: Bundle, M>( /// with the entity that stores the observer.
///
/// **Calling [`observe`](EntityCommands::observe) on the returned
/// [`EntityCommands`] will observe the observer itself, which you very
/// likely do not want.**
pub fn add_observer<E: Event, B: Bundle, M>(
&mut self, &mut self,
observer: impl IntoObserverSystem<E, B, M>, observer: impl IntoObserverSystem<E, B, M>,
) -> EntityCommands { ) -> EntityCommands {
@ -1923,7 +1928,7 @@ fn observe<E: Event, B: Bundle, M>(
) -> impl EntityCommand { ) -> impl EntityCommand {
move |entity: Entity, world: &mut World| { move |entity: Entity, world: &mut World| {
if let Ok(mut entity) = world.get_entity_mut(entity) { if let Ok(mut entity) = world.get_entity_mut(entity) {
entity.observe_entity(observer); entity.observe(observer);
} }
} }
} }

View file

@ -70,7 +70,7 @@ mod tests {
fn b() {} fn b() {}
let mut world = World::new(); let mut world = World::new();
world.observe(a.pipe(b)); world.add_observer(a.pipe(b));
} }
#[test] #[test]
@ -81,6 +81,6 @@ mod tests {
fn b(_: In<u32>) {} fn b(_: In<u32>) {}
let mut world = World::new(); let mut world = World::new();
world.observe(a.pipe(b)); world.add_observer(a.pipe(b));
} }
} }

View file

@ -1854,7 +1854,7 @@ impl<'w> EntityWorldMut<'w> {
/// Creates an [`Observer`] listening for events of type `E` targeting this entity. /// Creates an [`Observer`] listening for events of type `E` targeting this entity.
/// In order to trigger the callback the entity must also match the query when the event is fired. /// In order to trigger the callback the entity must also match the query when the event is fired.
pub fn observe_entity<E: Event, B: Bundle, M>( pub fn observe<E: Event, B: Bundle, M>(
&mut self, &mut self,
observer: impl IntoObserverSystem<E, B, M>, observer: impl IntoObserverSystem<E, B, M>,
) -> &mut Self { ) -> &mut Self {

View file

@ -458,8 +458,10 @@ impl Plugin for PbrPlugin {
) )
.init_resource::<LightMeta>(); .init_resource::<LightMeta>();
render_app.world_mut().observe(add_light_view_entities); render_app.world_mut().add_observer(add_light_view_entities);
render_app.world_mut().observe(remove_light_view_entities); render_app
.world_mut()
.add_observer(remove_light_view_entities);
let shadow_pass_node = ShadowPassNode::new(render_app.world_mut()); let shadow_pass_node = ShadowPassNode::new(render_app.world_mut());
let mut graph = render_app.world_mut().resource_mut::<RenderGraph>(); let mut graph = render_app.world_mut().resource_mut::<RenderGraph>();

View file

@ -11,7 +11,7 @@
//! # use bevy_picking::prelude::*; //! # use bevy_picking::prelude::*;
//! # let mut world = World::default(); //! # let mut world = World::default();
//! world.spawn_empty() //! world.spawn_empty()
//! .observe_entity(|trigger: Trigger<Pointer<Over>>| { //! .observe(|trigger: Trigger<Pointer<Over>>| {
//! println!("I am being hovered over"); //! println!("I am being hovered over");
//! }); //! });
//! ``` //! ```

View file

@ -15,7 +15,7 @@
//! # struct MyComponent; //! # struct MyComponent;
//! # let mut world = World::new(); //! # let mut world = World::new();
//! world.spawn(MyComponent) //! world.spawn(MyComponent)
//! .observe_entity(|mut trigger: Trigger<Pointer<Click>>| { //! .observe(|mut trigger: Trigger<Pointer<Click>>| {
//! // Get the underlying event type //! // Get the underlying event type
//! let click_event: &Pointer<Click> = trigger.event(); //! let click_event: &Pointer<Click> = trigger.event();
//! // Stop the event from bubbling up the entity hierarchjy //! // Stop the event from bubbling up the entity hierarchjy

View file

@ -85,12 +85,12 @@ pub struct SyncWorldPlugin;
impl Plugin for SyncWorldPlugin { impl Plugin for SyncWorldPlugin {
fn build(&self, app: &mut bevy_app::App) { fn build(&self, app: &mut bevy_app::App) {
app.init_resource::<PendingSyncEntity>(); app.init_resource::<PendingSyncEntity>();
app.observe( app.add_observer(
|trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| { |trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
pending.push(EntityRecord::Added(trigger.entity())); pending.push(EntityRecord::Added(trigger.entity()));
}, },
); );
app.observe( app.add_observer(
|trigger: Trigger<OnRemove, SyncToRenderWorld>, |trigger: Trigger<OnRemove, SyncToRenderWorld>,
mut pending: ResMut<PendingSyncEntity>, mut pending: ResMut<PendingSyncEntity>,
query: Query<&RenderEntity>| { query: Query<&RenderEntity>| {
@ -248,12 +248,12 @@ mod tests {
let mut render_world = World::new(); let mut render_world = World::new();
main_world.init_resource::<PendingSyncEntity>(); main_world.init_resource::<PendingSyncEntity>();
main_world.observe( main_world.add_observer(
|trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| { |trigger: Trigger<OnAdd, SyncToRenderWorld>, mut pending: ResMut<PendingSyncEntity>| {
pending.push(EntityRecord::Added(trigger.entity())); pending.push(EntityRecord::Added(trigger.entity()));
}, },
); );
main_world.observe( main_world.add_observer(
|trigger: Trigger<OnRemove, SyncToRenderWorld>, |trigger: Trigger<OnRemove, SyncToRenderWorld>,
mut pending: ResMut<PendingSyncEntity>, mut pending: ResMut<PendingSyncEntity>,
query: Query<&RenderEntity>| { query: Query<&RenderEntity>| {

View file

@ -581,7 +581,7 @@ mod tests {
fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Entity) { fn observe_trigger(app: &mut App, scene_id: InstanceId, scene_entity: Entity) {
// Add observer // Add observer
app.world_mut().observe( app.world_mut().add_observer(
move |trigger: Trigger<SceneInstanceReady>, move |trigger: Trigger<SceneInstanceReady>,
scene_spawner: Res<SceneSpawner>, scene_spawner: Res<SceneSpawner>,
mut trigger_count: ResMut<TriggerCount>| { mut trigger_count: ResMut<TriggerCount>| {

View file

@ -31,7 +31,7 @@ impl Plugin for CursorPlugin {
.init_resource::<CustomCursorCache>() .init_resource::<CustomCursorCache>()
.add_systems(Last, update_cursors); .add_systems(Last, update_cursors);
app.observe(on_remove_cursor_icon); app.add_observer(on_remove_cursor_icon);
} }
} }

View file

@ -24,7 +24,7 @@ fn main() {
.add_systems(Startup, setup) .add_systems(Startup, setup)
.add_systems(Update, setup_scene_once_loaded.before(animate_targets)) .add_systems(Update, setup_scene_once_loaded.before(animate_targets))
.add_systems(Update, (keyboard_animation_control, simulate_particles)) .add_systems(Update, (keyboard_animation_control, simulate_particles))
.observe(observe_on_step) .add_observer(observe_on_step)
.run(); .run();
} }

View file

@ -14,7 +14,7 @@ fn main() {
attack_armor.run_if(on_timer(Duration::from_millis(200))), attack_armor.run_if(on_timer(Duration::from_millis(200))),
) )
// Add a global observer that will emit a line whenever an attack hits an entity. // Add a global observer that will emit a line whenever an attack hits an entity.
.observe(attack_hits) .add_observer(attack_hits)
.run(); .run();
} }

View file

@ -15,7 +15,7 @@ fn main() {
.add_systems(Update, (draw_shapes, handle_click)) .add_systems(Update, (draw_shapes, handle_click))
// Observers are systems that run when an event is "triggered". This observer runs whenever // Observers are systems that run when an event is "triggered". This observer runs whenever
// `ExplodeMines` is triggered. // `ExplodeMines` is triggered.
.observe( .add_observer(
|trigger: Trigger<ExplodeMines>, |trigger: Trigger<ExplodeMines>,
mines: Query<&Mine>, mines: Query<&Mine>,
index: Res<SpatialIndex>, index: Res<SpatialIndex>,
@ -35,10 +35,10 @@ fn main() {
}, },
) )
// This observer runs whenever the `Mine` component is added to an entity, and places it in a simple spatial index. // This observer runs whenever the `Mine` component is added to an entity, and places it in a simple spatial index.
.observe(on_add_mine) .add_observer(on_add_mine)
// This observer runs whenever the `Mine` component is removed from an entity (including despawning it) // This observer runs whenever the `Mine` component is removed from an entity (including despawning it)
// and removes it from the spatial index. // and removes it from the spatial index.
.observe(on_remove_mine) .add_observer(on_remove_mine)
.run(); .run();
} }

View file

@ -17,7 +17,7 @@ fn main() {
// This system will remove a component after two seconds. // This system will remove a component after two seconds.
.add_systems(Update, remove_component) .add_systems(Update, remove_component)
// This observer will react to the removal of the component. // This observer will react to the removal of the component.
.observe(react_on_removal) .add_observer(react_on_removal)
.run(); .run();
} }