Skip to content

Commit 1721bda

Browse files
authored
Add Observers section to Events book page (#2356)
1 parent eae0cb6 commit 1721bda

File tree

2 files changed

+54
-21
lines changed

2 files changed

+54
-21
lines changed

content/learn/book/control-flow/events.md

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ status = 'hidden'
88

99
<!-- TBW -->
1010

11-
While Systems are great for running logic at scheduled updates, many features need to be reactive, not scheduled. Picking up an item, performing an attack, or even hovering the mouse over an object are all actions that wouldn't work very well if they *had* to run on every schedule update. Instead of a System, we can use **Events** to activate some logic or functionality at a specific time or in response to something occurring.
11+
While Systems are great for running logic at scheduled updates, many features need to be reactive, not scheduled. Picking up an item, performing an attack, or even hovering the cursor over an object are all actions that wouldn't work very well if they *had* to run on every schedule update. Instead of a System, we can use **Events** to activate some logic or functionality at a specific time or in response to something occurring.
1212

1313
There are three required parts when using an Event: a **Trigger**, an **Observer**, and the `Event` itself.
1414

1515
- A **Trigger** is the condition that will determine when an `Event` will happen in the `World`.
1616
- The Entities that will react to our `Event` are known as **Observers**. These Entities "observe" the `World` and will run some functionality in response to our `Event`.
17-
- Finally, the `Event` is a Rust type that implements the `Event` trait.
17+
- Finally, the `Event` is a Rust type that implements the [`Event`] trait.
1818

1919
A basic use of an `Event` would something like this:
2020

@@ -27,7 +27,7 @@ struct Speak {
2727
}
2828
```
2929

30-
2. Add an [`Observer`] to the World that will watch for our event:
30+
2. Add an [`Observer`] to the `World` that will watch for our event:
3131

3232
```rust
3333
// To add the observer immediately:
@@ -79,11 +79,50 @@ commands.trigger(custom_message);
7979
[`Event`]: https://docs.rs/bevy/latest/bevy/prelude/trait.Event.html
8080
[`Observer`]: https://docs.rs/bevy/latest/bevy/prelude/struct.Observer.html
8181

82+
## Event Observers
83+
84+
Once we have defined our `Event`, we need to create an [`Observer`] that will do something when our `Event` gets triggered. This can be as straightforward as calling the `add_observer` method on [`World`] or [`Commands`], but this is not the only way to make an `Observer`.
85+
86+
At its core, `Observer` is a [`Component`] that is added to an `Entity`. When we run `World::add_observer`, all that's actually happening is telling `World` to spawn a new `Entity` with an `Observer` component. Within that `Observer` component, we specify what `Event` we want the `Entity` to react to and what should happen when the `Event` happens. As we'll see later on with `EntityEvent`, this can be very handy for when we have an `Observer` that should only run when targeting a *specific* `Entity`.
87+
88+
```rust
89+
// `world` creates a new `Entity` with an `Observer` component, watching for a `Speak` event.
90+
world.add_observer(|speak: On<Speak>| {
91+
// When `Speak` is triggered, print out the message to the console.
92+
println!("{}", speak.message);
93+
});
94+
```
95+
96+
### Observer Systems
97+
98+
As you've seen by now, an `Observer` will run some functionality when the `Event` it is observing is triggered. We're able to do this because `Observer` has the ability to run a special kind of `System` called an `ObserverSystem`. The first parameter in an `ObserverSystem` must be `On`, which provides access to the observed `Event`, however we are also able to access more data from `World`.
99+
100+
```rust
101+
world.add_observer(|event: On<DespawnEnemyUnits>, commands: Commands, enemy_units: Query<Entity, With<Enemy>>|{
102+
for enemy in &enemy_units {
103+
commands.get_entity(enemy).despawn();
104+
}
105+
})
106+
```
107+
108+
In the above example, we've created an `Observer` that will run its `ObserverSystem` when a `DespawnEnemyUnits` event is triggered. Additionally, we've accessed `Commands` and `Query` as parameters to gain access to the data we need. Within the `ObserverSystem`, we use a `for` loop to despawn every `Enemy` entity that is given in the `enemy_units` query.
109+
110+
It is also worth noting that we can trigger an `Event` within an `ObserverSystem`. Instead of triggering immediately (as is the case when using `World::trigger`), triggering an `Event` inside an `Observer` with `Commands::trigger` will run the newly triggered `ObserverSystem` at the end of the `Command` queue. Once all of the other `Observer` commands that are currently in the queue are ran, then the new `ObserverSystem` will be run. Be aware that these events are evaluated *recursively*, and will exit once there are no more events left.
111+
112+
```rust
113+
// When Event `A` is triggered, it in turn will trigger Event `B`.
114+
world.add_observer(|event: On<A>, mut commands: Commands| {
115+
commands.trigger(B);
116+
});
117+
```
118+
119+
[`Component`]: https://docs.rs/bevy/latest/bevy/prelude/trait.Component.html
120+
82121
## Event Triggers
83122

84123
Every `Event` requires a [`Trigger`], represented by default with the `On<Event>` syntax. It's best to think of a `Trigger` as the call which activates the `Event`, hence why we use `On`: our code will run `On` our `<Event>`.
85124

86-
A [`Trigger`] can be called by accessing [`World`] (via [`World::trigger`]) to run the `Event` immediately, or by using [`Commands`] (via [`Commands::trigger`]) to add the `Event` to the Command Queue.
125+
A [`Trigger`] can be called by accessing `World` (via [`World::trigger`]) to run the `Event` immediately, or by using `Commands` (via [`Commands::trigger`]) to add the `Event` to the `Command` Queue.
87126

88127
```rust
89128
// An event that deals damage to the player.
@@ -131,7 +170,7 @@ As we mentioned above, a `Trigger` can also be used to pass values from the `Eve
131170

132171
## Entity Events
133172

134-
Up until now, Events have been utilized in a *global* context, meaning that observers will run their code without taking any specific entities into account.
173+
Up until now, events have been utilized in a *global* context, meaning that observers will run their code without taking any specific entities into account.
135174

136175
What if we do have some unique functionality that we want to run on certain entities? We can achieve this by modifying our `Event` types to implement [`EntityEvent`] rather than [`Event`]:
137176

@@ -149,7 +188,7 @@ struct Explode {
149188
}
150189
```
151190

152-
With [`EntityEvent`], we are selecting a single `Entity` that will respond to our event. To determine the selected `Entity`, we need to provide the `EventEntity` with an `entity_target` field. By default, `event_target` will be set to the value inside an `entity` field in a struct (if it exists). Otherwise we can manually specify the `event_target` by using the `#[event_target]` field attribute.
191+
With [`EntityEvent`], we are selecting a single `Entity` that will be the target of our event. To determine the selected `Entity`, we need to provide the `EventEntity` with an `entity_target` field. By default, `event_target` will be set to the value inside an `entity` field in a struct (if it exists). Otherwise we can manually specify the `event_target` by using the `#[event_target]` field attribute.
153192

154193
```rust
155194
// A simple EntityEvent:
@@ -177,22 +216,25 @@ struct Explode(#[event_target] Entity, i32, f32); // Using #[event_target] insid
177216

178217
```
179218

180-
Just as with a global [`Event`], [`EntityEvent`] also needs a [`Trigger`]. Thankfully, we trigger an `EntityEvent` the same way we trigger a regular [`Event`]:
219+
Just as with a global [`Event`], [`EntityEvent`] also needs a [`Trigger`]. We trigger an `EntityEvent` the same way we trigger a regular `Event`, however this time we make sure to specify our `event_target` entity (along with any other fields on the `EntityEvent`):
181220

182221
```rust
183-
world.trigger(Explode(some_entity, 4, 2.5));
184222
// Trigger an Explode `EntityEvent`, passing in a tuple consisting of
185223
// an Entity (`some_entity`), an i32 (`4`), and a f32 (`2.5`).
224+
world.trigger(Explode(some_entity, 4, 2.5));
186225
```
187226

188-
However, [`EntityEvent`] does differ from [`Event`] when it comes to using an observer. To make an Entity observe an [`EntityEvent`], we have to select the `Entity` with [`World::entity_mut`] and then add the observer with [`EntityCommands::observe`]:
227+
However, [`EntityEvent`] does differ from [`Event`] when it comes to using an observer. To create an `Observer` for an `EntityEvent`, we have to select the target `Entity` with [`World::entity_mut`] and then create the `Observer` via [`EntityCommands::observe`]:
189228

190229
```rust
191-
/// This observer will only run for Explode events triggered for `some_entity`
230+
// This observer will only run for Explode events triggered for `some_entity`
192231
world.entity_mut(some_entity).observe(|explode: On<Explode>| {});
232+
233+
// Alternatively, `Commands::get_entity` also works.
234+
commands.entity(some_entity).observe(|explode: On<Explode>| {});
193235
```
194236

195-
It is worth noting that an [`EntityEvent`] is still an [`Event`]; we can still set global observers to watch for an [`EntityEvent`]:
237+
An `EntityEvent` is still an `Event` though; we can create global observers that will run non-entity-specific code when an `EntityEvent` is triggered:
196238

197239
```rust
198240
world.add_observer(|explode: On<Explode>| {}); // Global observer that will run when the Explode event is triggered.
@@ -224,7 +266,7 @@ world.add_observer(|mut click: On<Click>| {
224266
});
225267
```
226268

227-
Likewise, if we have an [`EntityEvent`] that an `Observer` is observing, we can explicitly disable propagation:
269+
Likewise, if we have an [`EntityEvent`] that a global `Observer` is observing, we can explicitly disable propagation:
228270

229271
```rust
230272
// Disable event propagation on an observer.

content/learn/book/control-flow/observers.md

Lines changed: 0 additions & 9 deletions
This file was deleted.

0 commit comments

Comments
 (0)