Skip to content

Commit

Permalink
Add timer-based common run conditions (on_timer and `on_fixed_timer…
Browse files Browse the repository at this point in the history
…`) (bevyengine#7866)

# Objective

Fixes bevyengine#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
  • Loading branch information
jabuwu authored and Shfty committed Mar 19, 2023
1 parent d161152 commit 43f92d3
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 0 deletions.
76 changes: 76 additions & 0 deletions crates/bevy_time/src/common_conditions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
use crate::{fixed_timestep::FixedTime, Time, Timer, TimerMode};
use bevy_ecs::system::Res;
use bevy_utils::Duration;

/// Run condition that is active on a regular time interval, using [`Time`] to advance
/// the timer.
///
/// If used for a fixed timestep system, use [`on_fixed_timer`] instead.
///
/// ```rust,no_run
/// # use bevy_app::{App, IntoSystemAppConfig, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_ecs::schedule::IntoSystemConfig;
/// # use bevy_utils::Duration;
/// # use bevy_time::common_conditions::on_timer;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_system(tick.run_if(on_timer(Duration::from_secs(1))))
/// .run();
/// }
/// fn tick() {
/// // ran once a second
/// }
/// ```
///
/// Note that this does **not** guarantee that systems will run at exactly the
/// specified interval. If delta time is larger than the specified `duration` then
/// the system will only run once even though the timer may have completed multiple
/// times. This condition should only be used with large time durations (relative to
/// delta time).
///
/// For more accurate timers, use the [`Timer`] class directly (see
/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or
/// use fixed timesteps that allow systems to run multiple times per frame.
pub fn on_timer(duration: Duration) -> impl FnMut(Res<Time>) -> bool {
let mut timer = Timer::new(duration, TimerMode::Repeating);
move |time: Res<Time>| {
timer.tick(time.delta());
timer.just_finished()
}
}

/// Run condition that is active on a regular time interval, using [`FixedTime`] to
/// advance the timer.
///
/// If used for a non-fixed timestep system, use [`on_timer`] instead.
///
/// ```rust,no_run
/// # use bevy_app::{App, CoreSchedule, IntoSystemAppConfig, NoopPluginGroup as DefaultPlugins, PluginGroup};
/// # use bevy_ecs::schedule::IntoSystemConfig;
/// # use bevy_utils::Duration;
/// # use bevy_time::common_conditions::on_fixed_timer;
/// fn main() {
/// App::new()
/// .add_plugins(DefaultPlugins)
/// .add_system(
/// tick.in_schedule(CoreSchedule::FixedUpdate)
/// .run_if(on_fixed_timer(Duration::from_secs(1))),
/// )
/// .run();
/// }
/// fn tick() {
/// // ran once a second
/// }
/// ```
///
/// Note that this run condition may not behave as expected if `duration` is smaller
/// than the fixed timestep period, since the timer may complete multiple times in
/// one fixed update.
pub fn on_fixed_timer(duration: Duration) -> impl FnMut(Res<FixedTime>) -> bool {
let mut timer = Timer::new(duration, TimerMode::Repeating);
move |time: Res<FixedTime>| {
timer.tick(time.period);
timer.just_finished()
}
}
2 changes: 2 additions & 0 deletions crates/bevy_time/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/// Common run conditions
pub mod common_conditions;
pub mod fixed_timestep;
mod stopwatch;
#[allow(clippy::module_inception)]
Expand Down

0 comments on commit 43f92d3

Please sign in to comment.