Commands::send_event (#14933)

# Objective

sending events tends to be low-frequency so ergonomics can be
prioritized over efficiency.
add `Commands::send_event` to send any type of event without needing a
writer in hand.

i don't know how we feel about these kind of ergonomic things, i add
this to all my projects and find it useful. adding `mut
this_particular_event_writer: EventWriter<ThisParticularEvent>` every
time i want to send something is unnecessarily cumbersome.
it also simplifies the "send and receive in the same system" pattern
significantly.

basic example before:
```rs
fn my_func(
    q: Query<(Entity, &State)>,
    mut damage_event_writer: EventWriter<DamageEvent>,
    mut heal_event_writer: EventWriter<HealEvent>,
) {
    for (entity, state) in q.iter() {
        if let Some(damage) = state.get_damage() {
            damage_event_writer.send(DamageEvent { entity, damage });
        }

        if let Some(heal) = state.get_heal() {
            heal_event_writer.send(HealEvent { entity, heal });
        }
    }
}
```

basic example after:
```rs
import bevy::ecs::event::SendEventEx;

fn my_func(
    mut commands: Commands,
    q: Query<(Entity, &State)>,
) {
    for (entity, state) in q.iter() {
        if let Some(damage) = state.get_damage() {
            commands.send_event(DamageEvent { entity, damage });
        }

        if let Some(heal) = state.get_heal() {
            commands.send_event(HealEvent { entity, heal });
        }
    }
}
```

send/receive in the same system before:
```rs
fn send_and_receive_param_set(
    mut param_set: ParamSet<(EventReader<DebugEvent>, EventWriter<DebugEvent>)>,
) {
    // We must collect the events to resend, because we can't access the writer while we're iterating over the reader.
    let mut events_to_resend = Vec::new();

    // This is p0, as the first parameter in the `ParamSet` is the reader.
    for event in param_set.p0().read() {
        if event.resend_from_param_set {
            events_to_resend.push(event.clone());
        }
    }

    // This is p1, as the second parameter in the `ParamSet` is the writer.
    for mut event in events_to_resend {
        event.times_sent += 1;
        param_set.p1().send(event);
    }
}
```

after:
```rs
use bevy::ecs::event::SendEventEx;

fn send_via_commands_and_receive(
    mut reader: EventReader<DebugEvent>,
    mut commands: Commands,
) {
    for event in reader.read() {
        if event.resend_via_commands {
            commands.send_event(DebugEvent {
                times_sent: event.times_sent + 1,
                ..event.clone()
            });
        }
    }
}
```

---------

Co-authored-by: Jan Hohenheim <jan@hohenheim.ch>
This commit is contained in:
robtfm 2024-08-28 00:43:40 +01:00 committed by GitHub
parent e63d7c340f
commit 45281e62d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 33 additions and 1 deletions

View file

@ -7,6 +7,7 @@ mod mut_iterators;
mod mutator;
mod reader;
mod registry;
mod send_event;
mod update;
mod writer;
@ -24,6 +25,7 @@ pub use mut_iterators::{EventMutIterator, EventMutIteratorWithId};
pub use mutator::EventMutator;
pub use reader::EventReader;
pub use registry::{EventRegistry, ShouldUpdateEvents};
pub use send_event::SendEvent;
pub use update::{
event_update_condition, event_update_system, signal_event_update_system, EventUpdates,
};

View file

@ -0,0 +1,15 @@
use super::{Event, Events};
use crate::world::{Command, World};
/// A command to send an arbitrary [`Event`], used by [`Commands::send_event`](crate::system::Commands::send_event).
pub struct SendEvent<E: Event> {
/// The event to send.
pub event: E,
}
impl<E: Event> Command for SendEvent<E> {
fn apply(self, world: &mut World) {
let mut events = world.resource_mut::<Events<E>>();
events.send(self.event);
}
}

View file

@ -8,7 +8,7 @@ use crate::{
bundle::{Bundle, InsertMode},
component::{ComponentId, ComponentInfo},
entity::{Entities, Entity},
event::Event,
event::{Event, SendEvent},
observer::{Observer, TriggerEvent, TriggerTargets},
system::{RunSystemWithInput, SystemId},
world::{
@ -788,6 +788,21 @@ impl<'w, 's> Commands<'w, 's> {
) -> EntityCommands {
self.spawn(Observer::new(observer))
}
/// Sends an arbitrary [`Event`].
///
/// This is a convenience method for sending events without requiring an [`EventWriter`].
/// ## Performance
/// Since this is a command, exclusive world access is used, which means that it will not profit from
/// system-level parallelism on supported platforms.
/// If these events are performance-critical or very frequently
/// sent, consider using a typed [`EventWriter`] instead.
///
/// [`EventWriter`]: crate::event::EventWriter
pub fn send_event<E: Event>(&mut self, event: E) -> &mut Self {
self.add(SendEvent { event });
self
}
}
/// A [`Command`] which gets executed for a given [`Entity`].