Path: blob/main/crates/bevy_time/src/common_conditions.rs
6598 views
use crate::{Real, Time, Timer, TimerMode, Virtual};1use bevy_ecs::system::Res;2use core::time::Duration;34/// Run condition that is active on a regular time interval, using [`Time`] to advance5/// the timer. The timer ticks at the rate of [`Time::relative_speed`].6///7/// ```no_run8/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};9/// # use bevy_ecs::schedule::IntoScheduleConfigs;10/// # use core::time::Duration;11/// # use bevy_time::common_conditions::on_timer;12/// fn main() {13/// App::new()14/// .add_plugins(DefaultPlugins)15/// .add_systems(16/// Update,17/// tick.run_if(on_timer(Duration::from_secs(1))),18/// )19/// .run();20/// }21/// fn tick() {22/// // ran once a second23/// }24/// ```25///26/// Note that this does **not** guarantee that systems will run at exactly the27/// specified interval. If delta time is larger than the specified `duration` then28/// the system will only run once even though the timer may have completed multiple29/// times. This condition should only be used with large time durations (relative to30/// delta time).31///32/// For more accurate timers, use the [`Timer`] class directly (see33/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or34/// use fixed timesteps that allow systems to run multiple times per frame.35pub fn on_timer(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {36let mut timer = Timer::new(duration, TimerMode::Repeating);37move |time: Res<Time>| {38timer.tick(time.delta());39timer.just_finished()40}41}4243/// Run condition that is active on a regular time interval,44/// using [`Time<Real>`] to advance the timer.45/// The timer ticks are not scaled.46///47/// ```no_run48/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};49/// # use bevy_ecs::schedule::IntoScheduleConfigs;50/// # use core::time::Duration;51/// # use bevy_time::common_conditions::on_real_timer;52/// fn main() {53/// App::new()54/// .add_plugins(DefaultPlugins)55/// .add_systems(56/// Update,57/// tick.run_if(on_real_timer(Duration::from_secs(1))),58/// )59/// .run();60/// }61/// fn tick() {62/// // ran once a second63/// }64/// ```65///66/// Note that this does **not** guarantee that systems will run at exactly the67/// specified interval. If delta time is larger than the specified `duration` then68/// the system will only run once even though the timer may have completed multiple69/// times. This condition should only be used with large time durations (relative to70/// delta time).71///72/// For more accurate timers, use the [`Timer`] class directly (see73/// [`Timer::times_finished_this_tick`] to address the problem mentioned above), or74/// use fixed timesteps that allow systems to run multiple times per frame.75pub fn on_real_timer(duration: Duration) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {76let mut timer = Timer::new(duration, TimerMode::Repeating);77move |time: Res<Time<Real>>| {78timer.tick(time.delta());79timer.just_finished()80}81}8283/// Run condition that is active *once* after the specified delay,84/// using [`Time`] to advance the timer.85/// The timer ticks at the rate of [`Time::relative_speed`].86///87/// ```rust,no_run88/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};89/// # use bevy_ecs::schedule::IntoScheduleConfigs;90/// # use core::time::Duration;91/// # use bevy_time::common_conditions::once_after_delay;92/// fn main() {93/// App::new()94/// .add_plugins(DefaultPlugins)95/// .add_systems(96/// Update,97/// tick.run_if(once_after_delay(Duration::from_secs(1))),98/// )99/// .run();100/// }101/// fn tick() {102/// // ran once, after a second103/// }104/// ```105pub fn once_after_delay(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {106let mut timer = Timer::new(duration, TimerMode::Once);107move |time: Res<Time>| {108timer.tick(time.delta());109timer.just_finished()110}111}112113/// Run condition that is active *once* after the specified delay,114/// using [`Time<Real>`] to advance the timer.115/// The timer ticks are not scaled.116///117/// ```rust,no_run118/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};119/// # use bevy_ecs::schedule::IntoScheduleConfigs;120/// # use core::time::Duration;121/// # use bevy_time::common_conditions::once_after_delay;122/// fn main() {123/// App::new()124/// .add_plugins(DefaultPlugins)125/// .add_systems(126/// Update,127/// tick.run_if(once_after_delay(Duration::from_secs(1))),128/// )129/// .run();130/// }131/// fn tick() {132/// // ran once, after a second133/// }134/// ```135pub fn once_after_real_delay(duration: Duration) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {136let mut timer = Timer::new(duration, TimerMode::Once);137move |time: Res<Time<Real>>| {138timer.tick(time.delta());139timer.just_finished()140}141}142143/// Run condition that is active *indefinitely* after the specified delay,144/// using [`Time`] to advance the timer.145/// The timer ticks at the rate of [`Time::relative_speed`].146///147/// ```rust,no_run148/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};149/// # use bevy_ecs::schedule::IntoScheduleConfigs;150/// # use core::time::Duration;151/// # use bevy_time::common_conditions::repeating_after_delay;152/// fn main() {153/// App::new()154/// .add_plugins(DefaultPlugins)155/// .add_systems(156/// Update,157/// tick.run_if(repeating_after_delay(Duration::from_secs(1))),158/// )159/// .run();160/// }161/// fn tick() {162/// // ran every frame, after a second163/// }164/// ```165pub fn repeating_after_delay(duration: Duration) -> impl FnMut(Res<Time>) -> bool + Clone {166let mut timer = Timer::new(duration, TimerMode::Once);167move |time: Res<Time>| {168timer.tick(time.delta());169timer.is_finished()170}171}172173/// Run condition that is active *indefinitely* after the specified delay,174/// using [`Time<Real>`] to advance the timer.175/// The timer ticks are not scaled.176///177/// ```rust,no_run178/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};179/// # use bevy_ecs::schedule::IntoScheduleConfigs;180/// # use core::time::Duration;181/// # use bevy_time::common_conditions::repeating_after_real_delay;182/// fn main() {183/// App::new()184/// .add_plugins(DefaultPlugins)185/// .add_systems(186/// Update,187/// tick.run_if(repeating_after_real_delay(Duration::from_secs(1))),188/// )189/// .run();190/// }191/// fn tick() {192/// // ran every frame, after a second193/// }194/// ```195pub fn repeating_after_real_delay(196duration: Duration,197) -> impl FnMut(Res<Time<Real>>) -> bool + Clone {198let mut timer = Timer::new(duration, TimerMode::Once);199move |time: Res<Time<Real>>| {200timer.tick(time.delta());201timer.is_finished()202}203}204205/// Run condition that is active when the [`Time<Virtual>`] clock is paused.206/// Use [`bevy_ecs::schedule::common_conditions::not`] to make it active when207/// it's not paused.208///209/// ```rust,no_run210/// # use bevy_app::{App, NoopPluginGroup as DefaultPlugins, PluginGroup, Update};211/// # use bevy_ecs::schedule::{common_conditions::not, IntoScheduleConfigs};212/// # use bevy_time::common_conditions::paused;213/// fn main() {214/// App::new()215/// .add_plugins(DefaultPlugins)216/// .add_systems(217/// Update,218/// (219/// is_paused.run_if(paused),220/// not_paused.run_if(not(paused)),221/// )222/// )223/// .run();224/// }225/// fn is_paused() {226/// // ran when time is paused227/// }228///229/// fn not_paused() {230/// // ran when time is not paused231/// }232/// ```233pub fn paused(time: Res<Time<Virtual>>) -> bool {234time.is_paused()235}236237#[cfg(test)]238mod tests {239use super::*;240use bevy_ecs::schedule::{IntoScheduleConfigs, Schedule};241242fn test_system() {}243244// Ensure distributive_run_if compiles with the common conditions.245#[test]246fn distributive_run_if_compiles() {247Schedule::default().add_systems(248(test_system, test_system)249.distributive_run_if(on_timer(Duration::new(1, 0)))250.distributive_run_if(paused),251);252}253}254255256