Merge 77bc7a5ec2
into 877d278785
This commit is contained in:
commit
18abe7763a
@ -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));
|
||||||
|
@ -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,
|
||||||
/// # };
|
/// # };
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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 {}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -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`",
|
||||||
|
@ -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;
|
||||||
|
@ -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::{
|
||||||
|
@ -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
|
||||||
|
@ -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>();
|
||||||
|
@ -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]
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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 {
|
||||||
|
@ -60,7 +60,7 @@ impl Mine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Event)]
|
#[derive(BroadcastEvent)]
|
||||||
struct ExplodeMines {
|
struct ExplodeMines {
|
||||||
pos: Vec2,
|
pos: Vec2,
|
||||||
radius: f32,
|
radius: f32,
|
||||||
|
@ -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
|
||||||
|
@ -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`!
|
||||||
|
Loading…
Reference in New Issue
Block a user