Skip to content

Observer EventPatterns#24013

Open
ItsDoot wants to merge 12 commits into
bevyengine:mainfrom
ItsDoot:ecs/nomoreb
Open

Observer EventPatterns#24013
ItsDoot wants to merge 12 commits into
bevyengine:mainfrom
ItsDoot:ecs/nomoreb

Conversation

@ItsDoot
Copy link
Copy Markdown
Contributor

@ItsDoot ItsDoot commented Apr 28, 2026

Objective

On<E, B>'s B type parameter is the single most commonly confusing construct in bevy_ecs. The predominate use case is for filtering lifecycle event observers:

world.add_observer(|on: On<Add, A>| {
    // gets fired whenever component `A` is added to an entity
});

For most other use cases, like picking or custom user events, this type parameter is wholly unnecessary.

Therefore, it should be removed from view for the cases where it is not needed.

Solution

Introduce a tiny layer above Event called EventPattern, and update On's bounds:

pub trait EventPattern: 'static {
    type Event: Event;
    type Components: Bundle;
}

pub struct On<'w, 't, E: EventPattern> {
    observer: Entity,
    event: &'w mut E::Event,
    trigger: &'w mut EventPatternTrigger<'t, E>,
    trigger_context: &'w TriggerContext,
}

This allows us to lift the B type parameter in On<E, B> into the E type parameter. To ensure other events like picking continue to work, we introduce a blanket implementation for all Events:

// All events are implicitly EventPatterns, with no additional components.
impl<E: Event> EventPattern for E {
    type Event = Self;
    type Components = ();
}

To facilitate lifecycle events, we first rename them as follows:

  • Add -> AddEvent
  • Insert -> InsertEvent
  • Discard -> DiscardEvent
  • Remove -> RemoveEvent
  • Despawn -> DespawnEvent

Then, we introduce new generic types in place of those old identifiers that are EventPatterns:

pub struct Add<B: Bundle>(PhantomData<B>);

impl<B: Bundle> EventPattern for Add<B> {
    type Event = AddEvent;
    type Components = B;
}

pub struct Insert<B: Bundle>(PhantomData<B>);

impl<B: Bundle> EventPattern for Insert<B> {
    type Event = InsertEvent;
    type Components = B;
}

pub struct Discard<B: Bundle>(PhantomData<B>);

impl<B: Bundle> EventPattern for Discard<B> {
    type Event = DiscardEvent;
    type Components = B;
}

pub struct Remove<B: Bundle>(PhantomData<B>);

impl<B: Bundle> EventPattern for Remove<B> {
    type Event = RemoveEvent;
    type Components = B;
}

pub struct Despawn<B: Bundle>(PhantomData<B>);

impl<B: Bundle> EventPattern for Despawn<B> {
    type Event = DespawnEvent;
    type Components = B;
}

This results in a slightly modified observer setup:

world.add_observer(|on: On<Add<A>>| {
    // gets fired whenever component `A` is added to an entity
});

Testing

No new tests have been added, we have decent coverage of observer tests as is.

@ItsDoot ItsDoot added A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Apr 28, 2026
@ItsDoot ItsDoot added this to the 0.20 milestone Apr 28, 2026
@alice-i-cecile alice-i-cecile added M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers X-Needs-SME This type of work requires an SME to approve it. and removed X-Blessed Has a large architectural impact or tradeoffs, but the design has been endorsed by decision makers labels Apr 28, 2026
@github-project-automation github-project-automation Bot moved this to Needs SME Triage in ECS Apr 28, 2026
Comment thread _release-content/migration-guides/observer_event_matching.md
Copy link
Copy Markdown
Contributor

@james-j-obrien james-j-obrien left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool approach! Looks good.

Comment thread crates/bevy_ecs/src/lifecycle.rs Outdated
Copy link
Copy Markdown
Contributor

@Diddykonga Diddykonga left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Abstracts the E: Event and B: Bundle from On<E, B> into On<E> where E: EventMatcher
This allows the syntax to be like Add<C>, while the actual semantics use the Event and Components associated types on the EventMatcher. Similiar-ish to an custom WorldQuery.

@ItsDoot ItsDoot requested a review from chescock April 28, 2026 07:32
Copy link
Copy Markdown
Contributor

@chescock chescock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is an elegant way to solve this! I like how a bunch of B generics disappeared from places that don't care about them.

Comment thread crates/bevy_ecs/src/observer/system_param.rs Outdated
Comment thread crates/bevy_ecs/src/lifecycle.rs
Comment thread crates/bevy_ui_widgets/src/observe.rs Outdated
Comment thread crates/bevy_ecs/src/event/mod.rs Outdated
Comment thread _release-content/migration-guides/observer_event_matching.md
Comment thread crates/bevy_ecs/src/event/mod.rs
Comment thread crates/bevy_ecs/src/lifecycle.rs
@alice-i-cecile alice-i-cecile added S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels Apr 29, 2026
@alice-i-cecile
Copy link
Copy Markdown
Member

I'm curious about the answers to @chescock's questions :)

FYI, I'm planning to hold off merging this until after we cut the 0.19-rc, so we can land this together with a F: QueryFilter generic in 0.20.

@ItsDoot ItsDoot added S-Needs-Review Needs reviewer attention (from anyone!) to move forward and removed S-Waiting-on-Author The author needs to make changes or address concerns before this can be merged labels Apr 30, 2026
//! This is particularly useful in combination with immutable components,
//! to avoid any lifecycle-bypassing mutations.
//!
//! ## Lifecycle events and component types
Copy link
Copy Markdown
Contributor

@SpecificProtagonist SpecificProtagonist Apr 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this section needs to be updated slightly?

@ItsDoot ItsDoot changed the title Observer EventMatchers Observer EventPatterns May 1, 2026
@cart cart closed this May 5, 2026
@github-project-automation github-project-automation Bot moved this from Needs SME Triage to Done in ECS May 5, 2026
@cart cart reopened this May 5, 2026
@github-project-automation github-project-automation Bot moved this from Done to Needs SME Triage in ECS May 5, 2026
@alice-i-cecile alice-i-cecile added S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it and removed S-Needs-Review Needs reviewer attention (from anyone!) to move forward labels May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-ECS Entities, components, systems, and events C-Usability A targeted quality-of-life change that makes Bevy easier to use D-Straightforward Simple bug fixes and API improvements, docs, test and examples M-Migration-Guide A breaking change to Bevy's public API that needs to be noted in a migration guide S-Ready-For-Final-Review This PR has been approved by the community. It's ready for a maintainer to consider merging it X-Needs-SME This type of work requires an SME to approve it.

Projects

Status: Needs SME Triage

Development

Successfully merging this pull request may close these issues.

7 participants