//! This example shows how to send, mutate, and receive, events. It also demonstrates1//! how to control system ordering so that events are processed in a specific order.2//! It does this by simulating a damage over time effect that you might find in a game.34use bevy::prelude::*;56// In order to send or receive events first you must define them7// This event should be sent when something attempts to deal damage to another entity.8#[derive(BufferedEvent, Debug)]9struct DealDamage {10pub amount: i32,11}1213// This event should be sent when an entity receives damage.14#[derive(BufferedEvent, Debug, Default)]15struct DamageReceived;1617// This event should be sent when an entity blocks damage with armor.18#[derive(BufferedEvent, Debug, Default)]19struct ArmorBlockedDamage;2021// This resource represents a timer used to determine when to deal damage22// By default it repeats once per second23#[derive(Resource, Deref, DerefMut)]24struct DamageTimer(pub Timer);2526impl Default for DamageTimer {27fn default() -> Self {28DamageTimer(Timer::from_seconds(1.0, TimerMode::Repeating))29}30}3132// Next we define systems that send, mutate, and receive events33// This system reads 'DamageTimer', updates it, then sends a 'DealDamage' event34// if the timer has finished.35//36// Events are sent using an 'EventWriter<T>' by calling 'write' or 'write_default'.37// The 'write_default' method will send the event with the default value if the event38// has a 'Default' implementation.39fn deal_damage_over_time(40time: Res<Time>,41mut state: ResMut<DamageTimer>,42mut events: EventWriter<DealDamage>,43) {44if state.tick(time.delta()).is_finished() {45// Events can be sent with 'write' and constructed just like any other object.46events.write(DealDamage { amount: 10 });47}48}4950// This system mutates the 'DealDamage' events to apply some armor value51// It also sends an 'ArmorBlockedDamage' event if the value of 'DealDamage' is zero52//53// Events are mutated using an 'EventMutator<T>' by calling 'read'. This returns an iterator54// over all the &mut T that this system has not read yet. Note, you can have multiple55// 'EventReader', 'EventWriter', and 'EventMutator' in a given system, as long as the types (T) are different.56fn apply_armor_to_damage(57mut dmg_events: EventMutator<DealDamage>,58mut armor_events: EventWriter<ArmorBlockedDamage>,59) {60for event in dmg_events.read() {61event.amount -= 1;62if event.amount <= 0 {63// Zero-sized events can also be sent with 'send'64armor_events.write(ArmorBlockedDamage);65}66}67}6869// This system reads 'DealDamage' events and sends 'DamageReceived' if the amount is non-zero70//71// Events are read using an 'EventReader<T>' by calling 'read'. This returns an iterator over all the &T72// that this system has not read yet, and must be 'mut' in order to track which events have been read.73// Again, note you can have multiple 'EventReader', 'EventWriter', and 'EventMutator' in a given system,74// as long as the types (T) are different.75fn apply_damage_to_health(76mut dmg_events: EventReader<DealDamage>,77mut rcvd_events: EventWriter<DamageReceived>,78) {79for event in dmg_events.read() {80info!("Applying {} damage", event.amount);81if event.amount > 0 {82// Events with a 'Default' implementation can be written with 'write_default'83rcvd_events.write_default();84}85}86}8788// Finally these two systems read 'DamageReceived' events.89//90// The first system will play a sound.91// The second system will spawn a particle effect.92//93// As before, events are read using an 'EventReader' by calling 'read'. This returns an iterator over all the &T94// that this system has not read yet.95fn play_damage_received_sound(mut dmg_events: EventReader<DamageReceived>) {96for _ in dmg_events.read() {97info!("Playing a sound.");98}99}100101// Note that both systems receive the same 'DamageReceived' events. Any number of systems can102// receive the same event type.103fn play_damage_received_particle_effect(mut dmg_events: EventReader<DamageReceived>) {104for _ in dmg_events.read() {105info!("Playing particle effect.");106}107}108109fn main() {110App::new()111.add_plugins(DefaultPlugins)112// Events must be added to the app before they can be used113// using the 'add_event' method114.add_event::<DealDamage>()115.add_event::<ArmorBlockedDamage>()116.add_event::<DamageReceived>()117.init_resource::<DamageTimer>()118// As always we must add our systems to the apps schedule.119// Here we add our systems to the schedule using 'chain()' so that they run in order120// This ensures that 'apply_armor_to_damage' runs before 'apply_damage_to_health'121// It also ensures that 'EventWriters' are used before the associated 'EventReaders'122.add_systems(123Update,124(125deal_damage_over_time,126apply_armor_to_damage,127apply_damage_to_health,128)129.chain(),130)131// These two systems are not guaranteed to run in order, nor are they guaranteed to run132// after the above chain. They may even run in parallel with each other.133// This means they may have a one frame delay in processing events compared to the above chain134// In some instances this is fine. In other cases it can be an issue. See the docs for more information135.add_systems(136Update,137(138play_damage_received_sound,139play_damage_received_particle_effect,140),141)142.run();143}144145146