Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_time/src/common_conditions.rs
6598 views
1
use crate::{Real, Time, Timer, TimerMode, Virtual};
2
use bevy_ecs::system::Res;
3
use core::time::Duration;
4
5
/// Run condition that is active on a regular time interval, using [`Time`] to advance
6
/// the timer. The timer ticks at the rate of [`Time::relative_speed`].
7
///
8
/// ```no_run
9
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
10
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
11
/// # use core::time::Duration;
12
/// # use bevy_time::common_conditions::on_timer;
13
/// fn main() {
14
/// App::new()
15
/// .add_plugins(DefaultPlugins)
16
/// .add_systems(
17
/// Update,
18
/// tick.run_if(on_timer(Duration::from_secs(1))),
19
/// )
20
/// .run();
21
/// }
22
/// fn tick() {
23
/// // ran once a second
24
/// }
25
/// ```
26
///
27
/// Note that this does **not** guarantee that systems will run at exactly the
28
/// specified interval. If delta time is larger than the specified `duration` then
29
/// the system will only run once even though the timer may have completed multiple
30
/// times. This condition should only be used with large time durations (relative to
31
/// delta time).
32
///
33
/// For more accurate timers, use the [`Timer`] class directly (see
34
/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or
35
/// use fixed timesteps that allow systems to run multiple times per frame.
36
pub fn on_timer(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
37
let mut timer = Timer::new(duration, TimerMode::Repeating);
38
move |time: Res<Time>| {
39
timer.tick(time.delta());
40
timer.just_finished()
41
}
42
}
43
44
/// Run condition that is active on a regular time interval,
45
/// using [`Time<Real>`] to advance the timer.
46
/// The timer ticks are not scaled.
47
///
48
/// ```no_run
49
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
50
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
51
/// # use core::time::Duration;
52
/// # use bevy_time::common_conditions::on_real_timer;
53
/// fn main() {
54
/// App::new()
55
/// .add_plugins(DefaultPlugins)
56
/// .add_systems(
57
/// Update,
58
/// tick.run_if(on_real_timer(Duration::from_secs(1))),
59
/// )
60
/// .run();
61
/// }
62
/// fn tick() {
63
/// // ran once a second
64
/// }
65
/// ```
66
///
67
/// Note that this does **not** guarantee that systems will run at exactly the
68
/// specified interval. If delta time is larger than the specified `duration` then
69
/// the system will only run once even though the timer may have completed multiple
70
/// times. This condition should only be used with large time durations (relative to
71
/// delta time).
72
///
73
/// For more accurate timers, use the [`Timer`] class directly (see
74
/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or
75
/// use fixed timesteps that allow systems to run multiple times per frame.
76
pub fn on_real_timer(duration: Duration) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {
77
let mut timer = Timer::new(duration, TimerMode::Repeating);
78
move |time: Res<Time<Real>>| {
79
timer.tick(time.delta());
80
timer.just_finished()
81
}
82
}
83
84
/// Run condition that is active *once* after the specified delay,
85
/// using [`Time`] to advance the timer.
86
/// The timer ticks at the rate of [`Time::relative_speed`].
87
///
88
/// ```rust,no_run
89
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
90
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
91
/// # use core::time::Duration;
92
/// # use bevy_time::common_conditions::once_after_delay;
93
/// fn main() {
94
/// App::new()
95
/// .add_plugins(DefaultPlugins)
96
/// .add_systems(
97
/// Update,
98
/// tick.run_if(once_after_delay(Duration::from_secs(1))),
99
/// )
100
/// .run();
101
/// }
102
/// fn tick() {
103
/// // ran once, after a second
104
/// }
105
/// ```
106
pub fn once_after_delay(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
107
let mut timer = Timer::new(duration, TimerMode::Once);
108
move |time: Res<Time>| {
109
timer.tick(time.delta());
110
timer.just_finished()
111
}
112
}
113
114
/// Run condition that is active *once* after the specified delay,
115
/// using [`Time<Real>`] to advance the timer.
116
/// The timer ticks are not scaled.
117
///
118
/// ```rust,no_run
119
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
120
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
121
/// # use core::time::Duration;
122
/// # use bevy_time::common_conditions::once_after_delay;
123
/// fn main() {
124
/// App::new()
125
/// .add_plugins(DefaultPlugins)
126
/// .add_systems(
127
/// Update,
128
/// tick.run_if(once_after_delay(Duration::from_secs(1))),
129
/// )
130
/// .run();
131
/// }
132
/// fn tick() {
133
/// // ran once, after a second
134
/// }
135
/// ```
136
pub fn once_after_real_delay(duration: Duration) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {
137
let mut timer = Timer::new(duration, TimerMode::Once);
138
move |time: Res<Time<Real>>| {
139
timer.tick(time.delta());
140
timer.just_finished()
141
}
142
}
143
144
/// Run condition that is active *indefinitely* after the specified delay,
145
/// using [`Time`] to advance the timer.
146
/// The timer ticks at the rate of [`Time::relative_speed`].
147
///
148
/// ```rust,no_run
149
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
150
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
151
/// # use core::time::Duration;
152
/// # use bevy_time::common_conditions::repeating_after_delay;
153
/// fn main() {
154
/// App::new()
155
/// .add_plugins(DefaultPlugins)
156
/// .add_systems(
157
/// Update,
158
/// tick.run_if(repeating_after_delay(Duration::from_secs(1))),
159
/// )
160
/// .run();
161
/// }
162
/// fn tick() {
163
/// // ran every frame, after a second
164
/// }
165
/// ```
166
pub fn repeating_after_delay(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {
167
let mut timer = Timer::new(duration, TimerMode::Once);
168
move |time: Res<Time>| {
169
timer.tick(time.delta());
170
timer.is_finished()
171
}
172
}
173
174
/// Run condition that is active *indefinitely* after the specified delay,
175
/// using [`Time<Real>`] to advance the timer.
176
/// The timer ticks are not scaled.
177
///
178
/// ```rust,no_run
179
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
180
/// # use bevy_ecs::schedule::IntoScheduleConfigs;
181
/// # use core::time::Duration;
182
/// # use bevy_time::common_conditions::repeating_after_real_delay;
183
/// fn main() {
184
/// App::new()
185
/// .add_plugins(DefaultPlugins)
186
/// .add_systems(
187
/// Update,
188
/// tick.run_if(repeating_after_real_delay(Duration::from_secs(1))),
189
/// )
190
/// .run();
191
/// }
192
/// fn tick() {
193
/// // ran every frame, after a second
194
/// }
195
/// ```
196
pub fn repeating_after_real_delay(
197
duration: Duration,
198
) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {
199
let mut timer = Timer::new(duration, TimerMode::Once);
200
move |time: Res<Time<Real>>| {
201
timer.tick(time.delta());
202
timer.is_finished()
203
}
204
}
205
206
/// Run condition that is active when the [`Time<Virtual>`] clock is paused.
207
/// Use [`bevy_ecs::schedule::common_conditions::not`] to make it active when
208
/// it's not paused.
209
///
210
/// ```rust,no_run
211
/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};
212
/// # use bevy_ecs::schedule::{common_conditions::not, IntoScheduleConfigs};
213
/// # use bevy_time::common_conditions::paused;
214
/// fn main() {
215
/// App::new()
216
/// .add_plugins(DefaultPlugins)
217
/// .add_systems(
218
/// Update,
219
/// (
220
/// is_paused.run_if(paused),
221
/// not_paused.run_if(not(paused)),
222
/// )
223
/// )
224
/// .run();
225
/// }
226
/// fn is_paused() {
227
/// // ran when time is paused
228
/// }
229
///
230
/// fn not_paused() {
231
/// // ran when time is not paused
232
/// }
233
/// ```
234
pub fn paused(time: Res<Time<Virtual>>) -> bool {
235
time.is_paused()
236
}
237
238
#[cfg(test)]
239
mod tests {
240
use super::*;
241
use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule};
242
243
fn test_system() {}
244
245
// Ensure distributive_run_if compiles with the common conditions.
246
#[test]
247
fn distributive_run_if_compiles() {
248
Schedule::default().add_systems(
249
(test_system, test_system)
250
.distributive_run_if(on_timer(Duration::new(1, 0)))
251
.distributive_run_if(paused),
252
);
253
}
254
}
255
256