use crate::Stopwatch;1#[cfg(feature = "bevy_reflect")]2use bevy_reflect::prelude::*;3use core::time::Duration;45/// Tracks elapsed time. Enters the finished state once `duration` is reached.6///7/// Note that in order to advance the timer [`tick`](Timer::tick) **MUST** be called.8///9/// # Timer modes10///11/// There are two timer modes ([`TimerMode`]):12///13/// - Non repeating timers will stop tracking and stay in the finished state until reset.14/// - Repeating timers will only be in the finished state on each tick `duration` is reached or15/// exceeded, and can still be reset at any given point.16///17/// # Pausing timers18///19/// You can pause a timer using [`Timer::pause`]. Paused timers will not have elapsed time increased.20///21/// # Elapsing multiple times a frame22///23/// Repeating timers might elapse multiple times per frame if the time is advanced by more than the timer duration.24/// You can check how many times a timer elapsed each tick with [`Timer::times_finished_this_tick`].25/// For non-repeating timers, this will always be 0 or 1.26#[derive(Clone, Debug, Default, PartialEq, Eq)]27#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]28#[cfg_attr(29feature = "bevy_reflect",30derive(Reflect),31reflect(Default, Clone, PartialEq)32)]33pub struct Timer {34stopwatch: Stopwatch,35duration: Duration,36mode: TimerMode,37finished: bool,38times_finished_this_tick: u32,39}4041impl Timer {42/// Creates a new timer with a given duration.43///44/// See also [`Timer::from_seconds`](Timer::from_seconds).45pub fn new(duration: Duration, mode: TimerMode) -> Self {46Self {47duration,48mode,49..Default::default()50}51}5253/// Creates a new timer with a given duration in seconds.54///55/// # Example56/// ```57/// # use bevy_time::*;58/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);59/// ```60pub fn from_seconds(duration: f32, mode: TimerMode) -> Self {61Self {62duration: Duration::from_secs_f32(duration),63mode,64..Default::default()65}66}6768/// Returns `true` if the timer has reached its duration.69///70/// For repeating timers, this method behaves identically to [`Timer::just_finished`].71///72/// # Examples73/// ```74/// # use bevy_time::*;75/// use std::time::Duration;76///77/// let mut timer_once = Timer::from_seconds(1.0, TimerMode::Once);78/// timer_once.tick(Duration::from_secs_f32(1.5));79/// assert!(timer_once.is_finished());80/// timer_once.tick(Duration::from_secs_f32(0.5));81/// assert!(timer_once.is_finished());82///83/// let mut timer_repeating = Timer::from_seconds(1.0, TimerMode::Repeating);84/// timer_repeating.tick(Duration::from_secs_f32(1.1));85/// assert!(timer_repeating.is_finished());86/// timer_repeating.tick(Duration::from_secs_f32(0.8));87/// assert!(!timer_repeating.is_finished());88/// timer_repeating.tick(Duration::from_secs_f32(0.6));89/// assert!(timer_repeating.is_finished());90/// ```91#[inline]92pub fn is_finished(&self) -> bool {93self.finished94}9596/// Returns `true` only on the tick the timer reached its duration.97///98/// # Examples99/// ```100/// # use bevy_time::*;101/// use std::time::Duration;102/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);103/// timer.tick(Duration::from_secs_f32(1.5));104/// assert!(timer.just_finished());105/// timer.tick(Duration::from_secs_f32(0.5));106/// assert!(!timer.just_finished());107/// ```108#[inline]109pub fn just_finished(&self) -> bool {110self.times_finished_this_tick > 0111}112113/// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.114/// Will only equal `duration` when the timer is finished and non repeating.115///116/// See also [`Stopwatch::elapsed`](Stopwatch::elapsed).117///118/// # Examples119/// ```120/// # use bevy_time::*;121/// use std::time::Duration;122/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);123/// timer.tick(Duration::from_secs_f32(0.5));124/// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5));125/// ```126#[inline]127pub fn elapsed(&self) -> Duration {128self.stopwatch.elapsed()129}130131/// Returns the time elapsed on the timer as an `f32`.132/// See also [`Timer::elapsed`](Timer::elapsed).133#[inline]134pub fn elapsed_secs(&self) -> f32 {135self.stopwatch.elapsed_secs()136}137138/// Returns the time elapsed on the timer as an `f64`.139/// See also [`Timer::elapsed`](Timer::elapsed).140#[inline]141pub fn elapsed_secs_f64(&self) -> f64 {142self.stopwatch.elapsed_secs_f64()143}144145/// Sets the elapsed time of the timer without any other considerations.146///147/// See also [`Stopwatch::set`](Stopwatch::set).148///149/// #150/// ```151/// # use bevy_time::*;152/// use std::time::Duration;153/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);154/// timer.set_elapsed(Duration::from_secs(2));155/// assert_eq!(timer.elapsed(), Duration::from_secs(2));156/// // the timer is not finished even if the elapsed time is greater than the duration.157/// assert!(!timer.is_finished());158/// ```159#[inline]160pub fn set_elapsed(&mut self, time: Duration) {161self.stopwatch.set_elapsed(time);162}163164/// Returns the duration of the timer.165///166/// # Examples167/// ```168/// # use bevy_time::*;169/// use std::time::Duration;170/// let timer = Timer::new(Duration::from_secs(1), TimerMode::Once);171/// assert_eq!(timer.duration(), Duration::from_secs(1));172/// ```173#[inline]174pub fn duration(&self) -> Duration {175self.duration176}177178/// Sets the duration of the timer.179///180/// # Examples181/// ```182/// # use bevy_time::*;183/// use std::time::Duration;184/// let mut timer = Timer::from_seconds(1.5, TimerMode::Once);185/// timer.set_duration(Duration::from_secs(1));186/// assert_eq!(timer.duration(), Duration::from_secs(1));187/// ```188#[inline]189pub fn set_duration(&mut self, duration: Duration) {190self.duration = duration;191}192193/// Finishes the timer.194///195/// # Examples196/// ```197/// # use bevy_time::*;198/// let mut timer = Timer::from_seconds(1.5, TimerMode::Once);199/// timer.finish();200/// assert!(timer.is_finished());201/// ```202#[inline]203pub fn finish(&mut self) {204let remaining = self.remaining();205self.tick(remaining);206}207208/// Almost finishes the timer leaving 1 ns of remaining time.209/// This can be useful when needing an immediate action without having210/// to wait for the set duration of the timer in the first tick.211///212/// # Examples213/// ```214/// # use bevy_time::*;215/// use std::time::Duration;216/// let mut timer = Timer::from_seconds(1.5, TimerMode::Once);217/// timer.almost_finish();218/// assert!(!timer.is_finished());219/// assert_eq!(timer.remaining(), Duration::from_nanos(1));220/// ```221#[inline]222pub fn almost_finish(&mut self) {223let remaining = self.remaining() - Duration::from_nanos(1);224self.tick(remaining);225}226227/// Returns the mode of the timer.228///229/// # Examples230/// ```231/// # use bevy_time::*;232/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);233/// assert_eq!(timer.mode(), TimerMode::Repeating);234/// ```235#[inline]236pub fn mode(&self) -> TimerMode {237self.mode238}239240/// Sets the mode of the timer.241///242/// # Examples243/// ```244/// # use bevy_time::*;245/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);246/// timer.set_mode(TimerMode::Once);247/// assert_eq!(timer.mode(), TimerMode::Once);248/// ```249#[doc(alias = "repeating")]250#[inline]251pub fn set_mode(&mut self, mode: TimerMode) {252if self.mode != TimerMode::Repeating && mode == TimerMode::Repeating && self.finished {253self.stopwatch.reset();254self.finished = self.just_finished();255}256self.mode = mode;257}258259/// Advance the timer by `delta` seconds.260/// Non repeating timer will clamp at duration.261/// Repeating timer will wrap around.262/// Will not affect paused timers.263///264/// See also [`Stopwatch::tick`](Stopwatch::tick).265///266/// # Examples267/// ```268/// # use bevy_time::*;269/// use std::time::Duration;270/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);271/// let mut repeating = Timer::from_seconds(1.0, TimerMode::Repeating);272/// timer.tick(Duration::from_secs_f32(1.5));273/// repeating.tick(Duration::from_secs_f32(1.5));274/// assert_eq!(timer.elapsed_secs(), 1.0);275/// assert_eq!(repeating.elapsed_secs(), 0.5);276/// ```277pub fn tick(&mut self, delta: Duration) -> &Self {278if self.is_paused() {279self.times_finished_this_tick = 0;280if self.mode == TimerMode::Repeating {281self.finished = false;282}283return self;284}285286if self.mode != TimerMode::Repeating && self.is_finished() {287self.times_finished_this_tick = 0;288return self;289}290291self.stopwatch.tick(delta);292self.finished = self.elapsed() >= self.duration();293294if self.is_finished() {295if self.mode == TimerMode::Repeating {296self.times_finished_this_tick = self297.elapsed()298.as_nanos()299.checked_div(self.duration().as_nanos())300.map_or(u32::MAX, |x| x as u32);301self.set_elapsed(302self.elapsed()303.as_nanos()304.checked_rem(self.duration().as_nanos())305.map_or(Duration::ZERO, |x| Duration::from_nanos(x as u64)),306);307} else {308self.times_finished_this_tick = 1;309self.set_elapsed(self.duration());310}311} else {312self.times_finished_this_tick = 0;313}314315self316}317318/// Pauses the Timer. Disables the ticking of the timer.319///320/// See also [`Stopwatch::pause`](Stopwatch::pause).321///322/// # Examples323/// ```324/// # use bevy_time::*;325/// use std::time::Duration;326/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);327/// timer.pause();328/// timer.tick(Duration::from_secs_f32(0.5));329/// assert_eq!(timer.elapsed_secs(), 0.0);330/// ```331#[inline]332pub fn pause(&mut self) {333self.stopwatch.pause();334}335336/// Unpauses the Timer. Resumes the ticking of the timer.337///338/// See also [`Stopwatch::unpause()`](Stopwatch::unpause).339///340/// # Examples341/// ```342/// # use bevy_time::*;343/// use std::time::Duration;344/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);345/// timer.pause();346/// timer.tick(Duration::from_secs_f32(0.5));347/// timer.unpause();348/// timer.tick(Duration::from_secs_f32(0.5));349/// assert_eq!(timer.elapsed_secs(), 0.5);350/// ```351#[inline]352pub fn unpause(&mut self) {353self.stopwatch.unpause();354}355356/// Returns `true` if the timer is paused.357///358/// See also [`Stopwatch::is_paused`](Stopwatch::is_paused).359///360/// # Examples361/// ```362/// # use bevy_time::*;363/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);364/// assert!(!timer.is_paused());365/// timer.pause();366/// assert!(timer.is_paused());367/// timer.unpause();368/// assert!(!timer.is_paused());369/// ```370#[inline]371pub fn is_paused(&self) -> bool {372self.stopwatch.is_paused()373}374375/// Resets the timer. The reset doesn't affect the `paused` state of the timer.376///377/// See also [`Stopwatch::reset`](Stopwatch::reset).378///379/// Examples380/// ```381/// # use bevy_time::*;382/// use std::time::Duration;383/// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);384/// timer.tick(Duration::from_secs_f32(1.5));385/// timer.reset();386/// assert!(!timer.is_finished());387/// assert!(!timer.just_finished());388/// assert_eq!(timer.elapsed_secs(), 0.0);389/// ```390pub fn reset(&mut self) {391self.stopwatch.reset();392self.finished = false;393self.times_finished_this_tick = 0;394}395396/// Returns the fraction of the timer elapsed time (goes from 0.0 to 1.0).397///398/// # Examples399/// ```400/// # use bevy_time::*;401/// use std::time::Duration;402/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);403/// timer.tick(Duration::from_secs_f32(0.5));404/// assert_eq!(timer.fraction(), 0.25);405/// ```406#[inline]407pub fn fraction(&self) -> f32 {408if self.duration == Duration::ZERO {4091.0410} else {411self.elapsed().as_secs_f32() / self.duration().as_secs_f32()412}413}414415/// Returns the fraction of the timer remaining time (goes from 1.0 to 0.0).416///417/// # Examples418/// ```419/// # use bevy_time::*;420/// use std::time::Duration;421/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);422/// timer.tick(Duration::from_secs_f32(0.5));423/// assert_eq!(timer.fraction_remaining(), 0.75);424/// ```425#[inline]426pub fn fraction_remaining(&self) -> f32 {4271.0 - self.fraction()428}429430/// Returns the remaining time in seconds431///432/// # Examples433/// ```434/// # use bevy_time::*;435/// use std::cmp::Ordering;436/// use std::time::Duration;437/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);438/// timer.tick(Duration::from_secs_f32(0.5));439/// let result = timer.remaining_secs().total_cmp(&1.5);440/// assert_eq!(Ordering::Equal, result);441/// ```442#[inline]443pub fn remaining_secs(&self) -> f32 {444self.remaining().as_secs_f32()445}446447/// Returns the remaining time using Duration448///449/// # Examples450/// ```451/// # use bevy_time::*;452/// use std::time::Duration;453/// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);454/// timer.tick(Duration::from_secs_f32(0.5));455/// assert_eq!(timer.remaining(), Duration::from_secs_f32(1.5));456/// ```457#[inline]458pub fn remaining(&self) -> Duration {459self.duration() - self.elapsed()460}461462/// Returns the number of times a repeating timer463/// finished during the last [`tick`](Timer<T>::tick) call.464///465/// For non repeating-timers, this method will only ever466/// return 0 or 1.467///468/// # Examples469/// ```470/// # use bevy_time::*;471/// use std::time::Duration;472/// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);473/// timer.tick(Duration::from_secs_f32(6.0));474/// assert_eq!(timer.times_finished_this_tick(), 6);475/// timer.tick(Duration::from_secs_f32(2.0));476/// assert_eq!(timer.times_finished_this_tick(), 2);477/// timer.tick(Duration::from_secs_f32(0.5));478/// assert_eq!(timer.times_finished_this_tick(), 0);479/// ```480#[inline]481pub fn times_finished_this_tick(&self) -> u32 {482self.times_finished_this_tick483}484}485486/// Specifies [`Timer`] behavior.487#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]488#[cfg_attr(feature = "serialize", derive(serde::Deserialize, serde::Serialize))]489#[cfg_attr(490feature = "bevy_reflect",491derive(Reflect),492reflect(Default, Clone, PartialEq, Hash)493)]494pub enum TimerMode {495/// Run once and stop.496#[default]497Once,498/// Reset when finished.499Repeating,500}501502#[cfg(test)]503mod tests {504use super::*;505506#[test]507fn non_repeating_timer() {508let mut t = Timer::from_seconds(10.0, TimerMode::Once);509// Tick once, check all attributes510t.tick(Duration::from_secs_f32(0.25));511assert_eq!(t.elapsed_secs(), 0.25);512assert_eq!(t.elapsed_secs_f64(), 0.25);513assert_eq!(t.duration(), Duration::from_secs_f32(10.0));514assert!(!t.is_finished());515assert!(!t.just_finished());516assert_eq!(t.times_finished_this_tick(), 0);517assert_eq!(t.mode(), TimerMode::Once);518assert_eq!(t.fraction(), 0.025);519assert_eq!(t.fraction_remaining(), 0.975);520// Ticking while paused changes nothing521t.pause();522t.tick(Duration::from_secs_f32(500.0));523assert_eq!(t.elapsed_secs(), 0.25);524assert_eq!(t.duration(), Duration::from_secs_f32(10.0));525assert!(!t.is_finished());526assert!(!t.just_finished());527assert_eq!(t.times_finished_this_tick(), 0);528assert_eq!(t.mode(), TimerMode::Once);529assert_eq!(t.fraction(), 0.025);530assert_eq!(t.fraction_remaining(), 0.975);531// Tick past the end and make sure elapsed doesn't go past 0.0 and other things update532t.unpause();533t.tick(Duration::from_secs_f32(500.0));534assert_eq!(t.elapsed_secs(), 10.0);535assert_eq!(t.elapsed_secs_f64(), 10.0);536assert!(t.is_finished());537assert!(t.just_finished());538assert_eq!(t.times_finished_this_tick(), 1);539assert_eq!(t.fraction(), 1.0);540assert_eq!(t.fraction_remaining(), 0.0);541// Continuing to tick when finished should only change just_finished542t.tick(Duration::from_secs_f32(1.0));543assert_eq!(t.elapsed_secs(), 10.0);544assert_eq!(t.elapsed_secs_f64(), 10.0);545assert!(t.is_finished());546assert!(!t.just_finished());547assert_eq!(t.times_finished_this_tick(), 0);548assert_eq!(t.fraction(), 1.0);549assert_eq!(t.fraction_remaining(), 0.0);550}551552#[test]553fn repeating_timer() {554let mut t = Timer::from_seconds(2.0, TimerMode::Repeating);555// Tick once, check all attributes556t.tick(Duration::from_secs_f32(0.75));557assert_eq!(t.elapsed_secs(), 0.75);558assert_eq!(t.elapsed_secs_f64(), 0.75);559assert_eq!(t.duration(), Duration::from_secs_f32(2.0));560assert!(!t.is_finished());561assert!(!t.just_finished());562assert_eq!(t.times_finished_this_tick(), 0);563assert_eq!(t.mode(), TimerMode::Repeating);564assert_eq!(t.fraction(), 0.375);565assert_eq!(t.fraction_remaining(), 0.625);566// Tick past the end and make sure elapsed wraps567t.tick(Duration::from_secs_f32(1.5));568assert_eq!(t.elapsed_secs(), 0.25);569assert_eq!(t.elapsed_secs_f64(), 0.25);570assert!(t.is_finished());571assert!(t.just_finished());572assert_eq!(t.times_finished_this_tick(), 1);573assert_eq!(t.fraction(), 0.125);574assert_eq!(t.fraction_remaining(), 0.875);575// Continuing to tick should turn off both finished & just_finished for repeating timers576t.tick(Duration::from_secs_f32(1.0));577assert_eq!(t.elapsed_secs(), 1.25);578assert_eq!(t.elapsed_secs_f64(), 1.25);579assert!(!t.is_finished());580assert!(!t.just_finished());581assert_eq!(t.times_finished_this_tick(), 0);582assert_eq!(t.fraction(), 0.625);583assert_eq!(t.fraction_remaining(), 0.375);584}585586#[test]587fn times_finished_repeating() {588let mut t = Timer::from_seconds(1.0, TimerMode::Repeating);589assert_eq!(t.times_finished_this_tick(), 0);590t.tick(Duration::from_secs_f32(3.5));591assert_eq!(t.times_finished_this_tick(), 3);592assert_eq!(t.elapsed_secs(), 0.5);593assert_eq!(t.elapsed_secs_f64(), 0.5);594assert!(t.is_finished());595assert!(t.just_finished());596t.tick(Duration::from_secs_f32(0.2));597assert_eq!(t.times_finished_this_tick(), 0);598}599600#[test]601fn times_finished_this_tick() {602let mut t = Timer::from_seconds(1.0, TimerMode::Once);603assert_eq!(t.times_finished_this_tick(), 0);604t.tick(Duration::from_secs_f32(1.5));605assert_eq!(t.times_finished_this_tick(), 1);606t.tick(Duration::from_secs_f32(0.5));607assert_eq!(t.times_finished_this_tick(), 0);608}609610#[test]611fn times_finished_this_tick_repeating_zero_duration() {612let mut t = Timer::from_seconds(0.0, TimerMode::Repeating);613assert_eq!(t.times_finished_this_tick(), 0);614assert_eq!(t.elapsed(), Duration::ZERO);615assert_eq!(t.fraction(), 1.0);616t.tick(Duration::from_secs(1));617assert_eq!(t.times_finished_this_tick(), u32::MAX);618assert_eq!(t.elapsed(), Duration::ZERO);619assert_eq!(t.fraction(), 1.0);620t.tick(Duration::from_secs(2));621assert_eq!(t.times_finished_this_tick(), u32::MAX);622assert_eq!(t.elapsed(), Duration::ZERO);623assert_eq!(t.fraction(), 1.0);624t.reset();625assert_eq!(t.times_finished_this_tick(), 0);626assert_eq!(t.elapsed(), Duration::ZERO);627assert_eq!(t.fraction(), 1.0);628}629630#[test]631fn times_finished_this_tick_precise() {632let mut t = Timer::from_seconds(0.01, TimerMode::Repeating);633let duration = Duration::from_secs_f64(0.333);634635// total duration: 0.333 => 33 times finished636t.tick(duration);637assert_eq!(t.times_finished_this_tick(), 33);638// total duration: 0.666 => 33 times finished639t.tick(duration);640assert_eq!(t.times_finished_this_tick(), 33);641// total duration: 0.999 => 33 times finished642t.tick(duration);643assert_eq!(t.times_finished_this_tick(), 33);644// total duration: 1.332 => 34 times finished645t.tick(duration);646assert_eq!(t.times_finished_this_tick(), 34);647}648649#[test]650fn almost_finished_repeating() {651let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);652let duration = Duration::from_nanos(1);653654t.almost_finish();655assert!(!t.is_finished());656assert_eq!(t.times_finished_this_tick(), 0);657assert_eq!(t.remaining(), Duration::from_nanos(1));658659t.tick(duration);660assert!(t.is_finished());661assert_eq!(t.times_finished_this_tick(), 1);662}663664#[test]665fn paused() {666let mut t = Timer::from_seconds(10.0, TimerMode::Once);667668t.tick(Duration::from_secs_f32(10.0));669assert!(t.just_finished());670assert!(t.is_finished());671// A paused timer should change just_finished to false after a tick672t.pause();673t.tick(Duration::from_secs_f32(5.0));674assert!(!t.just_finished());675assert!(t.is_finished());676}677678#[test]679fn paused_repeating() {680let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);681682t.tick(Duration::from_secs_f32(10.0));683assert!(t.just_finished());684assert!(t.is_finished());685// A paused repeating timer should change finished and just_finished to false after a tick686t.pause();687t.tick(Duration::from_secs_f32(5.0));688assert!(!t.just_finished());689assert!(!t.is_finished());690}691}692693694