-
-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
event.rs
144 lines (132 loc) · 5.53 KB
/
event.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
//! This example shows how to send, mutate, and receive, events. As well as showing
//! how to you might control system ordering so that events are processed in a specific order.
//! It does this by simulating a damage over time effect that you might find in a game.
use bevy::prelude::*;
// In order to send or receive events first you must define them
// This event should be sent when something attempts to deal damage to another entity.
#[derive(Event, Debug)]
struct DealDamage {
pub amount: i32,
}
// This event should be sent when an entity receives damage.
#[derive(Event, Debug, Default)]
struct DamageReceived;
// This event should be sent when an entity blocks damage with armor.
#[derive(Event, Debug, Default)]
struct ArmorBlockedDamage;
// This resource represents a timer used to determine when to deal damage
// By default it repeats once per second
#[derive(Resource, Deref, DerefMut)]
struct DamageTimer(pub Timer);
impl Default for DamageTimer {
fn default() -> Self {
DamageTimer(Timer::from_seconds(1.0, TimerMode::Repeating))
}
}
// Next we define systems that send, mutate, and receive events
// This system reads 'DamageTimer', updates it, then sends a 'DealDamage' event
// if the timer has finished.
//
// Events are sent using an 'EventWriter<T>' by calling 'send' or 'send_default'.
// The 'send_default' method will send the event with the default value if the event
// has a 'Default' implementation.
fn deal_damage_over_time(
time: Res<Time>,
mut state: ResMut<DamageTimer>,
mut events: EventWriter<DealDamage>,
) {
if state.tick(time.delta()).finished() {
// Events can be sent with 'send' and constructed just like any other object.
events.send(DealDamage { amount: 10 });
}
}
// This system mutates the 'DealDamage' events to apply some armor value
// It also sends an 'ArmorBlockedDamage' event if the value of 'DealDamage' is zero
//
// Events are mutated using an 'EventMutator<T>' by calling 'read'. This returns an iterator
// over all the &mut T that this system has not read yet. Note, you can have multiple
// 'EventReader', 'EventWriter', and 'EventMutator' in a given system, as long as the types (T) are different.
fn apply_armor_to_damage(
mut dmg_events: EventMutator<DealDamage>,
mut armor_events: EventWriter<ArmorBlockedDamage>,
) {
for event in dmg_events.read() {
event.amount -= 1;
if event.amount <= 0 {
// Zero-sized events can also be sent with 'send'
armor_events.send(ArmorBlockedDamage);
}
}
}
// This system reads 'DealDamage' events and sends 'DamageReceived' if the amount is non-zero
//
// Events are read using an 'EventReader<T>' by calling 'read'. This returns an iterator over all the &T
// that this system has not read yet, and must be 'mut' in order to track which events have been read.
// Again, note you can have multiple 'EventReader', 'EventWriter', and 'EventMutator' in a given system,
// as long as the types (T) are different.
fn apply_damage_to_health(
mut dmg_events: EventReader<DealDamage>,
mut rcvd_events: EventWriter<DamageReceived>,
) {
for event in dmg_events.read() {
info!("Applying {} damage", event.amount);
if event.amount > 0 {
// Events with a 'Default' implementation can be sent with 'send_default'
rcvd_events.send_default();
}
}
}
// Finally these two systems read 'DamageReceived' events.
//
// The first system will play a sound.
// The second system will spawn a particle effect.
//
// As before, events are read using an 'EventReader' by calling 'read'. This returns an iterator over all the &T
// that this system has not read yet.
fn play_damage_received_sound(mut dmg_events: EventReader<DamageReceived>) {
for _ in dmg_events.read() {
info!("Playing a sound.");
}
}
// Note that both systems receive the same 'DamageReceived' events. Any number of systems can
// receive the same event type.
fn play_damage_received_particle_effect(mut dmg_events: EventReader<DamageReceived>) {
for _ in dmg_events.read() {
info!("Playing particle effect.");
}
}
fn main() {
App::new()
.add_plugins(DefaultPlugins)
// Events must be added to the app before they can be used
// using the 'add_event' method
.add_event::<DealDamage>()
.add_event::<ArmorBlockedDamage>()
.add_event::<DamageReceived>()
.init_resource::<DamageTimer>()
// As always we must add our systems to the apps schedule.
// Here we add our systems to the schedule using 'chain()' so that they run in order
// This ensures that 'apply_armor_to_damage' runs before 'apply_damage_to_health'
// It also ensures that 'EventWriters' are used before the associated 'EventReaders'
.add_systems(
Update,
(
deal_damage_over_time,
apply_armor_to_damage,
apply_damage_to_health,
)
.chain(),
)
// These two systems are not guaranteed to run in order, nor are they guaranteed to run
// after the above chain. They may even run in parallel with each other.
// This means they may have a one frame delay in processing events compared to the above chain
// In some instances this is fine. In other cases it can be an issue. See the docs for more information
.add_systems(
Update,
(
play_damage_received_sound,
play_damage_received_particle_effect,
),
)
.run();
}