8988b16bee
- Fix `ref` keyword used as variable name in code examples - Fix "retries next tick" → systems continue on normal schedule - Fix `State::set()` → `NextState::set()` in upgrade skill - Fix `SceneRoot` → `WorldAssetRoot` for 0.19 consistency - Fix Replace lifecycle event description (overwritten, not removed) - Fix grep patterns (quoting, trailing spaces) - Remove third-party physics/networking content (not Bevy built-ins)
106 lines
3.8 KiB
Markdown
106 lines
3.8 KiB
Markdown
# Events, Messages, Observers & Hooks
|
|
|
|
0.18 split the old single event system into **Messages** (pull, buffered) and **Events** (push, observed). They are different traits with different derives — using the wrong one won't compile, and porting old code mechanically to the wrong side is an architecture bug.
|
|
|
|
## Messages — pull-based, double-buffered
|
|
|
|
The renamed successor of `EventWriter`/`EventReader`. Use for decoupled many-to-many data flow where several systems batch-process the same stream.
|
|
|
|
```rust
|
|
#[derive(Message)]
|
|
struct Collision { a: Entity, b: Entity }
|
|
|
|
fn produce(mut writer: MessageWriter<Collision>) {
|
|
writer.write(Collision { a, b }); // also: write_batch(iter), write_default()
|
|
}
|
|
|
|
fn consume(mut reader: MessageReader<Collision>) {
|
|
for c in reader.read() { /* each reader has its own cursor */ }
|
|
}
|
|
```
|
|
|
|
- Double-buffered: a message survives one `update()` cycle and is dropped on the next. A reader that runs before the writer in the same frame sees it next frame.
|
|
- Multiple readers run concurrently; multiple writers of the same type cannot.
|
|
- Reader extras: `len()`, `is_empty()`, `clear()`.
|
|
- Run condition: `on_message::<M>()`.
|
|
|
|
## Events — push-based, immediate, observed
|
|
|
|
```rust
|
|
#[derive(Event)]
|
|
struct Speak { text: String }
|
|
|
|
// Global observer
|
|
app.add_observer(|on: On<Speak>| println!("{}", on.text));
|
|
|
|
// Entity-scoped observer — fires only for this entity
|
|
commands.entity(id).observe(|on: On<Hit>| { /* ... */ });
|
|
|
|
// Trigger
|
|
commands.trigger(Speak { text: "hi".into() });
|
|
world.trigger_ref(&mut event); // inspect the event after observers mutate it
|
|
```
|
|
|
|
Observer param `On<'w, 't, E, B>` (`B: Bundle` filters by component):
|
|
- `event()` / `event_mut()` — event data (also `Deref<Target = E>` for direct field access)
|
|
- `trigger()` / `trigger_mut()` — trigger context
|
|
- `observer()` — the observer entity
|
|
|
|
Observers run immediately during the trigger, not at a schedule point.
|
|
|
|
## EntityEvent — entity-targeted
|
|
|
|
```rust
|
|
#[derive(EntityEvent)]
|
|
struct Hit {
|
|
#[event_target]
|
|
entity: Entity,
|
|
damage: f32,
|
|
}
|
|
```
|
|
|
|
`#[entity_event(propagate)]` makes the event bubble up the `ChildOf` chain — useful for UI-like hit forwarding.
|
|
|
|
**0.19**: `EntityComponentsTrigger` gained archetype fields — destructure with `..`:
|
|
`let EntityComponentsTrigger { components, .. } = e.trigger();`
|
|
|
|
## Lifecycle events
|
|
|
|
Built-in events observers can watch per component:
|
|
|
|
| Event | Fires | Order |
|
|
|-------|-------|-------|
|
|
| `Add` | component added where absent | first |
|
|
| `Insert` | any insert (add or replace) | after `Add` |
|
|
| `Replace` | component value overwritten by a new insert over an existing value | before `Remove` |
|
|
| `Remove` | component removed | after `Replace` |
|
|
| `Despawn` | entity despawned (per component) | last |
|
|
|
|
```rust
|
|
world.add_observer(|on: On<Add, Health>| { /* entity just gained Health */ });
|
|
```
|
|
|
|
## Component hooks — synchronous, inline
|
|
|
|
For callbacks that must be atomic with the ECS operation itself (not deferred, not scheduled):
|
|
|
|
```rust
|
|
#[derive(Component)]
|
|
#[component(on_add = init_health_bar)]
|
|
struct Health(f32);
|
|
|
|
fn init_health_bar(world: DeferredWorld, ctx: HookContext) { /* ... */ }
|
|
```
|
|
|
|
Available: `on_add`, `on_insert`, `on_replace`, `on_remove`, `on_despawn`.
|
|
|
|
## Choosing the mechanism
|
|
|
|
| Mechanism | Model | Reach for it when |
|
|
|-----------|-------|-------------------|
|
|
| Messages | pull, buffered, scheduled | several systems independently process a stream; frame-batched work |
|
|
| Event + observer | push, immediate | something must react *now* (gameplay reactions, structural responses) |
|
|
| EntityEvent | push, targeted, can propagate | the reaction belongs to a specific entity |
|
|
| Component hook | synchronous, inline | the response must not be separable from the ECS operation |
|
|
| `Changed<T>` / `Added<T>` | per-tick polling | cheap frame-wise checks inside a normal system |
|