diff --git a/crates/bevy_core/src/time/timer.rs b/crates/bevy_core/src/time/timer.rs index 318531b286..fbb15acf39 100644 --- a/crates/bevy_core/src/time/timer.rs +++ b/crates/bevy_core/src/time/timer.rs @@ -13,8 +13,8 @@ pub struct Timer { elapsed: f32, duration: f32, finished: bool, - /// Will only be true on the tick `duration` is reached or exceeded. - just_finished: bool, + /// Will only be non-zero on the tick `duration` is reached or exceeded. + just_finished_count: u32, paused: bool, repeating: bool, } @@ -51,7 +51,8 @@ impl Timer { self.paused } - /// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`, inclusive. + /// 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. #[inline] pub fn elapsed(&self) -> f32 { self.elapsed @@ -74,8 +75,9 @@ impl Timer { /// Returns the finished state of the timer. /// - /// 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, and can still be reset at any given point. + /// 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`. #[inline] pub fn finished(&self) -> bool { self.finished @@ -84,7 +86,16 @@ impl Timer { /// Will only be true on the tick the timer's duration is reached or exceeded. #[inline] pub fn just_finished(&self) -> bool { - self.just_finished + self.just_finished_count > 0 + } + + /// Returns the total number of times the timer finished during this tick. + /// + /// 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. + #[inline] + pub fn just_finished_count(&self) -> u32 { + self.just_finished_count } #[inline] @@ -92,8 +103,11 @@ impl Timer { self.repeating } - #[inline] pub fn set_repeating(&mut self, repeating: bool) { + if !self.repeating && repeating && self.finished { + self.elapsed = 0.0; + self.finished = self.just_finished(); + } self.repeating = repeating } @@ -102,20 +116,24 @@ impl Timer { if self.paused { return self; } + let prev_finished = self.finished; self.elapsed += delta; - self.finished = self.elapsed >= self.duration; - self.just_finished = !prev_finished && self.finished; 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; } else { + self.just_finished_count = if prev_finished { 0 } else { 1 }; // Non-repeating timers clamp to duration self.elapsed = self.duration; } + } else { + self.just_finished_count = 0; } self } @@ -123,7 +141,7 @@ impl Timer { #[inline] pub fn reset(&mut self) { self.finished = false; - self.just_finished = false; + self.just_finished_count = 0; self.elapsed = 0.0; } @@ -151,6 +169,7 @@ mod tests { assert_eq!(t.duration(), 10.0); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); + assert_eq!(t.just_finished_count(), 0); assert_eq!(t.repeating(), false); assert_eq!(t.percent(), 0.025); assert_eq!(t.percent_left(), 0.975); @@ -161,6 +180,7 @@ mod tests { assert_eq!(t.duration(), 10.0); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); + assert_eq!(t.just_finished_count(), 0); assert_eq!(t.repeating(), false); assert_eq!(t.percent(), 0.025); assert_eq!(t.percent_left(), 0.975); @@ -170,6 +190,7 @@ mod tests { assert_eq!(t.elapsed(), 10.0); assert_eq!(t.finished(), true); assert_eq!(t.just_finished(), true); + assert_eq!(t.just_finished_count(), 1); assert_eq!(t.percent(), 1.0); assert_eq!(t.percent_left(), 0.0); // Continuing to tick when finished should only change just_finished @@ -177,6 +198,7 @@ mod tests { assert_eq!(t.elapsed(), 10.0); assert_eq!(t.finished(), true); assert_eq!(t.just_finished(), false); + assert_eq!(t.just_finished_count(), 0); assert_eq!(t.percent(), 1.0); assert_eq!(t.percent_left(), 0.0); } @@ -190,14 +212,16 @@ mod tests { assert_eq!(t.duration(), 2.0); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); + assert_eq!(t.just_finished_count(), 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(1.5); + t.tick(3.5); assert_eq!(t.elapsed(), 0.25); assert_eq!(t.finished(), true); assert_eq!(t.just_finished(), true); + assert_eq!(t.just_finished_count(), 2); 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 @@ -205,6 +229,7 @@ mod tests { assert_eq!(t.elapsed(), 1.25); assert_eq!(t.finished(), false); assert_eq!(t.just_finished(), false); + assert_eq!(t.just_finished_count(), 0); assert_eq!(t.percent(), 0.625); assert_eq!(t.percent_left(), 0.375); }