Skip to content
Draft
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions crates/bevy_ecs/macros/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,12 +148,12 @@ pub fn derive_entity_event(input: TokenStream) -> TokenStream {
}

impl #impl_generics #bevy_ecs_path::event::EntityEvent for #struct_name #type_generics #where_clause {
fn event_target(&self) -> #bevy_ecs_path::entity::Entity {
self.#entity_field
fn event_target(&self) -> &impl #bevy_ecs_path::entity::ContainsEntity {
&self.#entity_field
}

fn event_target_mut(&mut self) -> &mut #bevy_ecs_path::entity::Entity {
&mut self.#entity_field
fn set_event_target(&mut self, entity: #bevy_ecs_path::entity::Entity) {
self.#entity_field = entity.into();
}
}

Expand Down
80 changes: 76 additions & 4 deletions crates/bevy_ecs/src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ mod trigger;
pub use bevy_ecs_macros::{EntityEvent, Event};
pub use trigger::*;

use crate::entity::ContainsEntity;
use crate::{
component::{Component, ComponentId},
entity::Entity,
Expand Down Expand Up @@ -283,12 +284,13 @@ pub trait Event: Send + Sync + Sized + 'static {
/// [`Observer::watch_entities`]: crate::observer::Observer::watch_entities
pub trait EntityEvent: Event {
/// The [`Entity`] "target" of this [`EntityEvent`]. When triggered, this will run observers that watch for this specific entity.
fn event_target(&self) -> Entity;
/// Returns a mutable reference to the [`Entity`] "target" of this [`EntityEvent`]. When triggered, this will run observers that watch for this specific entity.
fn event_target(&self) -> &impl ContainsEntity;

/// Sets the "target" [`Entity`] of this [`EntityEvent`]. When triggered, this will run observers that watch for this specific entity.
///
/// Note: In general, this should not be mutated from within an [`Observer`](crate::observer::Observer), as this will not "retarget"
/// Note: In general, this should not be used from within an [`Observer`](crate::observer::Observer), as this will not "retarget"
/// the event in any of Bevy's built-in [`Trigger`] implementations.
fn event_target_mut(&mut self) -> &mut Entity;
fn set_event_target(&mut self, entity: Entity);
}

impl World {
Expand Down Expand Up @@ -959,4 +961,74 @@ mod tests {
});
schedule.run(&mut world);
}

#[test]
fn test_derive_entity_event() {
use bevy_ecs::prelude::*;

struct Entitoid(Entity);

impl ContainsEntity for Entitoid {
fn entity(&self) -> Entity {
self.0
}
}

// Lame :(
impl From<Entity> for Entitoid {
fn from(value: Entity) -> Self {
Self(value)
}
}

#[derive(EntityEvent)]
struct A(Entity);

#[derive(EntityEvent)]
struct B {
entity: Entity,
}

#[derive(EntityEvent)]
struct C {
#[event_target]
target: Entity,
}

#[derive(EntityEvent)]
struct D(Entitoid);

#[derive(EntityEvent)]
struct E {
entity: Entitoid,
}

#[derive(EntityEvent)]
struct F {
#[event_target]
target: Entitoid,
}

let mut world = World::new();
let entity = world.spawn_empty().id();

world.entity_mut(entity).trigger(A);

// Lame :(
world.entity_mut(entity).trigger(|entity| B { entity });
world
.entity_mut(entity)
.trigger(|entity| C { target: entity });
world
.entity_mut(entity)
.trigger(|entity| D(Entitoid(entity)));
world.entity_mut(entity).trigger(|entity| E {
entity: Entitoid(entity),
});
world.entity_mut(entity).trigger(|entity| F {
target: Entitoid(entity),
});

// No asserts; test just needs to compile
}
}
10 changes: 6 additions & 4 deletions crates/bevy_ecs/src/event/trigger.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::entity::ContainsEntity;
use crate::{
component::ComponentId,
entity::Entity,
Expand Down Expand Up @@ -141,7 +142,7 @@ unsafe impl<E: EntityEvent + for<'a> Event<Trigger<'a> = Self>> Trigger<E> for E
trigger_context: &TriggerContext,
event: &mut E,
) {
let entity = event.event_target();
let entity = event.event_target().entity();
// SAFETY:
// - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
// - the passed in event pointer comes from `event`, which is an `Event`
Expand Down Expand Up @@ -278,7 +279,7 @@ unsafe impl<
trigger_context: &TriggerContext,
event: &mut E,
) {
let mut current_entity = event.event_target();
let mut current_entity = event.event_target().entity();
self.original_event_target = current_entity;
// SAFETY:
// - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
Expand Down Expand Up @@ -309,7 +310,8 @@ unsafe impl<
break;
}

*event.event_target_mut() = current_entity;
event.set_event_target(current_entity);

// SAFETY:
// - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
// - the passed in event pointer comes from `event`, which is an `Event`
Expand Down Expand Up @@ -354,7 +356,7 @@ unsafe impl<'a, E: EntityEvent + Event<Trigger<'a> = EntityComponentsTrigger<'a>
trigger_context: &TriggerContext,
event: &mut E,
) {
let entity = event.event_target();
let entity = event.event_target().entity();
// SAFETY:
// - `observers` come from `world` and match the event type `E`, enforced by the call to `trigger`
// - the passed in event pointer comes from `event`, which is an `Event`
Expand Down
10 changes: 5 additions & 5 deletions crates/bevy_ecs/src/observer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ mod tests {
.observe(|_: On<EntityEventA>, mut res: ResMut<Order>| res.observed("a_1"))
.id();
world.add_observer(move |event: On<EntityEventA>, mut res: ResMut<Order>| {
assert_eq!(event.event_target(), entity);
assert_eq!(event.event_target().entity(), entity);
res.observed("a_2");
});

Expand Down Expand Up @@ -719,15 +719,15 @@ mod tests {
move |event: On<EventPropagating>, mut res: ResMut<Order>| {
res.observed("parent");

assert_eq!(event.event_target(), parent);
assert_eq!(event.event_target().entity(), parent);
assert_eq!(event.original_event_target(), child);
},
);

world.entity_mut(child).observe(
move |event: On<EventPropagating>, mut res: ResMut<Order>| {
res.observed("child");
assert_eq!(event.event_target(), child);
assert_eq!(event.event_target().entity(), child);
assert_eq!(event.original_event_target(), child);
},
);
Expand Down Expand Up @@ -937,7 +937,7 @@ mod tests {

world.add_observer(
|event: On<EventPropagating>, query: Query<&A>, mut res: ResMut<Order>| {
if query.get(event.event_target()).is_ok() {
if query.get(event.event_target().entity()).is_ok() {
res.observed("event");
}
},
Expand Down Expand Up @@ -1127,7 +1127,7 @@ mod tests {
let entity = world
.spawn_empty()
.observe(|event: On<EntityEventA>| {
assert_eq!(event.target(), event.event_target());
assert_eq!(event.target(), event.event_target().entity());
})
.id();
world.trigger(EntityEventA(entity));
Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/observer/system_param.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl<'w, 't, E: EntityEvent, B: Bundle> On<'w, 't, E, B> {
note = "Call On::event() to access the event, then read the target entity from the event directly."
)]
pub fn target(&self) -> Entity {
self.event.event_target()
self.event.event_target().entity()
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/bevy_ecs/src/world/entity_ref.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6274,7 +6274,7 @@ mod tests {
.spawn_empty()
.observe(|event: On<TestEvent>, mut commands: Commands| {
commands
.entity(event.event_target())
.entity(event.event_target().entity())
.insert(TestComponent(0));
})
.id();
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_ecs/src/world/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,13 +92,13 @@ mod tests {
struct FollowupEvent(Entity);

fn despawn(kill: On<Kill>, mut commands: Commands) {
commands.entity(kill.event_target()).despawn();
commands.entity(kill.event_target().entity()).despawn();
}

fn followup(kill: On<Kill>, mut commands: Commands) {
// When using a simple .trigger() here, this panics because the entity has already been despawned.
// Instead, we need to use `.queue_handled` or `.queue_silenced` to avoid the panic.
commands.queue_silenced(trigger(FollowupEvent(kill.event_target())));
commands.queue_silenced(trigger(FollowupEvent(kill.event_target().entity())));
}

let mut world = World::new();
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_scene/src/scene_spawner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ mod tests {
component::Component,
hierarchy::Children,
observer::On,
prelude::ReflectComponent,
prelude::*,
query::With,
system::{Commands, Query, Res, ResMut, RunSystemOnce},
};
Expand Down Expand Up @@ -898,7 +898,7 @@ mod tests {
"`SceneInstanceReady` contains the wrong `InstanceId`"
);
assert_eq!(
event.event_target(),
event.event_target().entity(),
scene_entity.unwrap_or(Entity::PLACEHOLDER),
"`SceneInstanceReady` triggered on the wrong parent entity"
);
Expand Down
2 changes: 1 addition & 1 deletion examples/no_std/library/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ fn tick_timers(
}

fn unwrap<B: Bundle>(event: On<Unwrap>, world: &mut World) {
if let Ok(mut target) = world.get_entity_mut(event.event_target())
if let Ok(mut target) = world.get_entity_mut(event.entity)
&& let Some(DelayedComponent(bundle)) = target.take::<DelayedComponent<B>>()
{
target.insert(bundle);
Expand Down
2 changes: 1 addition & 1 deletion examples/picking/mesh_picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ fn update_material_on<E: EntityEvent>(
// versions of this observer, each triggered by a different event and with a different hardcoded
// material. Instead, the event type is a generic, and the material is passed in.
move |event, mut query| {
if let Ok(mut material) = query.get_mut(event.event_target()) {
if let Ok(mut material) = query.get_mut(event.event_target().entity()) {
material.0 = new_material.clone();
}
}
Expand Down
2 changes: 1 addition & 1 deletion examples/picking/sprite_picking.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ fn recolor_on<E: EntityEvent + Debug + Clone + Reflect>(
color: Color,
) -> impl Fn(On<E>, Query<&mut Sprite>) {
move |ev, mut sprites| {
let Ok(mut sprite) = sprites.get_mut(ev.event_target()) else {
let Ok(mut sprite) = sprites.get_mut(ev.event_target().entity()) else {
return;
};
sprite.color = color;
Expand Down
12 changes: 6 additions & 6 deletions examples/ui/ui_drag_and_drop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,37 +58,37 @@ fn setup(mut commands: Commands) {
GlobalZIndex::default()
))
.observe(move |on_over: On<Pointer<Over>>, mut query: Query<(&mut BackgroundColor, &mut BorderColor)>| {
if let Ok((mut background_color, mut border_color)) = query.get_mut(on_over.event_target()) {
if let Ok((mut background_color, mut border_color)) = query.get_mut(on_over.entity) {
background_color.0 = tile_color.lighter(0.1);
border_color.set_all(tile_border_color.lighter(0.1));
}
})
.observe(move |on_out: On<Pointer<Out>>, mut query: Query<(&mut BackgroundColor, &mut BorderColor)>| {
if let Ok((mut background_color, mut border_color)) = query.get_mut(on_out.event_target()) {
if let Ok((mut background_color, mut border_color)) = query.get_mut(on_out.entity) {
background_color.0 = tile_color;
border_color.set_all(tile_border_color);
}
})
.observe(|on_drag_start: On<Pointer<DragStart>>, mut query: Query<(&mut Outline, &mut GlobalZIndex)>| {
if let Ok((mut outline, mut global_zindex, )) = query.get_mut(on_drag_start.event_target()) {
if let Ok((mut outline, mut global_zindex, )) = query.get_mut(on_drag_start.entity) {
outline.color = Color::WHITE;
global_zindex.0 = 1;
}
})
.observe(|on_drag: On<Pointer<Drag>>, mut query: Query<&mut UiTransform>| {
if let Ok(mut transform) = query.get_mut(on_drag.event_target()) {
if let Ok(mut transform) = query.get_mut(on_drag.entity) {
transform.translation = Val2::px(on_drag.distance.x, on_drag.distance.y);
}
})
.observe(move |on_drag_end: On<Pointer<DragEnd>>, mut query: Query<(&mut UiTransform, &mut Outline, &mut GlobalZIndex)>| {
if let Ok((mut transform, mut outline, mut global_zindex)) = query.get_mut(on_drag_end.event_target()) {
if let Ok((mut transform, mut outline, mut global_zindex)) = query.get_mut(on_drag_end.entity) {
transform.translation = Val2::ZERO;
outline.color = Color::NONE;
global_zindex.0 = 0;
}
})
.observe(|on_drag_drop: On<Pointer<DragDrop>>, mut query: Query<&mut Node>| {
if let Ok([mut a, mut b]) = query.get_many_mut([on_drag_drop.event_target(), on_drag_drop.dropped]) {
if let Ok([mut a, mut b]) = query.get_many_mut([on_drag_drop.entity, on_drag_drop.dropped]) {
core::mem::swap(&mut a.grid_row, &mut b.grid_row);
core::mem::swap(&mut a.grid_column, &mut b.grid_column);
}
Expand Down
Loading