bevy/crates
James O'Brien eb3c81374a
Generalised ECS reactivity with Observers (#10839)
# Objective

- Provide an expressive way to register dynamic behavior in response to
ECS changes that is consistent with existing bevy types and traits as to
provide a smooth user experience.
- Provide a mechanism for immediate changes in response to events during
command application in order to facilitate improved query caching on the
path to relations.

## Solution

- A new fundamental ECS construct, the `Observer`; inspired by flec's
observers but adapted to better fit bevy's access patterns and rust's
type system.

---

## Examples
There are 3 main ways to register observers. The first is a "component
observer" that looks like this:
```rust
world.observe(|trigger: Trigger<OnAdd, Transform>, query: Query<&Transform>| {
    let transform = query.get(trigger.entity()).unwrap();
});
```
The above code will spawn a new entity representing the observer that
will run it's callback whenever the `Transform` component is added to an
entity. This is a system-like function that supports dependency
injection for all the standard bevy types: `Query`, `Res`, `Commands`
etc. It also has a `Trigger` parameter that provides information about
the trigger such as the target entity, and the event being triggered.
Importantly these systems run during command application which is key
for their future use to keep ECS internals up to date. There are similar
events for `OnInsert` and `OnRemove`, and this will be expanded with
things such as `ArchetypeCreated`, `TableEmpty` etc. in follow up PRs.

Another way to register an observer is an "entity observer" that looks
like this:
```rust
world.entity_mut(entity).observe(|trigger: Trigger<Resize>| {
    // ...
});
```
Entity observers run whenever an event of their type is triggered
targeting that specific entity. This type of observer will de-spawn
itself if the entity (or entities) it is observing is ever de-spawned so
as to not leave dangling observers.

Entity observers can also be spawned from deferred contexts such as
other observers, systems, or hooks using commands:
```rust
commands.entity(entity).observe(|trigger: Trigger<Resize>| {
    // ...
});
```

Observers are not limited to in built event types, they can be used with
any type that implements `Event` (which has been extended to implement
Component). This means events can also carry data:

```rust
#[derive(Event)]
struct Resize { x: u32, y: u32 }

commands.entity(entity).observe(|trigger: Trigger<Resize>, query: Query<&mut Size>| {
    let event = trigger.event();
    // ...
});

// Will trigger the observer when commands are applied.
commands.trigger_targets(Resize { x: 10, y: 10 }, entity);
```

You can also trigger events that target more than one entity at a time:

```rust
commands.trigger_targets(Resize { x: 10, y: 10 }, [e1, e2]);
```

Additionally, Observers don't _need_ entity targets:

```rust
app.observe(|trigger: Trigger<Quit>| {
})

commands.trigger(Quit);
```

In these cases, `trigger.entity()` will be a placeholder.

Observers are actually just normal entities with an `ObserverState` and
`Observer` component! The `observe()` functions above are just shorthand
for:

```rust
world.spawn(Observer::new(|trigger: Trigger<Resize>| {});
```

This will spawn the `Observer` system and use an `on_add` hook to add
the `ObserverState` component.

Dynamic components and trigger types are also fully supported allowing
for runtime defined trigger types.

## Possible Follow-ups
1. Deprecate `RemovedComponents`, observers should fulfill all use cases
while being more flexible and performant.
2. Queries as entities: Swap queries to entities and begin using
observers listening to archetype creation triggers to keep their caches
in sync, this allows unification of `ObserverState` and `QueryState` as
well as unlocking several API improvements for `Query` and the
management of `QueryState`.
3. Trigger bubbling: For some UI use cases in particular users are
likely to want some form of bubbling for entity observers, this is
trivial to implement naively but ideally this includes an acceleration
structure to cache hierarchy traversals.
4. All kinds of other in-built trigger types.
5. Optimization; in order to not bloat the complexity of the PR I have
kept the implementation straightforward, there are several areas where
performance can be improved. The focus for this PR is to get the
behavior implemented and not incur a performance cost for users who
don't use observers.

I am leaving each of these to follow up PR's in order to keep each of
them reviewable as this already includes significant changes.

---------

Co-authored-by: Alice Cecile <alice.i.cecile@gmail.com>
Co-authored-by: MiniaczQ <xnetroidpl@gmail.com>
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
2024-06-15 01:33:26 +00:00
..
bevy_a11y fix: upgrade to winit v0.30 (#13366) 2024-06-03 13:06:48 +00:00
bevy_animation Make the component types of the new animation players clonable. (#13736) 2024-06-07 21:51:24 +00:00
bevy_app Generalised ECS reactivity with Observers (#10839) 2024-06-15 01:33:26 +00:00
bevy_asset Improve error handling for AssetServer::add_async (#13745) 2024-06-10 12:56:21 +00:00
bevy_audio Update to rodio 0.18 (#13612) 2024-06-02 14:25:51 +00:00
bevy_color Wgpu 0.20 (#13186) 2024-06-14 18:39:31 +00:00
bevy_core Determine msrv for every standalone bevy_* crate. (#13211) 2024-05-13 18:26:41 +00:00
bevy_core_pipeline Allow mix of hdr and non-hdr cameras to same render target (#13419) 2024-06-06 20:55:05 +00:00
bevy_derive Deprecate dynamic plugins (#13080) 2024-05-20 20:01:28 +00:00
bevy_dev_tools Move utilities from examples to bevy_state and add concept of state-scoped entities (#13649) 2024-06-04 11:44:34 +00:00
bevy_diagnostic Poll system information in separate tasks (#13693) 2024-06-10 19:06:22 +00:00
bevy_dylib Add README.md to all crates (#13184) 2024-05-02 18:56:00 +00:00
bevy_dynamic_plugin Deprecate dynamic plugins (#13080) 2024-05-20 20:01:28 +00:00
bevy_ecs Generalised ECS reactivity with Observers (#10839) 2024-06-15 01:33:26 +00:00
bevy_encase_derive Add README.md to all crates (#13184) 2024-05-02 18:56:00 +00:00
bevy_gilrs Add README.md to all crates (#13184) 2024-05-02 18:56:00 +00:00
bevy_gizmos view.inverse_clip_from_world should be world_from_clip (#13756) 2024-06-09 14:40:14 +00:00
bevy_gltf Add labels to Gltf Node and Mesh assets (#13558) 2024-06-05 23:10:33 +00:00
bevy_hierarchy Fix EntityCommands::despawn docs (#13774) 2024-06-09 17:59:19 +00:00
bevy_input flush key_input cache when Bevy loses focus (Adopted) (#13678) 2024-06-05 02:06:47 +00:00
bevy_internal Add missing plugins to doc of DefaultPlugins (#13833) 2024-06-14 18:03:04 +00:00
bevy_log Improve tracing layer customization (#13159) 2024-05-12 21:16:56 +00:00
bevy_macro_utils Add README.md to all crates (#13184) 2024-05-02 18:56:00 +00:00
bevy_math Added an illustration to the compass direction docs (issue 13664) (#13788) 2024-06-10 17:31:11 +00:00
bevy_mikktspace Determine msrv for every standalone bevy_* crate. (#13211) 2024-05-13 18:26:41 +00:00
bevy_pbr Revert "Make FOG_ENABLED a shader_def instead of material flag (#13783)" (#13803) 2024-06-10 23:25:16 +00:00
bevy_ptr add Debug for ptr types (#13498) 2024-05-24 21:25:11 +00:00
bevy_reflect improved the error message by insert_boxed (issue #13646) (again) (#13706) 2024-06-07 20:56:16 +00:00
bevy_render Wgpu 0.20 (#13186) 2024-06-14 18:39:31 +00:00
bevy_scene Revert Align Scene::write_to_world_with to match DynamicScene::write_to_world_with (#13800) 2024-06-10 21:14:11 +00:00
bevy_sprite Add from_color to StandardMaterial and ColorMaterial (#13791) 2024-06-10 21:25:31 +00:00
bevy_state Restore overwrite capabilities of insert_state (#13848) 2024-06-14 22:22:59 +00:00
bevy_tasks fix: upgrade to winit v0.30 (#13366) 2024-06-03 13:06:48 +00:00
bevy_text fix non-exact text h-alignment (#13846) 2024-06-14 19:14:42 +00:00
bevy_time Ensure that events are updated even when using a bare-bones Bevy App (#13808) 2024-06-12 14:28:51 +00:00
bevy_transform Fix links to Transform in Transform and GlobalTransform docs (#13704) 2024-06-06 20:06:54 +00:00
bevy_ui reduce the antialias strength (#13814) 2024-06-14 18:36:15 +00:00
bevy_utils Add mappings to EntityMapper (#13727) 2024-06-08 12:52:23 +00:00
bevy_window rename touchpad to gesture, and add new gestures (#13660) 2024-06-04 12:44:25 +00:00
bevy_winit 13743 app exit hang (#13744) 2024-06-08 21:42:01 +00:00