This commit is contained in:
Tim 2025-07-18 00:01:01 +02:00 committed by GitHub
commit 18abe7763a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
23 changed files with 234 additions and 198 deletions

View File

@ -1,7 +1,7 @@
use core::hint::black_box; use core::hint::black_box;
use bevy_ecs::{ use bevy_ecs::{
event::EntityEvent, event::{BroadcastEvent, EntityEvent},
observer::{On, TriggerTargets}, observer::{On, TriggerTargets},
world::World, world::World,
}; };
@ -16,6 +16,8 @@ fn deterministic_rand() -> ChaCha8Rng {
#[derive(Clone, EntityEvent)] #[derive(Clone, EntityEvent)]
struct EventBase; struct EventBase;
impl BroadcastEvent for EventBase {}
pub fn observe_simple(criterion: &mut Criterion) { pub fn observe_simple(criterion: &mut Criterion) {
let mut group = criterion.benchmark_group("observe"); let mut group = criterion.benchmark_group("observe");
group.warm_up_time(core::time::Duration::from_millis(500)); group.warm_up_time(core::time::Duration::from_millis(500));

View File

@ -1320,7 +1320,7 @@ impl App {
/// # /// #
/// # let mut app = App::new(); /// # let mut app = App::new();
/// # /// #
/// # #[derive(Event)] /// # #[derive(BroadcastEvent)]
/// # struct Party { /// # struct Party {
/// # friends_allowed: bool, /// # friends_allowed: bool,
/// # }; /// # };

View File

@ -306,7 +306,7 @@ Observers are systems that listen for a "trigger" of a specific `Event`:
```rust ```rust
use bevy_ecs::prelude::*; use bevy_ecs::prelude::*;
#[derive(Event)] #[derive(BroadcastEvent)]
struct Speak { struct Speak {
message: String message: String
} }

View File

@ -17,7 +17,7 @@ pub const EVENT: &str = "entity_event";
pub const AUTO_PROPAGATE: &str = "auto_propagate"; pub const AUTO_PROPAGATE: &str = "auto_propagate";
pub const TRAVERSAL: &str = "traversal"; pub const TRAVERSAL: &str = "traversal";
pub fn derive_event(input: TokenStream) -> TokenStream { pub fn derive_broadcast_event(input: TokenStream) -> TokenStream {
let mut ast = parse_macro_input!(input as DeriveInput); let mut ast = parse_macro_input!(input as DeriveInput);
let bevy_ecs_path: Path = crate::bevy_ecs_path(); let bevy_ecs_path: Path = crate::bevy_ecs_path();
@ -31,6 +31,7 @@ pub fn derive_event(input: TokenStream) -> TokenStream {
TokenStream::from(quote! { TokenStream::from(quote! {
impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause {} impl #impl_generics #bevy_ecs_path::event::Event for #struct_name #type_generics #where_clause {}
impl #impl_generics #bevy_ecs_path::event::BroadcastEvent for #struct_name #type_generics #where_clause {}
}) })
} }

View File

@ -547,10 +547,10 @@ pub(crate) fn bevy_ecs_path() -> syn::Path {
BevyManifest::shared().get_path("bevy_ecs") BevyManifest::shared().get_path("bevy_ecs")
} }
/// Implement the `Event` trait. /// Implement the `BroadcastEvent` trait.
#[proc_macro_derive(Event)] #[proc_macro_derive(BroadcastEvent)]
pub fn derive_event(input: TokenStream) -> TokenStream { pub fn derive_broadcast_event(input: TokenStream) -> TokenStream {
component::derive_event(input) component::derive_broadcast_event(input)
} }
/// Cheat sheet for derive syntax, /// Cheat sheet for derive syntax,

View File

@ -23,7 +23,7 @@ use crate::{
bundle::BundleId, bundle::BundleId,
component::{ComponentId, Components, RequiredComponentConstructor, StorageType}, component::{ComponentId, Components, RequiredComponentConstructor, StorageType},
entity::{Entity, EntityLocation}, entity::{Entity, EntityLocation},
event::Event, event::BroadcastEvent,
observer::Observers, observer::Observers,
storage::{ImmutableSparseSet, SparseArray, SparseSet, TableId, TableRow}, storage::{ImmutableSparseSet, SparseArray, SparseSet, TableId, TableRow},
}; };
@ -35,7 +35,7 @@ use core::{
}; };
use nonmax::NonMaxU32; use nonmax::NonMaxU32;
#[derive(Event)] #[derive(BroadcastEvent)]
#[expect(dead_code, reason = "Prepare for the upcoming Query as Entities")] #[expect(dead_code, reason = "Prepare for the upcoming Query as Entities")]
pub(crate) struct ArchetypeCreated(pub ArchetypeId); pub(crate) struct ArchetypeCreated(pub ArchetypeId);

View File

@ -1,4 +1,4 @@
use bevy_ecs_macros::Event; use bevy_ecs_macros::BroadcastEvent;
use bevy_ptr::UnsafeCellDeref; use bevy_ptr::UnsafeCellDeref;
#[cfg(feature = "bevy_reflect")] #[cfg(feature = "bevy_reflect")]
use bevy_reflect::Reflect; use bevy_reflect::Reflect;
@ -86,7 +86,7 @@ impl Tick {
} }
} }
/// An observer [`Event`] that can be used to maintain [`Tick`]s in custom data structures, enabling to make /// A [`BroadcastEvent`] that can be used to maintain [`Tick`]s in custom data structures, enabling to make
/// use of bevy's periodic checks that clamps ticks to a certain range, preventing overflows and thus /// use of bevy's periodic checks that clamps ticks to a certain range, preventing overflows and thus
/// keeping methods like [`Tick::is_newer_than`] reliably return `false` for ticks that got too old. /// keeping methods like [`Tick::is_newer_than`] reliably return `false` for ticks that got too old.
/// ///
@ -111,7 +111,7 @@ impl Tick {
/// schedule.0.check_change_ticks(*check); /// schedule.0.check_change_ticks(*check);
/// }); /// });
/// ``` /// ```
#[derive(Debug, Clone, Copy, Event)] #[derive(Debug, Clone, Copy, BroadcastEvent)]
pub struct CheckChangeTicks(pub(crate) Tick); pub struct CheckChangeTicks(pub(crate) Tick);
impl CheckChangeTicks { impl CheckChangeTicks {

View File

@ -11,87 +11,13 @@ use core::{
marker::PhantomData, marker::PhantomData,
}; };
/// Something that "happens" and can be processed by app logic. /// Supertrait for the observer based [`BroadcastEvent`] and [`EntityEvent`].
///
/// Events can be triggered on a [`World`] using a method like [`trigger`](World::trigger),
/// causing any global [`Observer`] watching that event to run. This allows for push-based
/// event handling where observers are immediately notified of events as they happen.
///
/// Additional event handling behavior can be enabled by implementing the [`EntityEvent`]
/// and [`BufferedEvent`] traits:
///
/// - [`EntityEvent`]s support targeting specific entities, triggering any observers watching those targets.
/// They are useful for entity-specific event handlers and can even be propagated from one entity to another.
/// - [`BufferedEvent`]s support a pull-based event handling system where events are written using an [`EventWriter`]
/// and read later using an [`EventReader`]. This is an alternative to observers that allows efficient batch processing
/// of events at fixed points in a schedule.
/// ///
/// Events must be thread-safe. /// Events must be thread-safe.
///
/// # Usage
///
/// The [`Event`] trait can be derived:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// #[derive(Event)]
/// struct Speak {
/// message: String,
/// }
/// ```
///
/// An [`Observer`] can then be added to listen for this event type:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Event)]
/// # struct Speak {
/// # message: String,
/// # }
/// #
/// # let mut world = World::new();
/// #
/// world.add_observer(|trigger: On<Speak>| {
/// println!("{}", trigger.message);
/// });
/// ```
///
/// The event can be triggered on the [`World`] using the [`trigger`](World::trigger) method:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(Event)]
/// # struct Speak {
/// # message: String,
/// # }
/// #
/// # let mut world = World::new();
/// #
/// # world.add_observer(|trigger: On<Speak>| {
/// # println!("{}", trigger.message);
/// # });
/// #
/// # world.flush();
/// #
/// world.trigger(Speak {
/// message: "Hello!".to_string(),
/// });
/// ```
///
/// For events that additionally need entity targeting or buffering, consider also deriving
/// [`EntityEvent`] or [`BufferedEvent`], respectively.
///
/// [`World`]: crate::world::World
/// [`Observer`]: crate::observer::Observer
/// [`EventReader`]: super::EventReader
/// [`EventWriter`]: super::EventWriter
#[diagnostic::on_unimplemented( #[diagnostic::on_unimplemented(
message = "`{Self}` is not an `Event`", message = "`{Self}` is not an `Event`",
label = "invalid `Event`", label = "invalid `Event`",
note = "consider annotating `{Self}` with `#[derive(Event)]`" note = "consider annotating `{Self}` with `#[derive(BroadcastEvent)]` or `#[derive(EntityEvent)]`"
)] )]
pub trait Event: Send + Sync + 'static { pub trait Event: Send + Sync + 'static {
/// Generates the [`EventKey`] for this event type. /// Generates the [`EventKey`] for this event type.
@ -128,13 +54,68 @@ pub trait Event: Send + Sync + 'static {
} }
} }
/// An [`Event`] without an entity target.
///
/// [`BroadcastEvent`]s can be triggered on a [`World`] with the method [`trigger`](World::trigger),
/// causing any global [`Observer`]s for that event to run.
///
/// # Usage
///
/// The [`BroadcastEvent`] trait can be derived:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// #[derive(BroadcastEvent)]
/// struct Speak {
/// message: String,
/// }
/// ```
///
/// An [`Observer`] can then be added to listen for this event type:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(BroadcastEvent)]
/// # struct Speak {
/// # message: String,
/// # }
/// #
/// # let mut world = World::new();
/// #
/// world.add_observer(|trigger: On<Speak>| {
/// println!("{}", trigger.message);
/// });
/// ```
///
/// The event can be triggered on the [`World`] using the [`trigger`](World::trigger) method:
///
/// ```
/// # use bevy_ecs::prelude::*;
/// #
/// # #[derive(BroadcastEvent)]
/// # struct Speak {
/// # message: String,
/// # }
/// #
/// # let mut world = World::new();
/// #
/// world.trigger(Speak {
/// message: "Hello!".to_string(),
/// });
/// ```
///
/// [`Observer`]: crate::observer::Observer
pub trait BroadcastEvent: Event {}
/// An [`Event`] that can be targeted at specific entities. /// An [`Event`] that can be targeted at specific entities.
/// ///
/// Entity events can be triggered on a [`World`] with specific entity targets using a method /// Entity events can be triggered on a [`World`] with specific entity targets using a method
/// like [`trigger_targets`](World::trigger_targets), causing any [`Observer`] watching the event /// like [`trigger_targets`](World::trigger_targets), causing any [`Observer`] watching the event
/// for those entities to run. /// for those entities to run.
/// ///
/// Unlike basic [`Event`]s, entity events can optionally be propagated from one entity target to another /// Unlike [`BroadcastEvent`]s, entity events can optionally be propagated from one entity target to another
/// based on the [`EntityEvent::Traversal`] type associated with the event. This enables use cases /// based on the [`EntityEvent::Traversal`] type associated with the event. This enables use cases
/// such as bubbling events to parent entities for UI purposes. /// such as bubbling events to parent entities for UI purposes.
/// ///
@ -142,7 +123,7 @@ pub trait Event: Send + Sync + 'static {
/// ///
/// # Usage /// # Usage
/// ///
/// The [`EntityEvent`] trait can be derived. The `event` attribute can be used to further configure /// The [`EntityEvent`] trait can be derived. The `entity_event` attribute can be used to further configure
/// the propagation behavior: adding `auto_propagate` sets [`EntityEvent::AUTO_PROPAGATE`] to `true`, /// the propagation behavior: adding `auto_propagate` sets [`EntityEvent::AUTO_PROPAGATE`] to `true`,
/// while adding `traversal = X` sets [`EntityEvent::Traversal`] to be of type `X`. /// while adding `traversal = X` sets [`EntityEvent::Traversal`] to be of type `X`.
/// ///
@ -233,12 +214,8 @@ pub trait Event: Send + Sync + 'static {
/// world.trigger_targets(Damage { amount: 10.0 }, armor_piece); /// world.trigger_targets(Damage { amount: 10.0 }, armor_piece);
/// ``` /// ```
/// ///
/// [`World`]: crate::world::World
/// [`TriggerTargets`]: crate::observer::TriggerTargets /// [`TriggerTargets`]: crate::observer::TriggerTargets
/// [`Observer`]: crate::observer::Observer /// [`Observer`]: crate::observer::Observer
/// [`Events<E>`]: super::Events
/// [`EventReader`]: super::EventReader
/// [`EventWriter`]: super::EventWriter
#[diagnostic::on_unimplemented( #[diagnostic::on_unimplemented(
message = "`{Self}` is not an `EntityEvent`", message = "`{Self}` is not an `EntityEvent`",
label = "invalid `EntityEvent`", label = "invalid `EntityEvent`",

View File

@ -1,4 +1,20 @@
//! Event handling types. //! Events are things that "happen" and can be processed by app logic.
//!
//! - [`Event`]: A supertrait for push-based events that trigger global observers added via [`add_observer`].
//! - [`BroadcastEvent`]: An event without an entity target. Can be used via [`trigger`].
//! - [`EntityEvent`]: An event targeting specific entities, triggering any observers watching those targets. Can be used via [`trigger_targets`].
//! They can trigger entity-specific observers added via [`observe`] and can be propagated from one entity to another.
//! - [`BufferedEvent`]: Support a pull-based event handling system where events are written using an [`EventWriter`]
//! and read later using an [`EventReader`]. This is an alternative to observers that allows efficient batch processing
//! of events at fixed points in a schedule.
//!
//! [`World`]: crate::world::World
//! [`add_observer`]: crate::world::World::add_observer
//! [`observe`]: crate::world::EntityWorldMut::observe
//! [`trigger`]: crate::world::World::trigger
//! [`trigger_targets`]: crate::world::World::trigger_targets
//! [`Observer`]: crate::observer::Observer
mod base; mod base;
mod collections; mod collections;
mod event_cursor; mod event_cursor;
@ -11,8 +27,8 @@ mod update;
mod writer; mod writer;
pub(crate) use base::EventInstance; pub(crate) use base::EventInstance;
pub use base::{BufferedEvent, EntityEvent, Event, EventId, EventKey}; pub use base::{BroadcastEvent, BufferedEvent, EntityEvent, Event, EventId, EventKey};
pub use bevy_ecs_macros::{BufferedEvent, EntityEvent, Event}; pub use bevy_ecs_macros::{BroadcastEvent, BufferedEvent, EntityEvent};
#[expect(deprecated, reason = "`SendBatchIds` was renamed to `WriteBatchIds`.")] #[expect(deprecated, reason = "`SendBatchIds` was renamed to `WriteBatchIds`.")]
pub use collections::{Events, SendBatchIds, WriteBatchIds}; pub use collections::{Events, SendBatchIds, WriteBatchIds};
pub use event_cursor::EventCursor; pub use event_cursor::EventCursor;

View File

@ -79,8 +79,8 @@ pub mod prelude {
entity::{ContainsEntity, Entity, EntityMapper}, entity::{ContainsEntity, Entity, EntityMapper},
error::{BevyError, Result}, error::{BevyError, Result},
event::{ event::{
BufferedEvent, EntityEvent, Event, EventKey, EventMutator, EventReader, EventWriter, BroadcastEvent, BufferedEvent, EntityEvent, Event, EventKey, EventMutator, EventReader,
Events, EventWriter, Events,
}, },
hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children}, hierarchy::{ChildOf, ChildSpawner, ChildSpawnerCommands, Children},
lifecycle::{ lifecycle::{

View File

@ -44,7 +44,7 @@ use crate::prelude::ReflectComponent;
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # let mut world = World::default(); /// # let mut world = World::default();
/// #[derive(Event)] /// #[derive(BroadcastEvent)]
/// struct Speak { /// struct Speak {
/// message: String, /// message: String,
/// } /// }
@ -67,7 +67,7 @@ use crate::prelude::ReflectComponent;
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # let mut world = World::default(); /// # let mut world = World::default();
/// # #[derive(Event)] /// # #[derive(BroadcastEvent)]
/// # struct Speak; /// # struct Speak;
/// // These are functionally the same: /// // These are functionally the same:
/// world.add_observer(|trigger: On<Speak>| {}); /// world.add_observer(|trigger: On<Speak>| {});
@ -79,7 +79,7 @@ use crate::prelude::ReflectComponent;
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # let mut world = World::default(); /// # let mut world = World::default();
/// # #[derive(Event)] /// # #[derive(BroadcastEvent)]
/// # struct PrintNames; /// # struct PrintNames;
/// # #[derive(Component, Debug)] /// # #[derive(Component, Debug)]
/// # struct Name; /// # struct Name;
@ -97,7 +97,7 @@ use crate::prelude::ReflectComponent;
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # let mut world = World::default(); /// # let mut world = World::default();
/// # #[derive(Event)] /// # #[derive(BroadcastEvent)]
/// # struct SpawnThing; /// # struct SpawnThing;
/// # #[derive(Component, Debug)] /// # #[derive(Component, Debug)]
/// # struct Thing; /// # struct Thing;
@ -111,9 +111,9 @@ use crate::prelude::ReflectComponent;
/// ``` /// ```
/// # use bevy_ecs::prelude::*; /// # use bevy_ecs::prelude::*;
/// # let mut world = World::default(); /// # let mut world = World::default();
/// # #[derive(Event)] /// # #[derive(BroadcastEvent)]
/// # struct A; /// # struct A;
/// # #[derive(Event)] /// # #[derive(BroadcastEvent)]
/// # struct B; /// # struct B;
/// world.add_observer(|trigger: On<A>, mut commands: Commands| { /// world.add_observer(|trigger: On<A>, mut commands: Commands| {
/// commands.trigger(B); /// commands.trigger(B);
@ -211,7 +211,7 @@ pub struct Observer {
} }
impl Observer { impl Observer {
/// Creates a new [`Observer`], which defaults to a "global" observer. This means it will run whenever the event `E` is triggered /// Creates a new [`Observer`], which defaults to a global observer. This means it will run whenever the event `E` is triggered
/// for _any_ entity (or no entity). /// for _any_ entity (or no entity).
/// ///
/// # Panics /// # Panics

View File

@ -145,13 +145,14 @@ pub use trigger_targets::*;
use crate::{ use crate::{
change_detection::MaybeLocation, change_detection::MaybeLocation,
component::ComponentId, component::ComponentId,
event::BroadcastEvent,
prelude::*, prelude::*,
system::IntoObserverSystem, system::IntoObserverSystem,
world::{DeferredWorld, *}, world::{DeferredWorld, *},
}; };
impl World { impl World {
/// Spawns a "global" [`Observer`] which will watch for the given event. /// Spawns a global [`Observer`] which will watch for the given event.
/// Returns its [`Entity`] as a [`EntityWorldMut`]. /// Returns its [`Entity`] as a [`EntityWorldMut`].
/// ///
/// `system` can be any system whose first parameter is [`On`]. /// `system` can be any system whose first parameter is [`On`].
@ -186,17 +187,21 @@ impl World {
self.spawn(Observer::new(system)) self.spawn(Observer::new(system))
} }
/// Triggers the given [`Event`], which will run any [`Observer`]s watching for it. /// Triggers the given [`BroadcastEvent`], which will run any [`Observer`]s watching for it.
/// ///
/// While event types commonly implement [`Copy`], /// While event types commonly implement [`Copy`],
/// those that don't will be consumed and will no longer be accessible. /// those that don't will be consumed and will no longer be accessible.
/// If you need to use the event after triggering it, use [`World::trigger_ref`] instead. /// If you need to use the event after triggering it, use [`World::trigger_ref`] instead.
#[track_caller] #[track_caller]
pub fn trigger<E: Event>(&mut self, event: E) { pub fn trigger<E: BroadcastEvent>(&mut self, event: E) {
self.trigger_with_caller(event, MaybeLocation::caller()); self.trigger_with_caller(event, MaybeLocation::caller());
} }
pub(crate) fn trigger_with_caller<E: Event>(&mut self, mut event: E, caller: MaybeLocation) { pub(crate) fn trigger_with_caller<E: BroadcastEvent>(
&mut self,
mut event: E,
caller: MaybeLocation,
) {
let event_key = E::register_event_key(self); let event_key = E::register_event_key(self);
// SAFETY: We just registered `event_key` with the type of `event` // SAFETY: We just registered `event_key` with the type of `event`
unsafe { unsafe {
@ -204,18 +209,18 @@ impl World {
} }
} }
/// Triggers the given [`Event`] as a mutable reference, which will run any [`Observer`]s watching for it. /// Triggers the given [`BroadcastEvent`] as a mutable reference, which will run any [`Observer`]s watching for it.
/// ///
/// Compared to [`World::trigger`], this method is most useful when it's necessary to check /// Compared to [`World::trigger`], this method is most useful when it's necessary to check
/// or use the event after it has been modified by observers. /// or use the event after it has been modified by observers.
#[track_caller] #[track_caller]
pub fn trigger_ref<E: Event>(&mut self, event: &mut E) { pub fn trigger_ref<E: BroadcastEvent>(&mut self, event: &mut E) {
let event_key = E::register_event_key(self); let event_key = E::register_event_key(self);
// SAFETY: We just registered `event_key` with the type of `event` // SAFETY: We just registered `event_key` with the type of `event`
unsafe { self.trigger_dynamic_ref_with_caller(event_key, event, MaybeLocation::caller()) }; unsafe { self.trigger_dynamic_ref_with_caller(event_key, event, MaybeLocation::caller()) };
} }
unsafe fn trigger_dynamic_ref_with_caller<E: Event>( unsafe fn trigger_dynamic_ref_with_caller<E: BroadcastEvent>(
&mut self, &mut self,
event_key: EventKey, event_key: EventKey,
event_data: &mut E, event_data: &mut E,
@ -497,6 +502,7 @@ impl World {
mod tests { mod tests {
use alloc::{vec, vec::Vec}; use alloc::{vec, vec::Vec};
use bevy_ecs_macros::BroadcastEvent;
use bevy_platform::collections::HashMap; use bevy_platform::collections::HashMap;
use bevy_ptr::OwningPtr; use bevy_ptr::OwningPtr;
@ -524,8 +530,21 @@ mod tests {
#[derive(EntityEvent)] #[derive(EntityEvent)]
struct EventA; struct EventA;
impl BroadcastEvent for EventA {}
#[derive(EntityEvent)] #[derive(EntityEvent)]
struct EventWithData { struct EntityEventA;
#[derive(BroadcastEvent)]
struct BroadcastEventA;
#[derive(EntityEvent)]
struct EntityEventWithData {
counter: usize,
}
#[derive(BroadcastEvent)]
struct BroadcastEventWithData {
counter: usize, counter: usize,
} }
@ -681,14 +700,20 @@ mod tests {
fn observer_trigger_ref() { fn observer_trigger_ref() {
let mut world = World::new(); let mut world = World::new();
world.add_observer(|mut trigger: On<EventWithData>| trigger.event_mut().counter += 1); world.add_observer(|mut trigger: On<BroadcastEventWithData>| {
world.add_observer(|mut trigger: On<EventWithData>| trigger.event_mut().counter += 2); trigger.event_mut().counter += 1;
world.add_observer(|mut trigger: On<EventWithData>| trigger.event_mut().counter += 4); });
world.add_observer(|mut trigger: On<BroadcastEventWithData>| {
trigger.event_mut().counter += 2;
});
world.add_observer(|mut trigger: On<BroadcastEventWithData>| {
trigger.event_mut().counter += 4;
});
// This flush is required for the last observer to be called when triggering the event, // This flush is required for the last observer to be called when triggering the event,
// due to `World::add_observer` returning `WorldEntityMut`. // due to `World::add_observer` returning `WorldEntityMut`.
world.flush(); world.flush();
let mut event = EventWithData { counter: 0 }; let mut event = BroadcastEventWithData { counter: 0 };
world.trigger_ref(&mut event); world.trigger_ref(&mut event);
assert_eq!(7, event.counter); assert_eq!(7, event.counter);
} }
@ -697,20 +722,20 @@ mod tests {
fn observer_trigger_targets_ref() { fn observer_trigger_targets_ref() {
let mut world = World::new(); let mut world = World::new();
world.add_observer(|mut trigger: On<EventWithData, A>| { world.add_observer(|mut trigger: On<EntityEventWithData, A>| {
trigger.event_mut().counter += 1; trigger.event_mut().counter += 1;
}); });
world.add_observer(|mut trigger: On<EventWithData, B>| { world.add_observer(|mut trigger: On<EntityEventWithData, B>| {
trigger.event_mut().counter += 2; trigger.event_mut().counter += 2;
}); });
world.add_observer(|mut trigger: On<EventWithData, A>| { world.add_observer(|mut trigger: On<EntityEventWithData, A>| {
trigger.event_mut().counter += 4; trigger.event_mut().counter += 4;
}); });
// This flush is required for the last observer to be called when triggering the event, // This flush is required for the last observer to be called when triggering the event,
// due to `World::add_observer` returning `WorldEntityMut`. // due to `World::add_observer` returning `WorldEntityMut`.
world.flush(); world.flush();
let mut event = EventWithData { counter: 0 }; let mut event = EntityEventWithData { counter: 0 };
let component_a = world.register_component::<A>(); let component_a = world.register_component::<A>();
world.trigger_targets_ref(&mut event, component_a); world.trigger_targets_ref(&mut event, component_a);
assert_eq!(5, event.counter); assert_eq!(5, event.counter);
@ -846,16 +871,16 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.init_resource::<Order>(); world.init_resource::<Order>();
let system: fn(On<EventA>) = |_| { let system: fn(On<EntityEventA>) = |_| {
panic!("Trigger routed to non-targeted entity."); panic!("Trigger routed to non-targeted entity.");
}; };
world.spawn_empty().observe(system); world.spawn_empty().observe(system);
let entity = world let entity = world
.spawn_empty() .spawn_empty()
.observe(|_: On<EventA>, mut res: ResMut<Order>| res.observed("a_1")) .observe(|_: On<EntityEventA>, mut res: ResMut<Order>| res.observed("a_1"))
.id(); .id();
world.add_observer(move |obs: On<EventA>, mut res: ResMut<Order>| { world.add_observer(move |obs: On<EntityEventA>, mut res: ResMut<Order>| {
assert_eq!(obs.target(), entity); assert_eq!(obs.target(), entity);
res.observed("a_2"); res.observed("a_2");
}); });
@ -863,7 +888,7 @@ mod tests {
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
// and therefore does not automatically flush. // and therefore does not automatically flush.
world.flush(); world.flush();
world.trigger_targets(EventA, entity); world.trigger_targets(EntityEventA, entity);
world.flush(); world.flush();
assert_eq!(vec!["a_2", "a_1"], world.resource::<Order>().0); assert_eq!(vec!["a_2", "a_1"], world.resource::<Order>().0);
} }
@ -881,26 +906,27 @@ mod tests {
// targets (entity_1, A) // targets (entity_1, A)
let entity_1 = world let entity_1 = world
.spawn_empty() .spawn_empty()
.observe(|_: On<EventA, A>, mut res: ResMut<R>| res.0 += 1) .observe(|_: On<EntityEventA, A>, mut res: ResMut<R>| res.0 += 1)
.id(); .id();
// targets (entity_2, B) // targets (entity_2, B)
let entity_2 = world let entity_2 = world
.spawn_empty() .spawn_empty()
.observe(|_: On<EventA, B>, mut res: ResMut<R>| res.0 += 10) .observe(|_: On<EntityEventA, B>, mut res: ResMut<R>| res.0 += 10)
.id(); .id();
// targets any entity or component // targets any entity or component
world.add_observer(|_: On<EventA>, mut res: ResMut<R>| res.0 += 100); world.add_observer(|_: On<EntityEventA>, mut res: ResMut<R>| res.0 += 100);
// targets any entity, and components A or B // targets any entity, and components A or B
world.add_observer(|_: On<EventA, (A, B)>, mut res: ResMut<R>| res.0 += 1000); world.add_observer(|_: On<EntityEventA, (A, B)>, mut res: ResMut<R>| res.0 += 1000);
// test all tuples // test all tuples
world.add_observer(|_: On<EventA, (A, B, (A, B))>, mut res: ResMut<R>| res.0 += 10000); world
.add_observer(|_: On<EntityEventA, (A, B, (A, B))>, mut res: ResMut<R>| res.0 += 10000);
world.add_observer( world.add_observer(
|_: On<EventA, (A, B, (A, B), ((A, B), (A, B)))>, mut res: ResMut<R>| { |_: On<EntityEventA, (A, B, (A, B), ((A, B), (A, B)))>, mut res: ResMut<R>| {
res.0 += 100000; res.0 += 100000;
}, },
); );
world.add_observer( world.add_observer(
|_: On<EventA, (A, B, (A, B), (B, A), (A, B, ((A, B), (B, A))))>, |_: On<EntityEventA, (A, B, (A, B), (B, A), (A, B, ((A, B), (B, A))))>,
mut res: ResMut<R>| res.0 += 1000000, mut res: ResMut<R>| res.0 += 1000000,
); );
@ -908,21 +934,21 @@ mod tests {
world.flush(); world.flush();
// trigger for an entity and a component // trigger for an entity and a component
world.trigger_targets(EventA, (entity_1, component_a)); world.trigger_targets(EntityEventA, (entity_1, component_a));
world.flush(); world.flush();
// only observer that doesn't trigger is the one only watching entity_2 // only observer that doesn't trigger is the one only watching entity_2
assert_eq!(1111101, world.resource::<R>().0); assert_eq!(1111101, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0; world.resource_mut::<R>().0 = 0;
// trigger for both entities, but no components: trigger once per entity target // trigger for both entities, but no components: trigger once per entity target
world.trigger_targets(EventA, (entity_1, entity_2)); world.trigger_targets(EntityEventA, (entity_1, entity_2));
world.flush(); world.flush();
// only the observer that doesn't require components triggers - once per entity // only the observer that doesn't require components triggers - once per entity
assert_eq!(200, world.resource::<R>().0); assert_eq!(200, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0; world.resource_mut::<R>().0 = 0;
// trigger for both components, but no entities: trigger once // trigger for both components, but no entities: trigger once
world.trigger_targets(EventA, (component_a, component_b)); world.trigger_targets(EntityEventA, (component_a, component_b));
world.flush(); world.flush();
// all component observers trigger, entities are not observed // all component observers trigger, entities are not observed
assert_eq!(1111100, world.resource::<R>().0); assert_eq!(1111100, world.resource::<R>().0);
@ -930,14 +956,17 @@ mod tests {
// trigger for both entities and both components: trigger once per entity target // trigger for both entities and both components: trigger once per entity target
// we only get 2222211 because a given observer can trigger only once per entity target // we only get 2222211 because a given observer can trigger only once per entity target
world.trigger_targets(EventA, ((component_a, component_b), (entity_1, entity_2))); world.trigger_targets(
EntityEventA,
((component_a, component_b), (entity_1, entity_2)),
);
world.flush(); world.flush();
assert_eq!(2222211, world.resource::<R>().0); assert_eq!(2222211, world.resource::<R>().0);
world.resource_mut::<R>().0 = 0; world.resource_mut::<R>().0 = 0;
// trigger to test complex tuples: (A, B, (A, B)) // trigger to test complex tuples: (A, B, (A, B))
world.trigger_targets( world.trigger_targets(
EventA, EntityEventA,
(component_a, component_b, (component_a, component_b)), (component_a, component_b, (component_a, component_b)),
); );
world.flush(); world.flush();
@ -947,7 +976,7 @@ mod tests {
// trigger to test complex tuples: (A, B, (A, B), ((A, B), (A, B))) // trigger to test complex tuples: (A, B, (A, B), ((A, B), (A, B)))
world.trigger_targets( world.trigger_targets(
EventA, EntityEventA,
( (
component_a, component_a,
component_b, component_b,
@ -962,7 +991,7 @@ mod tests {
// trigger to test the most complex tuple: (A, B, (A, B), (B, A), (A, B, ((A, B), (B, A)))) // trigger to test the most complex tuple: (A, B, (A, B), (B, A), (A, B, ((A, B), (B, A))))
world.trigger_targets( world.trigger_targets(
EventA, EntityEventA,
( (
component_a, component_a,
component_b, component_b,
@ -999,7 +1028,7 @@ mod tests {
}); });
let entity = entity.flush(); let entity = entity.flush();
world.trigger_targets(EventA, entity); world.trigger_targets(EntityEventA, entity);
world.flush(); world.flush();
assert_eq!(vec!["event_a"], world.resource::<Order>().0); assert_eq!(vec!["event_a"], world.resource::<Order>().0);
} }
@ -1021,7 +1050,7 @@ mod tests {
world.commands().queue(move |world: &mut World| { world.commands().queue(move |world: &mut World| {
// SAFETY: we registered `event_a` above and it matches the type of EventA // SAFETY: we registered `event_a` above and it matches the type of EventA
unsafe { world.trigger_targets_dynamic(event_a, EventA, ()) }; unsafe { world.trigger_targets_dynamic(event_a, EntityEventA, ()) };
}); });
world.flush(); world.flush();
assert_eq!(vec!["event_a"], world.resource::<Order>().0); assert_eq!(vec!["event_a"], world.resource::<Order>().0);
@ -1350,10 +1379,12 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
// This fails because `ResA` is not present in the world // This fails because `ResA` is not present in the world
world.add_observer(|_: On<EventA>, _: Res<ResA>, mut commands: Commands| { world.add_observer(
commands.insert_resource(ResB); |_: On<BroadcastEventA>, _: Res<ResA>, mut commands: Commands| {
}); commands.insert_resource(ResB);
world.trigger(EventA); },
);
world.trigger(BroadcastEventA);
} }
#[test] #[test]
@ -1363,14 +1394,14 @@ mod tests {
let mut world = World::new(); let mut world = World::new();
world.add_observer( world.add_observer(
|_: On<EventA>, mut params: ParamSet<(Query<Entity>, Commands)>| { |_: On<BroadcastEventA>, mut params: ParamSet<(Query<Entity>, Commands)>| {
params.p1().insert_resource(ResA); params.p1().insert_resource(ResA);
}, },
); );
// TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut // TODO: ideally this flush is not necessary, but right now observe() returns WorldEntityMut
// and therefore does not automatically flush. // and therefore does not automatically flush.
world.flush(); world.flush();
world.trigger(EventA); world.trigger(BroadcastEventA);
world.flush(); world.flush();
assert!(world.get_resource::<ResA>().is_some()); assert!(world.get_resource::<ResA>().is_some());
@ -1379,7 +1410,7 @@ mod tests {
#[test] #[test]
#[track_caller] #[track_caller]
fn observer_caller_location_event() { fn observer_caller_location_event() {
#[derive(Event)] #[derive(BroadcastEvent)]
struct EventA; struct EventA;
let caller = MaybeLocation::caller(); let caller = MaybeLocation::caller();
@ -1419,7 +1450,7 @@ mod tests {
let b_id = world.register_component::<B>(); let b_id = world.register_component::<B>();
world.add_observer( world.add_observer(
|trigger: On<EventA, (A, B)>, mut counter: ResMut<Counter>| { |trigger: On<EntityEventA, (A, B)>, mut counter: ResMut<Counter>| {
for &component in trigger.components() { for &component in trigger.components() {
*counter.0.entry(component).or_default() += 1; *counter.0.entry(component).or_default() += 1;
} }
@ -1427,11 +1458,11 @@ mod tests {
); );
world.flush(); world.flush();
world.trigger_targets(EventA, [a_id, b_id]); world.trigger_targets(EntityEventA, [a_id, b_id]);
world.trigger_targets(EventA, a_id); world.trigger_targets(EntityEventA, a_id);
world.trigger_targets(EventA, b_id); world.trigger_targets(EntityEventA, b_id);
world.trigger_targets(EventA, [a_id, b_id]); world.trigger_targets(EntityEventA, [a_id, b_id]);
world.trigger_targets(EventA, a_id); world.trigger_targets(EntityEventA, a_id);
world.flush(); world.flush();
let counter = world.resource::<Counter>(); let counter = world.resource::<Counter>();

View File

@ -93,11 +93,11 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
error::{ignore, DefaultErrorHandler}, error::{ignore, DefaultErrorHandler},
event::Event, event::BroadcastEvent,
observer::On, observer::On,
}; };
#[derive(Event)] #[derive(BroadcastEvent)]
struct TriggerEvent; struct TriggerEvent;
#[test] #[test]

View File

@ -106,21 +106,21 @@ impl<'w, E, B: Bundle> On<'w, E, B> {
} }
impl<'w, E: EntityEvent, B: Bundle> On<'w, E, B> { impl<'w, E: EntityEvent, B: Bundle> On<'w, E, B> {
/// Returns the [`Entity`] that was targeted by the `event` that triggered this observer. /// Returns the [`Entity`] that was targeted by the [`EntityEvent`] that triggered this observer.
/// ///
/// Note that if event propagation is enabled, this may not be the same as the original target of the event, /// Note that if event propagation is enabled, this may not be the same as the original target of the event,
/// which can be accessed via [`On::original_target`]. /// which can be accessed via [`On::original_target`].
/// ///
/// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. /// If the event is also a [`BroadcastEvent`] sent with [`trigger`](World::trigger), this will return [`Entity::PLACEHOLDER`].
pub fn target(&self) -> Entity { pub fn target(&self) -> Entity {
self.trigger.current_target.unwrap_or(Entity::PLACEHOLDER) self.trigger.current_target.unwrap_or(Entity::PLACEHOLDER)
} }
/// Returns the original [`Entity`] that the `event` was targeted at when it was first triggered. /// Returns the original [`Entity`] that the [`EntityEvent`] was targeted at when it was first triggered.
/// ///
/// If event propagation is not enabled, this will always return the same value as [`On::target`]. /// If event propagation is not enabled, this will always return the same value as [`On::target`].
/// ///
/// If the event was not targeted at a specific entity, this will return [`Entity::PLACEHOLDER`]. /// If the event is also a [`BroadcastEvent`] sent with [`trigger`](World::trigger), this will return [`Entity::PLACEHOLDER`].
pub fn original_target(&self) -> Entity { pub fn original_target(&self) -> Entity {
self.trigger.original_target.unwrap_or(Entity::PLACEHOLDER) self.trigger.original_target.unwrap_or(Entity::PLACEHOLDER)
} }
@ -185,11 +185,13 @@ pub struct ObserverTrigger {
pub event_key: EventKey, pub event_key: EventKey,
/// The [`ComponentId`]s the trigger targeted. /// The [`ComponentId`]s the trigger targeted.
pub components: SmallVec<[ComponentId; 2]>, pub components: SmallVec<[ComponentId; 2]>,
/// The entity that the entity-event targeted, if any. /// For [`EntityEvent`]s used with `trigger_targets` this is the entity that the event targeted.
/// Can only be `None` for [`BroadcastEvent`]s used with `trigger`.
/// ///
/// Note that if event propagation is enabled, this may not be the same as [`ObserverTrigger::original_target`]. /// Note that if event propagation is enabled, this may not be the same as [`ObserverTrigger::original_target`].
pub current_target: Option<Entity>, pub current_target: Option<Entity>,
/// The entity that the entity-event was originally targeted at, if any. /// For [`EntityEvent`]s used with `trigger_targets` this is the entity that the event was originally targeted at.
/// Can only be `None` for [`BroadcastEvent`]s used with `trigger`.
/// ///
/// If event propagation is enabled, this will be the first entity that the event was targeted at, /// If event propagation is enabled, this will be the first entity that the event was targeted at,
/// even if the event was propagated to other entities. /// even if the event was propagated to other entities.

View File

@ -9,7 +9,7 @@ use crate::{
change_detection::MaybeLocation, change_detection::MaybeLocation,
entity::Entity, entity::Entity,
error::Result, error::Result,
event::{BufferedEvent, EntityEvent, Event, Events}, event::{BroadcastEvent, BufferedEvent, EntityEvent, Events},
observer::TriggerTargets, observer::TriggerTargets,
resource::Resource, resource::Resource,
schedule::ScheduleLabel, schedule::ScheduleLabel,
@ -208,9 +208,9 @@ pub fn run_schedule(label: impl ScheduleLabel) -> impl Command<Result> {
} }
} }
/// A [`Command`] that sends a global [`Event`] without any targets. /// A [`Command`] that sends a [`BroadcastEvent`].
#[track_caller] #[track_caller]
pub fn trigger(event: impl Event) -> impl Command { pub fn trigger(event: impl BroadcastEvent) -> impl Command {
let caller = MaybeLocation::caller(); let caller = MaybeLocation::caller();
move |world: &mut World| { move |world: &mut World| {
world.trigger_with_caller(event, caller); world.trigger_with_caller(event, caller);

View File

@ -20,7 +20,7 @@ use crate::{
component::{Component, ComponentId, Mutable}, component::{Component, ComponentId, Mutable},
entity::{Entities, Entity, EntityClonerBuilder, EntityDoesNotExistError, OptIn, OptOut}, entity::{Entities, Entity, EntityClonerBuilder, EntityDoesNotExistError, OptIn, OptOut},
error::{warn, BevyError, CommandWithEntity, ErrorContext, HandleError}, error::{warn, BevyError, CommandWithEntity, ErrorContext, HandleError},
event::{BufferedEvent, EntityEvent, Event}, event::{BroadcastEvent, BufferedEvent, EntityEvent, Event},
observer::{Observer, TriggerTargets}, observer::{Observer, TriggerTargets},
resource::Resource, resource::Resource,
schedule::ScheduleLabel, schedule::ScheduleLabel,
@ -1083,11 +1083,11 @@ impl<'w, 's> Commands<'w, 's> {
self.queue(command::run_system_cached_with(system, input).handle_error_with(warn)); self.queue(command::run_system_cached_with(system, input).handle_error_with(warn));
} }
/// Sends a global [`Event`] without any targets. /// Sends a [`BroadcastEvent`].
/// ///
/// This will run any [`Observer`] of the given [`Event`] that isn't scoped to specific targets. /// This will run any [`Observer`] of the given [`BroadcastEvent`] that isn't scoped to specific targets.
#[track_caller] #[track_caller]
pub fn trigger(&mut self, event: impl Event) { pub fn trigger(&mut self, event: impl BroadcastEvent) {
self.queue(command::trigger(event)); self.queue(command::trigger(event));
} }

View File

@ -53,13 +53,13 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::{ use crate::{
event::Event, event::BroadcastEvent,
observer::On, observer::On,
system::{In, IntoSystem}, system::{In, IntoSystem},
world::World, world::World,
}; };
#[derive(Event)] #[derive(BroadcastEvent)]
struct TriggerEvent; struct TriggerEvent;
#[test] #[test]

View File

@ -526,6 +526,7 @@ impl<I: SystemInput, O> core::fmt::Debug for RegisteredSystemError<I, O> {
mod tests { mod tests {
use core::cell::Cell; use core::cell::Cell;
use bevy_ecs_macros::BroadcastEvent;
use bevy_utils::default; use bevy_utils::default;
use crate::{prelude::*, system::SystemId}; use crate::{prelude::*, system::SystemId};
@ -898,7 +899,7 @@ mod tests {
#[test] #[test]
fn system_with_input_mut() { fn system_with_input_mut() {
#[derive(Event)] #[derive(BroadcastEvent)]
struct MyEvent { struct MyEvent {
cancelled: bool, cancelled: bool,
} }

View File

@ -7,7 +7,7 @@ use crate::{
change_detection::{MaybeLocation, MutUntyped}, change_detection::{MaybeLocation, MutUntyped},
component::{ComponentId, Mutable}, component::{ComponentId, Mutable},
entity::Entity, entity::Entity,
event::{BufferedEvent, EntityEvent, Event, EventId, EventKey, Events, WriteBatchIds}, event::{BroadcastEvent, BufferedEvent, EntityEvent, EventId, EventKey, Events, WriteBatchIds},
lifecycle::{HookContext, INSERT, REPLACE}, lifecycle::{HookContext, INSERT, REPLACE},
observer::{Observers, TriggerTargets}, observer::{Observers, TriggerTargets},
prelude::{Component, QueryState}, prelude::{Component, QueryState},
@ -860,12 +860,12 @@ impl<'w> DeferredWorld<'w> {
} }
} }
/// Sends a global [`Event`] without any targets. /// Sends a [`BroadcastEvent`].
/// ///
/// This will run any [`Observer`] of the given [`Event`] that isn't scoped to specific targets. /// This will run any [`Observer`] of the given [`BroadcastEvent`] that isn't scoped to specific targets.
/// ///
/// [`Observer`]: crate::observer::Observer /// [`Observer`]: crate::observer::Observer
pub fn trigger(&mut self, trigger: impl Event) { pub fn trigger(&mut self, trigger: impl BroadcastEvent) {
self.commands().trigger(trigger); self.commands().trigger(trigger);
} }

View File

@ -2,7 +2,7 @@ use crate::{DynamicScene, Scene};
use bevy_asset::{AssetEvent, AssetId, Assets, Handle}; use bevy_asset::{AssetEvent, AssetId, Assets, Handle};
use bevy_ecs::{ use bevy_ecs::{
entity::{Entity, EntityHashMap}, entity::{Entity, EntityHashMap},
event::{EntityEvent, EventCursor, Events}, event::{BroadcastEvent, EntityEvent, EventCursor, Events},
hierarchy::ChildOf, hierarchy::ChildOf,
reflect::AppTypeRegistry, reflect::AppTypeRegistry,
resource::Resource, resource::Resource,
@ -22,11 +22,13 @@ use bevy_ecs::{
system::{Commands, Query}, system::{Commands, Query},
}; };
/// Triggered on a scene's parent entity when [`crate::SceneInstance`] becomes ready to use. /// This [`Event`] is triggered when the [`SceneInstance`] becomes ready to use.
/// If the scene has a parent the event will be triggered on that entity, otherwise the event has no target.
/// ///
/// See also [`On`], [`SceneSpawner::instance_is_ready`]. /// See also [`On`], [`SceneSpawner::instance_is_ready`].
/// ///
/// [`On`]: bevy_ecs::observer::On /// [`On`]: bevy_ecs::observer::On
/// [`Event`]: bevy_ecs::event::Event
#[derive(Clone, Copy, Debug, Eq, PartialEq, EntityEvent, Reflect)] #[derive(Clone, Copy, Debug, Eq, PartialEq, EntityEvent, Reflect)]
#[reflect(Debug, PartialEq, Clone)] #[reflect(Debug, PartialEq, Clone)]
pub struct SceneInstanceReady { pub struct SceneInstanceReady {
@ -34,6 +36,8 @@ pub struct SceneInstanceReady {
pub instance_id: InstanceId, pub instance_id: InstanceId,
} }
impl BroadcastEvent for SceneInstanceReady {}
/// Information about a scene instance. /// Information about a scene instance.
#[derive(Debug)] #[derive(Debug)]
pub struct InstanceInfo { pub struct InstanceInfo {

View File

@ -60,7 +60,7 @@ impl Mine {
} }
} }
#[derive(Event)] #[derive(BroadcastEvent)]
struct ExplodeMines { struct ExplodeMines {
pos: Vec2, pos: Vec2,
radius: f32, radius: f32,

View File

@ -8,13 +8,13 @@ use bevy::{
use std::fmt::Debug; use std::fmt::Debug;
/// event opening a new context menu at position `pos` /// event opening a new context menu at position `pos`
#[derive(Event)] #[derive(BroadcastEvent)]
struct OpenContextMenu { struct OpenContextMenu {
pos: Vec2, pos: Vec2,
} }
/// event will be sent to close currently open context menus /// event will be sent to close currently open context menus
#[derive(Event)] #[derive(BroadcastEvent)]
struct CloseContextMenus; struct CloseContextMenus;
/// marker component identifying root of a context menu /// marker component identifying root of a context menu

View File

@ -1,7 +1,7 @@
--- ---
title: Event Split title: Event Split
authors: ["@Jondolf"] authors: ["@Jondolf", "@tim-blackbird"]
pull_requests: [19647, 20101] pull_requests: [19647, 20101, 20104, 20151]
--- ---
In past releases, all event types were defined by simply deriving the `Event` trait: In past releases, all event types were defined by simply deriving the `Event` trait:
@ -23,21 +23,24 @@ The first two are observer APIs, while the third is a fully separate "buffered"
All three patterns are fundamentally different in both the interface and usage. Despite the same event type being used everywhere, All three patterns are fundamentally different in both the interface and usage. Despite the same event type being used everywhere,
APIs are typically built to support only one of them. APIs are typically built to support only one of them.
This has led to a lot of confusion and frustration for users. A common footgun was using a "buffered event" with an observer, This has led to a lot of confusion and frustration for users. Common footguns include:
or an observer event with `EventReader`, leaving the user wondering why the event is not being detected.
**Bevy 0.17** aims to solve this ambiguity by splitting the event traits into `Event`, `EntityEvent`, and `BufferedEvent`. - Using a "buffered event" with an observer, or an observer event with `EventReader`, leaving the user wondering why the event is not being detected.
- `On`(formerly `Trigger`) has a `target` getter which would cause confusion for events only meant to be used with `trigger` where it returns `Entity::PLACEHOLDER`.
- `Event`: A shared trait for observer events. **Bevy 0.17** aims to solve this ambiguity by splitting the different kinds of events into multiple traits:
- `EntityEvent`: An `Event` that additionally supports targeting specific entities and propagating the event from one entity to another.
- `BufferedEvent`: An event that supports usage with `EventReader` and `EventWriter` for pull-based event handling. - `Event`: A supertrait for observer events.
- `BroadcastEvent`: An observer event without an entity target.
- `EntityEvent`: An observer event that targets specific entities and can propagate the event from one entity to another across relationships.
- `BufferedEvent`: An event used with `EventReader` and `EventWriter` for pull-based event handling.
## Using Events ## Using Events
A basic `Event` can be defined like before, by deriving the `Event` trait. Events without an entity target can be defined, by deriving the `BroadcastEvent` trait.
```rust ```rust
#[derive(Event)] #[derive(BroadcastEvent)]
struct Speak { struct Speak {
message: String, message: String,
} }
@ -57,8 +60,8 @@ commands.trigger(Speak {
}); });
``` ```
To allow an event to be targeted at entities and even propagated further, you can instead derive `EntityEvent`. To make an event target entities and even be propagated further, you can instead derive `EntityEvent`.
It supports optionally specifying some options for propagation using the `event` attribute: It supports optionally specifying some options for propagation using the `entity_event` attribute:
```rust ```rust
// When the `Damage` event is triggered on an entity, bubble the event up to ancestors. // When the `Damage` event is triggered on an entity, bubble the event up to ancestors.
@ -69,8 +72,7 @@ struct Damage {
} }
``` ```
Every `EntityEvent` is also an `Event`, so you can still use `trigger` to trigger them globally. `EntityEvent`s can be used with targeted observer APIs such as `trigger_targets` and `observe`:
However, entity events also support targeted observer APIs such as `trigger_targets` and `observe`:
```rust ```rust
// Spawn an enemy entity. // Spawn an enemy entity.
@ -116,6 +118,6 @@ fn read_messages(mut reader: EventReader<Message>) {
In summary: In summary:
- Need a basic event you can trigger and observe? Derive `Event`! - Need an event you can trigger and observe? Derive `BroadcastEvent`!
- Need the observer event to be targeted at an entity? Derive `EntityEvent`! - Need the observer event to be targeted at an entity? Derive `EntityEvent`!
- Need the event to be buffered and support the `EventReader`/`EventWriter` API? Derive `BufferedEvent`! - Need the event to be buffered and support the `EventReader`/`EventWriter` API? Derive `BufferedEvent`!