# Observer Overhaul Observers are great! But what if they were better? As they grow in prominence, we should fix bugs, write docs and add features to make observers ergonomic and clear for UI. Populated from [Alice's work planning board](https://github.com/orgs/bevyengine/projects/17/views/1). This document is designed for high-level planning. Implementation details and discussion belong in the linked issues. If there isn't one, make one and link it here! ## Goals / requirements - observers should be easy to use, especially for UI - well-documented - type-safe APIs - low on boilerplate - common patterns are easy - performance should be reasonably good - it will never be as fast as queries and batched events, but it shouldn't be pointlessly bad - dynamic event and component types should be supported - at least by the internals! - we should try to future-proof here ## Shiny future Observers are a flexible push-based tool for responding to events, allowing you to extract arbitrary data from the world and combine it with information about the triggered event. They can only be triggered via commands, but are evaluated immediately afterwards, and can trigger other observers. Taken as a whole, this makes them an ideal tool for all manner of infrequent and irregular behavior: everything from UI callbacks to scripting to dialog trees. They exist alongside buffered events: batch, untargeted events designed for predictable processing behavior and high performance. Entity events are well-suited to high-throughput applications that are nearly always active during gameplay and have a well-defined (if perhaps complex) lifecycle: physics collisions, attack and damage rolls, pathfinding, etc... Observers come in two flavors: broadcast observers and entity observers. Broadcast observers watch for any matching `BroadcastEvent` (which do not target specific entities), and are used for responding to infrequent, game-wide events: things like windowing events, the wave advancing in a tower defence, a player levelling up.. Entity observers watch for `EntityEvent`s which target entities. Entity observers generally only watch a defined set of entities, giving them bespoke behavior in response to events, without the need to clutter schedules, constantly poll or define marker components. However, entity observers can also be configured to watch *any* entity, at which point they are termed "universal observers". Universal observers are useful for setting up behavior for an entire classes of objects: controlling widget behavior, defining NPC behavior broadly, keeping data in sync with immutable components... Observers are a form of system, distinguished by their first parameter: `On` (entity observers) and `Receive` (broadcast observers). As such, they can access other system parameters from the `World` freely: resources, queries commands and so on. ### Filtering events The type of events which trigger an observer are given by their first generic, `E`, which represents some collection of events. The event which triggered the observer can be accessed by reference via `On::event()` or `Watch::event()`: events are moved into the `World` when they are sent. Both broadcast and entity events can be composed, with tuples of events causing the observer is triggered if *any* of the matching events occured. When composed in this way, calling `event()` returns a tuple of `Option` types, just like the `AnyOf` `QueryData` type. For entity observers, the set of triggering events is further refined based on which entities are targeted. First, a `F: QueryFilter` generic in `On` is applied (which can be `()`, to apply no filter). Only events which target entities which match the query filter (at the time the observer is evaluated) will cause it to be triggered. This query filter is primarily useful for entity observers which watch *all* entities, rather than specific ones. Second, the target is checked for inclusion against the set of entities watched by the observer. Entity observers which watch a closed set of entities often watch exactly one entity, reflecting truly bespoke behavior (as you might see in a button in a UI or a boss enemy). Information about the `Entity` that an event was targeted at can be retrieved via `On::target()`. If needed, the observer that is currently active can be retrieved via `On::observer()` or ` ::observer()`. Optionally, entity events can propagate, commonly up the standard `ChildOf` hierarchy (bubbling), but custom propagation behavior can be defined. When entity events propagate, they are re-sent to their next target, keeping track of the original target that started the chain, which can be accessed via `On::original_target()`. This propagation continues until the chain reaches a dead-end, or the observer handling the propagation manually stops it. ### Accessing data from targeted entities Accessing data from the entity that an entity-event targeted is very common. To make this easier, the `Targeted<D: QueryData>` `SystemParam` is provided. This system parameter functions like `Single`, querying for and directly providing the requested data. Unlike `Single` however, it does not have a `QueryFilter` generic. The entity that's selected is controlled by the `On` argument: filtering can and should be done by the query filter and/or watching mechanisms provided there. Similarly, the `OriginalTargeted<D: QueryData>` system parameter can be used to do exactly the same thing for the original target of an entity event. Conveniently, both `EntityRef` and `EntityMut` implement `QueryData`, allowing ad-hoc (if conflict-prone) access to any data on targeted entities. While entity events are cancelled if their target has been despawned, the entity requested by `Targeted` can still fail validation, typically due to missing components. This can be handled in the same fashion as `Single`: wrapping the parameter in `When` causes the system (or in this case observer) to be silently skipped. These parameters do not use any special conflict management strategy, such as creating a temporary entity scope by removing the entity from the world. Instead, ### Spawning and configuration Broadcast observers are spawned directly into the world, via `world.spawn(Observer::broadcast(receive_fn))`. For convenience and parity with `add_systems`, `app.add_broadcast_observers((receive_fn_1, receive_fn_2))` is also provided. Entity observers can be spawned in the same fashion, via `Observer::entity_observer(on_fn)`, and then the set of entities it watches can then be configured via `.watch()`, `.unwatch()` and `.watch_all()`. On `App`, the `app.add_universal_observers((on_fn_1, on_fn_2))` method is provided, which spawns observers that watch for any entity that matches the supplied query filters. Entity observers can also be spawned as part of bundles, via bundle effects which create new entities with an `Observing` relation to the entity that generated them. This is helpful for attaching bespoke behavior, and many observers can be attached to a single entity in this way, as tracked by the `ObservedBy` relation target. By default, when all entities watched by an entity observer are despawned (or the relations between them and that observer are broken), that entity observer is cleaned up and is despawned. ### Lifecycle events Lifecycle events (such as `Add<C: Component>` or `Replace<C: Component>`) are shared by hooks and observers. They are emitted by `bevy_ecs` itself, but otherwise behave exactly like any other entity-event. ### Internals Under the hood, observers are fully untyped: observers which respond to dynamic component and event types can be constructed. Similarly, much of the same infrastructure is shared for both broadcast and entity observers. While the typed API is overwhelmingly used, it exists purely for convenience and to reduce errors. The notion of targeting components (and the `B: Bundle` generic) has been removed from the public API: instead, lifecycle events use a generic. ## Completed - [improve the documentation](https://github.com/bevyengine/bevy/issues/14084) - [provide access to the original target of entity-events](https://github.com/bevyengine/bevy/pull/19663) - [split BufferedEvent and EntityEvent](https://github.com/bevyengine/bevy/pull/19647) - [named observers](https://github.com/bevyengine/bevy/pull/19611/) ## Ready For Implementation ### Bugs - [Panic when calling EntityWorldMut::observe several times before calling EntityWorldMut::insert](https://github.com/bevyengine/bevy/issues/18844) - [Observers panic when an earlier observer despawned the entity](https://github.com/bevyengine/bevy/issues/19623) - might be fixed by making this a relationship? ### Docs - [Better docs for propagate(false)](https://github.com/bevyengine/bevy/issues/19648) - [improve the immutable components example](https://github.com/bevyengine/bevy/issues/18584) ### Cleanup - [Type system should prevent the use of exclusive systems as observers](https://github.com/bevyengine/bevy/issues/19080) ### Features - [observers should use relationships](https://github.com/bevyengine/bevy/issues/17607) / [observers in bundles](https://github.com/bevyengine/bevy/issues/14204) ## In progress - don't use `ComponentId` to store events: new-type instead - https://github.com/bevyengine/bevy/pull/19755 ## Needs Design ### Major - should we have a shared Event trait at all? - [the split PR](https://github.com/bevyengine/bevy/pull/19647) explored a number of options - value of the shared trait is unclear - [multi-event observers](https://github.com/bevyengine/bevy/issues/14649) - [Provide easy access to components of targeted entity in observers](https://github.com/bevyengine/bevy/issues/15653) - remove the B: Bundle generic in observers, and make lifecycle events generic - this would allow us to remove component targeting, at least for now - performance and support for dynamic components are the major considerations - [filtered observers](https://github.com/bevyengine/bevy/issues/15287) - should we statically distinguish between global vs entity-targeted events? - leads to `Entity::PLACEHOLDER` or `Option<Entity` [in bad places](https://github.com/bevyengine/bevy/issues/19660) - split apart `On` to avoid optionality - do we want something like [monitors](https://www.flecs.dev/flecs/md_docs_2ObserversManual.html#monitors), whic are fired when entities stop / start matching a query? ### Minor - [Add 'observe_target' method to 'RelatedSpawner' #18975](https://github.com/bevyengine/bevy/pull/18975) - [what should we name the entity methods for observers?](https://github.com/bevyengine/bevy/issues/19263) - three entities of interest: observer entity, current target entity, original target entity - [observers don't respect the stack nature of comands](https://github.com/bevyengine/bevy/issues/19569) + [`Deferred` doesn't seem to work with observers](https://github.com/bevyengine/bevy/issues/14597) - can we just call flush more often? - should we keep emitting buffered events for `Pointer`? - the [original_target PR](https://github.com/bevyengine/bevy/pull/19663) made these a lot less useful - these can be trivially recovered by writing an observer that sends events - it would save perf and reduce complexity - should we provide a helper or example to automatically convert entity-events into buffered events? - observers should be despawned by default when all watched entities are removed - how do we make this acheive this in a configurable way? - give `trigger_targets` a friendlier name ## Needs Investigation - can you currently return Results from observers? - can you currently watch for *any* lifecycle event, regardless of component? Is this useful? ## Out-of-scope - [observer ordering / scheduling](https://github.com/bevyengine/bevy/issues/14890) - probably blocked on systems-as-entities, and extremely contentious and complex - run conditions on observers - for now, disable your observers or check within them :smiling_face_with_tear: - full support for dynamic events - nice to have, and important to design around, but not needed right away - components-as-entities - would make the API less confusing, but too controversial - observing resources - trivial once resources-as-entities is complete - but that's a whole different can of worms - reactive systems - this is closer to how [flecs observers](https://www.flecs.dev/flecs/md_docs_2ObserversManual.html) would work in a Bevy context - very powerful, and probably useful - much more complex and requires substantial cross-engine design effort