From 6de8c035bdd0f6466d400f8525f56ba3013f425d Mon Sep 17 00:00:00 2001 From: Chris Janaqi Date: Fri, 5 Mar 2021 18:51:20 +0000 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20`Timer`=20refactor=20to=20?= =?UTF-8?q?duration.=E2=9C=A8=20Add=20`Stopwatch`=20struct.=20(#1151)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This pull request is following the discussion on the issue #1127. Additionally, it integrates the change proposed by #1112. The list of change of this pull request: * 💥 Change `Timer` to `Timer` in order to make specialization very explicit to the user, while removing the boilerplated NewType pattern for Timers. This is a breaking change * ✨ Add `Timer::times_finished` method that counts the number of wraps for repeating timers. * ♻️ Refactored `Timer` * 🐛 Fix a bug where 2 successive calls to `Timer::tick` which makes a repeating timer to finish makes `Timer::just_finished` to return `false` where it should return `true`. Minimal failing example: ```rust use bevy::prelude::*; let mut timer: Timer<()> = Timer::from_seconds(1.0, true); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); timer.tick(1.5); assert!(timer.finished()); assert!(timer.just_finished()); // <- This fails where it should not ``` * 📚 Add extensive documentation for Timer with doc examples. * ✨ Add `Cooldown` and `Stopwatch` structs similar to `Timer` with extensive doc and tests. Even if the type specialization is not retained for bevy, the doc, bugfix and added method are worth salvaging 😅. This is my first PR for bevy, please be kind to me ❤️ . Co-authored-by: Carter Anderson --- crates/bevy_core/src/time/mod.rs | 2 + crates/bevy_core/src/time/stopwatch.rs | 167 +++++++ crates/bevy_core/src/time/timer.rs | 424 ++++++++++++++---- .../src/log_diagnostics_plugin.rs | 4 +- crates/bevy_reflect/src/impls/std.rs | 3 +- examples/2d/contributors.rs | 2 +- examples/2d/sprite_sheet.rs | 2 +- examples/app/plugin.rs | 2 +- examples/ecs/event.rs | 2 +- examples/ecs/timers.rs | 10 +- examples/ui/font_atlas_debug.rs | 2 +- 11 files changed, 507 insertions(+), 113 deletions(-) create mode 100644 crates/bevy_core/src/time/stopwatch.rs diff --git a/crates/bevy_core/src/time/mod.rs b/crates/bevy_core/src/time/mod.rs index 8be564301c013b..e247dc28d1bf0d 100644 --- a/crates/bevy_core/src/time/mod.rs +++ b/crates/bevy_core/src/time/mod.rs @@ -1,8 +1,10 @@ mod fixed_timestep; +mod stopwatch; #[allow(clippy::module_inception)] mod time; mod timer; pub use fixed_timestep::*; +pub use stopwatch::*; pub use time::*; pub use timer::*; diff --git a/crates/bevy_core/src/time/stopwatch.rs b/crates/bevy_core/src/time/stopwatch.rs new file mode 100644 index 00000000000000..1705a8207d9f3e --- /dev/null +++ b/crates/bevy_core/src/time/stopwatch.rs @@ -0,0 +1,167 @@ +use bevy_reflect::{Reflect, ReflectComponent}; +use bevy_utils::Duration; + +/// A Stopwatch is a struct that track elapsed time when started. +/// +/// # Examples +/// +/// ``` +/// # use bevy_core::*; +/// use std::time::Duration; +/// let mut stopwatch = Stopwatch::new(); +/// assert_eq!(stopwatch.elapsed_secs(), 0.0); +/// stopwatch.tick(Duration::from_secs_f32(1.0)); // tick one second +/// assert_eq!(stopwatch.elapsed_secs(), 1.0); +/// stopwatch.pause(); +/// stopwatch.tick(Duration::from_secs_f32(1.0)); // paused stopwatches don't tick +/// assert_eq!(stopwatch.elapsed_secs(), 1.0); +/// stopwatch.reset(); // reset the stopwatch +/// assert!(stopwatch.paused()); +/// assert_eq!(stopwatch.elapsed_secs(), 0.0); +/// ``` +#[derive(Clone, Debug, Default, Reflect)] +#[reflect(Component)] +pub struct Stopwatch { + elapsed: Duration, + paused: bool, +} + +impl Stopwatch { + /// Create a new unpaused `Stopwatch` with no elapsed time. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// let stopwatch = Stopwatch::new(); + /// assert_eq!(stopwatch.elapsed_secs(), 0.0); + /// assert_eq!(stopwatch.paused(), false); + /// ``` + pub fn new() -> Self { + Default::default() + } + + /// Returns the elapsed time since the last [`reset`](Stopwatch::reset) + /// of the stopwatch. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut stopwatch = Stopwatch::new(); + /// stopwatch.tick(Duration::from_secs(1)); + /// assert_eq!(stopwatch.elapsed(), Duration::from_secs(1)); + /// ``` + #[inline] + pub fn elapsed(&self) -> Duration { + self.elapsed + } + + #[inline] + pub fn elapsed_secs(&self) -> f32 { + self.elapsed().as_secs_f32() + } + + /// Sets the elapsed time of the stopwatch. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut stopwatch = Stopwatch::new(); + /// stopwatch.set_elapsed(Duration::from_secs_f32(1.0)); + /// assert_eq!(stopwatch.elapsed_secs(), 1.0); + /// ``` + #[inline] + pub fn set_elapsed(&mut self, time: Duration) { + self.elapsed = time; + } + + /// Advance the stopwatch by `delta` seconds. + /// If the stopwatch is paused, ticking will not have any effect + /// on elapsed time. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut stopwatch = Stopwatch::new(); + /// stopwatch.tick(Duration::from_secs_f32(1.5)); + /// assert_eq!(stopwatch.elapsed_secs(), 1.5); + /// ``` + pub fn tick(&mut self, delta: Duration) -> &Self { + if !self.paused() { + self.elapsed += delta; + } + self + } + + /// Pauses the stopwatch. Any call to [`tick`](Stopwatch::tick) while + /// paused will not have any effect on the elapsed time. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut stopwatch = Stopwatch::new(); + /// stopwatch.pause(); + /// stopwatch.tick(Duration::from_secs_f32(1.5)); + /// assert!(stopwatch.paused()); + /// assert_eq!(stopwatch.elapsed_secs(), 0.0); + /// ``` + #[inline] + pub fn pause(&mut self) { + self.paused = true; + } + + /// Unpauses the stopwatch. Resume the effect of ticking on elapsed time. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut stopwatch = Stopwatch::new(); + /// stopwatch.pause(); + /// stopwatch.tick(Duration::from_secs_f32(1.0)); + /// stopwatch.unpause(); + /// stopwatch.tick(Duration::from_secs_f32(1.0)); + /// assert!(!stopwatch.paused()); + /// assert_eq!(stopwatch.elapsed_secs(), 1.0); + /// ``` + #[inline] + pub fn unpause(&mut self) { + self.paused = false; + } + + /// Returns `true` if the stopwatch is paused. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// let mut stopwatch = Stopwatch::new(); + /// assert!(!stopwatch.paused()); + /// stopwatch.pause(); + /// assert!(stopwatch.paused()); + /// stopwatch.unpause(); + /// assert!(!stopwatch.paused()); + /// ``` + #[inline] + pub fn paused(&self) -> bool { + self.paused + } + + /// Resets the stopwatch. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut stopwatch = Stopwatch::new(); + /// stopwatch.tick(Duration::from_secs_f32(1.5)); + /// stopwatch.reset(); + /// assert_eq!(stopwatch.elapsed_secs(), 0.0); + /// ``` + #[inline] + pub fn reset(&mut self) { + self.elapsed = Default::default(); + } +} diff --git a/crates/bevy_core/src/time/timer.rs b/crates/bevy_core/src/time/timer.rs index 755b869252e86e..bec4b29fbca8e1 100644 --- a/crates/bevy_core/src/time/timer.rs +++ b/crates/bevy_core/src/time/timer.rs @@ -1,3 +1,4 @@ +use crate::Stopwatch; use bevy_ecs::reflect::ReflectComponent; use bevy_reflect::Reflect; use bevy_utils::Duration; @@ -11,228 +12,455 @@ use bevy_utils::Duration; #[derive(Clone, Debug, Default, Reflect)] #[reflect(Component)] pub struct Timer { - elapsed: f32, - duration: f32, - finished: bool, - /// Will only be non-zero on the tick `duration` is reached or exceeded. - just_finished_count: u32, - paused: bool, + stopwatch: Stopwatch, + duration: Duration, repeating: bool, + finished: bool, + times_finished: u32, } impl Timer { + /// Creates a new timer with a given duration. + /// + /// See also [`Timer::from_seconds`](Timer::from_seconds). pub fn new(duration: Duration, repeating: bool) -> Self { - Timer { - duration: duration.as_secs_f32(), + Self { + duration, repeating, ..Default::default() } } - pub fn from_seconds(seconds: f32, repeating: bool) -> Self { - Timer { - duration: seconds, + /// Creates a new timer with a given duration in seconds. + /// + /// # Example + /// ``` + /// # use bevy_core::*; + /// let mut timer = Timer::from_seconds(1.0, false); + /// ``` + pub fn from_seconds(duration: f32, repeating: bool) -> Self { + Self { + duration: Duration::from_secs_f32(duration), repeating, ..Default::default() } } + /// Returns `true` if the timer has reached its duration. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.tick(Duration::from_secs_f32(1.5)); + /// assert!(timer.finished()); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert!(timer.finished()); + /// ``` #[inline] - pub fn pause(&mut self) { - self.paused = true - } - - #[inline] - pub fn unpause(&mut self) { - self.paused = false + pub fn finished(&self) -> bool { + self.finished } + /// Returns `true` only on the tick the timer reached its duration. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.tick(Duration::from_secs_f32(1.5)); + /// assert!(timer.just_finished()); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert!(!timer.just_finished()); + /// ``` #[inline] - pub fn paused(&self) -> bool { - self.paused + pub fn just_finished(&self) -> bool { + self.times_finished > 0 } /// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`. /// Will only equal `duration` when the timer is finished and non repeating. + /// + /// See also [`Stopwatch::elapsed`](Stopwatch::elapsed). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5)); + /// ``` #[inline] - pub fn elapsed(&self) -> f32 { - self.elapsed - } - - #[inline] - pub fn set_elapsed(&mut self, elapsed: f32) { - self.elapsed = elapsed - } - - #[inline] - pub fn duration(&self) -> f32 { - self.duration + pub fn elapsed(&self) -> Duration { + self.stopwatch.elapsed() } + /// Returns the time elapsed on the timer as a `f32`. + /// See also [`Timer::elapsed`](Timer::elapsed). #[inline] - pub fn set_duration(&mut self, duration: f32) { - self.duration = duration + pub fn elapsed_secs(&self) -> f32 { + self.stopwatch.elapsed_secs() } - /// Returns the finished state of the timer. + /// Sets the elapsed time of the timer without any other considerations. /// - /// Non-repeating timers will stop tracking and stay in the finished state until reset. - /// Repeating timers will only be in the finished state on each tick `duration` is reached or exceeded, so in that case - /// this function is equivalent to `just_finished`. + /// See also [`Stopwatch::set`](Stopwatch::set). + /// + /// # + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.set_elapsed(Duration::from_secs(2)); + /// assert_eq!(timer.elapsed(), Duration::from_secs(2)); + /// // the timer is not finished even if the elapsed time is greater than the duration. + /// assert!(!timer.finished()); + /// ``` #[inline] - pub fn finished(&self) -> bool { - self.finished + pub fn set_elapsed(&mut self, time: Duration) { + self.stopwatch.set_elapsed(time); } - /// Will only be true on the tick the timer's duration is reached or exceeded. + /// Returns the duration of the timer. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let timer = Timer::new(Duration::from_secs(1), false); + /// assert_eq!(timer.duration(), Duration::from_secs(1)); + /// ``` #[inline] - pub fn just_finished(&self) -> bool { - self.just_finished_count > 0 + pub fn duration(&self) -> Duration { + self.duration } - /// Returns the total number of times the timer finished during this tick. + /// Sets the duration of the timer. /// - /// This value can be used to ensure no completions of a repeating timer are skipped over due to a tick with an unexpectedly - /// long delta time. For non repeating timers, the value will only ever be 0 or 1. + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.5, false); + /// timer.set_duration(Duration::from_secs(1)); + /// assert_eq!(timer.duration(), Duration::from_secs(1)); + /// ``` #[inline] - pub fn just_finished_count(&self) -> u32 { - self.just_finished_count + pub fn set_duration(&mut self, duration: Duration) { + self.duration = duration; } + /// Returns `true` if the timer is repeating. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// let mut timer = Timer::from_seconds(1.0, true); + /// assert!(timer.repeating()); + /// ``` #[inline] pub fn repeating(&self) -> bool { self.repeating } + /// Sets whether the timer is repeating or not. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// let mut timer = Timer::from_seconds(1.0, true); + /// timer.set_repeating(false); + /// assert!(!timer.repeating()); + /// ``` + #[inline] pub fn set_repeating(&mut self, repeating: bool) { if !self.repeating && repeating && self.finished { - self.elapsed = 0.0; + self.stopwatch.reset(); self.finished = self.just_finished(); } self.repeating = repeating } - /// Advances the timer by `delta` seconds. - pub fn tick(&mut self, delta: f32) -> &Self { - if self.paused { + /// Advance the timer by `delta` seconds. + /// Non repeating timer will clamp at duration. + /// Repeating timer will wrap around. + /// + /// See also [`Stopwatch::tick`](Stopwatch::tick). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// let mut repeating = Timer::from_seconds(1.0, true); + /// timer.tick(Duration::from_secs_f32(1.5)); + /// repeating.tick(Duration::from_secs_f32(1.5)); + /// assert_eq!(timer.elapsed_secs(), 1.0); + /// assert_eq!(repeating.elapsed_secs(), 0.5); + /// ``` + pub fn tick(&mut self, delta: Duration) -> &Self { + if self.paused() { return self; } - let prev_finished = self.finished; - self.elapsed += delta; - self.finished = self.elapsed >= self.duration; + if !self.repeating() && self.finished() { + self.times_finished = 0; + return self; + } - if self.finished { - if self.repeating { - // Count the number of times the timer will wrap around from this tick - self.just_finished_count = (self.elapsed / self.duration) as u32; - // Repeating timers wrap around - self.elapsed %= self.duration; + self.stopwatch.tick(delta); + self.finished = self.elapsed() >= self.duration(); + + if self.finished() { + if self.repeating() { + self.times_finished = self.percent().floor() as u32; + // Duration does not have a modulo + self.set_elapsed(self.elapsed() - self.duration() * self.times_finished); } else { - self.just_finished_count = if prev_finished { 0 } else { 1 }; - // Non-repeating timers clamp to duration - self.elapsed = self.duration; + self.times_finished = 1; + self.set_elapsed(self.duration()); } } else { - self.just_finished_count = 0; + self.times_finished = 0; } + self } + /// Pauses the Timer. Disables the ticking of the timer. + /// + /// See also [`Stopwatch::pause`](Stopwatch::pause). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.pause(); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert_eq!(timer.elapsed_secs(), 0.0); + /// ``` + #[inline] + pub fn pause(&mut self) { + self.stopwatch.pause(); + } + + /// Unpauses the Timer. Resumes the ticking of the timer. + /// + /// See also [`Stopwatch::unpause()`](Stopwatch::unpause). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.pause(); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// timer.unpause(); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert_eq!(timer.elapsed_secs(), 0.5); + /// ``` + #[inline] + pub fn unpause(&mut self) { + self.stopwatch.unpause(); + } + + /// Returns `true` if the timer is paused. + /// + /// See also [`Stopwatch::paused`](Stopwatch::paused). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// let mut timer = Timer::from_seconds(1.0, false); + /// assert!(!timer.paused()); + /// timer.pause(); + /// assert!(timer.paused()); + /// timer.unpause(); + /// assert!(!timer.paused()); + /// ``` #[inline] + pub fn paused(&self) -> bool { + self.stopwatch.paused() + } + + /// Resets the timer. the reset doesn't affect the `paused` state of the timer. + /// + /// See also [`Stopwatch::reset`](Stopwatch::reset). + /// + /// Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, false); + /// timer.tick(Duration::from_secs_f32(1.5)); + /// timer.reset(); + /// assert!(!timer.finished()); + /// assert!(!timer.just_finished()); + /// assert_eq!(timer.elapsed_secs(), 0.0); + /// ``` pub fn reset(&mut self) { + self.stopwatch.reset(); self.finished = false; - self.just_finished_count = 0; - self.elapsed = 0.0; + self.times_finished = 0; } - /// Percent timer has elapsed (goes from 0.0 to 1.0) + /// Returns the percentage of the timer elapsed time (goes from 0.0 to 1.0). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(2.0, false); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert_eq!(timer.percent(), 0.25); + /// ``` + #[inline] pub fn percent(&self) -> f32 { - self.elapsed / self.duration + self.elapsed().as_secs_f32() / self.duration().as_secs_f32() } - /// Percent left on timer (goes from 1.0 to 0.0) + /// Returns the percentage of the timer remaining time (goes from 0.0 to 1.0). + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(2.0, false); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert_eq!(timer.percent_left(), 0.75); + /// ``` + #[inline] pub fn percent_left(&self) -> f32 { - (self.duration - self.elapsed) / self.duration + 1.0 - self.percent() + } + + /// Returns the number of times a repeating timer + /// finished during the last [`tick`](Timer::tick) call. + /// + /// For non repeating-timers, this method will only ever + /// return 0 or 1. + /// + /// # Examples + /// ``` + /// # use bevy_core::*; + /// use std::time::Duration; + /// let mut timer = Timer::from_seconds(1.0, true); + /// timer.tick(Duration::from_secs_f32(6.0)); + /// assert_eq!(timer.times_finished(), 6); + /// timer.tick(Duration::from_secs_f32(2.0)); + /// assert_eq!(timer.times_finished(), 2); + /// timer.tick(Duration::from_secs_f32(0.5)); + /// assert_eq!(timer.times_finished(), 0); + /// ``` + #[inline] + pub fn times_finished(&self) -> u32 { + self.times_finished } } #[cfg(test)] #[allow(clippy::float_cmp)] mod tests { - use super::Timer; + use super::*; #[test] - fn test_non_repeating() { + fn non_repeating_timer() { let mut t = Timer::from_seconds(10.0, false); // Tick once, check all attributes - t.tick(0.25); - assert_eq!(t.elapsed(), 0.25); - assert_eq!(t.duration(), 10.0); + t.tick(Duration::from_secs_f32(0.25)); + assert_eq!(t.elapsed_secs(), 0.25); + assert_eq!(t.duration(), Duration::from_secs_f32(10.0)); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); - assert_eq!(t.just_finished_count(), 0); + assert_eq!(t.times_finished(), 0); assert_eq!(t.repeating(), false); assert_eq!(t.percent(), 0.025); assert_eq!(t.percent_left(), 0.975); // Ticking while paused changes nothing t.pause(); - t.tick(500.0); - assert_eq!(t.elapsed(), 0.25); - assert_eq!(t.duration(), 10.0); + t.tick(Duration::from_secs_f32(500.0)); + assert_eq!(t.elapsed_secs(), 0.25); + assert_eq!(t.duration(), Duration::from_secs_f32(10.0)); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); - assert_eq!(t.just_finished_count(), 0); + assert_eq!(t.times_finished(), 0); assert_eq!(t.repeating(), false); assert_eq!(t.percent(), 0.025); assert_eq!(t.percent_left(), 0.975); // Tick past the end and make sure elapsed doesn't go past 0.0 and other things update t.unpause(); - t.tick(500.0); - assert_eq!(t.elapsed(), 10.0); + t.tick(Duration::from_secs_f32(500.0)); + assert_eq!(t.elapsed_secs(), 10.0); assert_eq!(t.finished(), true); assert_eq!(t.just_finished(), true); - assert_eq!(t.just_finished_count(), 1); + assert_eq!(t.times_finished(), 1); assert_eq!(t.percent(), 1.0); assert_eq!(t.percent_left(), 0.0); // Continuing to tick when finished should only change just_finished - t.tick(1.0); - assert_eq!(t.elapsed(), 10.0); + t.tick(Duration::from_secs_f32(1.0)); + assert_eq!(t.elapsed_secs(), 10.0); assert_eq!(t.finished(), true); assert_eq!(t.just_finished(), false); - assert_eq!(t.just_finished_count(), 0); + assert_eq!(t.times_finished(), 0); assert_eq!(t.percent(), 1.0); assert_eq!(t.percent_left(), 0.0); } #[test] - fn test_repeating() { + fn repeating_timer() { let mut t = Timer::from_seconds(2.0, true); // Tick once, check all attributes - t.tick(0.75); - assert_eq!(t.elapsed(), 0.75); - assert_eq!(t.duration(), 2.0); + t.tick(Duration::from_secs_f32(0.75)); + assert_eq!(t.elapsed_secs(), 0.75); + assert_eq!(t.duration(), Duration::from_secs_f32(2.0)); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); - assert_eq!(t.just_finished_count(), 0); + assert_eq!(t.times_finished(), 0); assert_eq!(t.repeating(), true); assert_eq!(t.percent(), 0.375); assert_eq!(t.percent_left(), 0.625); // Tick past the end and make sure elapsed wraps - t.tick(3.5); - assert_eq!(t.elapsed(), 0.25); + t.tick(Duration::from_secs_f32(1.5)); + assert_eq!(t.elapsed_secs(), 0.25); assert_eq!(t.finished(), true); assert_eq!(t.just_finished(), true); - assert_eq!(t.just_finished_count(), 2); + assert_eq!(t.times_finished(), 1); assert_eq!(t.percent(), 0.125); assert_eq!(t.percent_left(), 0.875); // Continuing to tick should turn off both finished & just_finished for repeating timers - t.tick(1.0); - assert_eq!(t.elapsed(), 1.25); + t.tick(Duration::from_secs_f32(1.0)); + assert_eq!(t.elapsed_secs(), 1.25); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); - assert_eq!(t.just_finished_count(), 0); + assert_eq!(t.times_finished(), 0); assert_eq!(t.percent(), 0.625); assert_eq!(t.percent_left(), 0.375); } + + #[test] + fn times_finished_repeating() { + let mut t = Timer::from_seconds(1.0, true); + assert_eq!(t.times_finished(), 0); + t.tick(Duration::from_secs_f32(3.5)); + assert_eq!(t.times_finished(), 3); + assert_eq!(t.elapsed_secs(), 0.5); + assert!(t.finished()); + assert!(t.just_finished()); + t.tick(Duration::from_secs_f32(0.2)); + assert_eq!(t.times_finished(), 0); + } + + #[test] + fn times_finished() { + let mut t = Timer::from_seconds(1.0, false); + assert_eq!(t.times_finished(), 0); + t.tick(Duration::from_secs_f32(1.5)); + assert_eq!(t.times_finished(), 1); + t.tick(Duration::from_secs_f32(0.5)); + assert_eq!(t.times_finished(), 0); + } } diff --git a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs index e75691e16d96cc..cc37e6183e2d44 100644 --- a/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs +++ b/crates/bevy_diagnostic/src/log_diagnostics_plugin.rs @@ -90,7 +90,7 @@ impl LogDiagnosticsPlugin { time: Res