Skip to content

Commit 98cf0d2

Browse files
committed
Add timer-based common run conditions (on_timer and on_fixed_timer) (#7866)
# Objective Fixes #7864 ## Solution Add the run conditions described in the issue. Also needed to add `bevy` as a dev dependency to `bevy_time` so the doctests can run. --- ## Changelog - Add `on_timer` and `on_fixed_timer` run conditions
1 parent 239b070 commit 98cf0d2

File tree

2 files changed

+78
-0
lines changed

2 files changed

+78
-0
lines changed
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
use crate::{fixed_timestep::FixedTime, Time, Timer, TimerMode};
2+
use bevy_ecs::system::Res;
3+
use bevy_utils::Duration;
4+
5+
/// Run condition that is active on a regular time interval, using [`Time`] to advance
6+
/// the timer.
7+
///
8+
/// If used for a fixed timestep system, use [`on_fixed_timer`] instead.
9+
///
10+
/// ```rust,no_run
11+
/// # use bevy_app::{App, IntoSystemAppConfig, NoopPluginGroup as DefaultPlugins, PluginGroup};
12+
/// # use bevy_ecs::schedule::IntoSystemConfig;
13+
/// # use bevy_utils::Duration;
14+
/// # use bevy_time::common_conditions::on_timer;
15+
/// fn main() {
16+
/// App::new()
17+
/// .add_plugins(DefaultPlugins)
18+
/// .add_system(tick.run_if(on_timer(Duration::from_secs(1))))
19+
/// .run();
20+
/// }
21+
/// fn tick() {
22+
/// // ran once a second
23+
/// }
24+
/// ```
25+
///
26+
/// Note that this does **not** guarantee that systems will run at exactly the
27+
/// specified interval. If delta time is larger than the specified `duration` then
28+
/// the system will only run once even though the timer may have completed multiple
29+
/// times. This condition should only be used with large time durations (relative to
30+
/// delta time).
31+
///
32+
/// For more accurate timers, use the [`Timer`] class directly (see
33+
/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or
34+
/// use fixed timesteps that allow systems to run multiple times per frame.
35+
pub fn on_timer(duration: Duration) -> impl FnMut(Res<Time>) -> bool {
36+
let mut timer = Timer::new(duration, TimerMode::Repeating);
37+
move |time: Res<Time>| {
38+
timer.tick(time.delta());
39+
timer.just_finished()
40+
}
41+
}
42+
43+
/// Run condition that is active on a regular time interval, using [`FixedTime`] to
44+
/// advance the timer.
45+
///
46+
/// If used for a non-fixed timestep system, use [`on_timer`] instead.
47+
///
48+
/// ```rust,no_run
49+
/// # use bevy_app::{App, CoreSchedule, IntoSystemAppConfig, NoopPluginGroup as DefaultPlugins, PluginGroup};
50+
/// # use bevy_ecs::schedule::IntoSystemConfig;
51+
/// # use bevy_utils::Duration;
52+
/// # use bevy_time::common_conditions::on_fixed_timer;
53+
/// fn main() {
54+
/// App::new()
55+
/// .add_plugins(DefaultPlugins)
56+
/// .add_system(
57+
/// tick.in_schedule(CoreSchedule::FixedUpdate)
58+
/// .run_if(on_fixed_timer(Duration::from_secs(1))),
59+
/// )
60+
/// .run();
61+
/// }
62+
/// fn tick() {
63+
/// // ran once a second
64+
/// }
65+
/// ```
66+
///
67+
/// Note that this run condition may not behave as expected if `duration` is smaller
68+
/// than the fixed timestep period, since the timer may complete multiple times in
69+
/// one fixed update.
70+
pub fn on_fixed_timer(duration: Duration) -> impl FnMut(Res<FixedTime>) -> bool {
71+
let mut timer = Timer::new(duration, TimerMode::Repeating);
72+
move |time: Res<FixedTime>| {
73+
timer.tick(time.period);
74+
timer.just_finished()
75+
}
76+
}

crates/bevy_time/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
/// Common run conditions
2+
pub mod common_conditions;
13
pub mod fixed_timestep;
24
mod stopwatch;
35
#[allow(clippy::module_inception)]

0 commit comments

Comments
 (0)