Skip to content

Commit

Permalink
Make paused timers update just_finished on tick (bevyengine#4445)
Browse files Browse the repository at this point in the history
# Objective
Make timers update `just_finished` on tick, even if paused.
Fixes bevyengine#4436

## Solution
`just_finished()` returns `times_finished > 0`. So I:
 * Renamed `times_finished` to `times_finished_this_tick` to reduce confusion.
 * Set `times_finished_this_tick` to `0` on tick when paused.
 * Additionally set `finished` to `false` if the timer is repeating.

Notably this change broke none of the existing tests, so I added a couple for this.

Files changed shows a lot of noise because of the rename. Check the first commit for the relevant changes.

Co-authored-by: devil-ira <[email protected]>
  • Loading branch information
2 people authored and exjam committed May 22, 2022
1 parent 4af1866 commit 00c1b9f
Showing 1 changed file with 64 additions and 32 deletions.
96 changes: 64 additions & 32 deletions crates/bevy_core/src/time/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ pub struct Timer {
duration: Duration,
repeating: bool,
finished: bool,
times_finished: u32,
times_finished_this_tick: u32,
}

impl Timer {
Expand Down Expand Up @@ -76,7 +76,7 @@ impl Timer {
/// ```
#[inline]
pub fn just_finished(&self) -> bool {
self.times_finished > 0
self.times_finished_this_tick > 0
}

/// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.
Expand Down Expand Up @@ -202,11 +202,15 @@ impl Timer {
/// ```
pub fn tick(&mut self, delta: Duration) -> &Self {
if self.paused() {
self.times_finished_this_tick = 0;
if self.repeating() {
self.finished = false;
}
return self;
}

if !self.repeating() && self.finished() {
self.times_finished = 0;
self.times_finished_this_tick = 0;
return self;
}

Expand All @@ -215,16 +219,16 @@ impl Timer {

if self.finished() {
if self.repeating() {
self.times_finished =
self.times_finished_this_tick =
(self.elapsed().as_nanos() / self.duration().as_nanos()) as u32;
// Duration does not have a modulo
self.set_elapsed(self.elapsed() - self.duration() * self.times_finished);
self.set_elapsed(self.elapsed() - self.duration() * self.times_finished_this_tick);
} else {
self.times_finished = 1;
self.times_finished_this_tick = 1;
self.set_elapsed(self.duration());
}
} else {
self.times_finished = 0;
self.times_finished_this_tick = 0;
}

self
Expand Down Expand Up @@ -305,7 +309,7 @@ impl Timer {
pub fn reset(&mut self) {
self.stopwatch.reset();
self.finished = false;
self.times_finished = 0;
self.times_finished_this_tick = 0;
}

/// Returns the percentage of the timer elapsed time (goes from 0.0 to 1.0).
Expand Down Expand Up @@ -350,15 +354,15 @@ impl Timer {
/// 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);
/// assert_eq!(timer.times_finished_this_tick(), 6);
/// timer.tick(Duration::from_secs_f32(2.0));
/// assert_eq!(timer.times_finished(), 2);
/// assert_eq!(timer.times_finished_this_tick(), 2);
/// timer.tick(Duration::from_secs_f32(0.5));
/// assert_eq!(timer.times_finished(), 0);
/// assert_eq!(timer.times_finished_this_tick(), 0);
/// ```
#[inline]
pub fn times_finished(&self) -> u32 {
self.times_finished
pub fn times_finished_this_tick(&self) -> u32 {
self.times_finished_this_tick
}
}

Expand All @@ -376,7 +380,7 @@ mod tests {
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 0);
assert!(!t.repeating());
assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975);
Expand All @@ -387,7 +391,7 @@ mod tests {
assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 0);
assert!(!t.repeating());
assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975);
Expand All @@ -397,15 +401,15 @@ mod tests {
assert_eq!(t.elapsed_secs(), 10.0);
assert!(t.finished());
assert!(t.just_finished());
assert_eq!(t.times_finished(), 1);
assert_eq!(t.times_finished_this_tick(), 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(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed_secs(), 10.0);
assert!(t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 0);
assert_eq!(t.percent(), 1.0);
assert_eq!(t.percent_left(), 0.0);
}
Expand All @@ -419,7 +423,7 @@ mod tests {
assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 0);
assert!(t.repeating());
assert_eq!(t.percent(), 0.375);
assert_eq!(t.percent_left(), 0.625);
Expand All @@ -428,58 +432,86 @@ mod tests {
assert_eq!(t.elapsed_secs(), 0.25);
assert!(t.finished());
assert!(t.just_finished());
assert_eq!(t.times_finished(), 1);
assert_eq!(t.times_finished_this_tick(), 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(Duration::from_secs_f32(1.0));
assert_eq!(t.elapsed_secs(), 1.25);
assert!(!t.finished());
assert!(!t.just_finished());
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 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);
assert_eq!(t.times_finished_this_tick(), 0);
t.tick(Duration::from_secs_f32(3.5));
assert_eq!(t.times_finished(), 3);
assert_eq!(t.times_finished_this_tick(), 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);
assert_eq!(t.times_finished_this_tick(), 0);
}

#[test]
fn times_finished() {
fn times_finished_this_tick() {
let mut t = Timer::from_seconds(1.0, false);
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 0);
t.tick(Duration::from_secs_f32(1.5));
assert_eq!(t.times_finished(), 1);
assert_eq!(t.times_finished_this_tick(), 1);
t.tick(Duration::from_secs_f32(0.5));
assert_eq!(t.times_finished(), 0);
assert_eq!(t.times_finished_this_tick(), 0);
}

#[test]
fn times_finished_precise() {
fn times_finished_this_tick_precise() {
let mut t = Timer::from_seconds(0.01, true);
let duration = Duration::from_secs_f64(0.333);

// total duration: 0.333 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 33);
assert_eq!(t.times_finished_this_tick(), 33);
// total duration: 0.666 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 33);
assert_eq!(t.times_finished_this_tick(), 33);
// total duration: 0.999 => 33 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 33);
assert_eq!(t.times_finished_this_tick(), 33);
// total duration: 1.332 => 34 times finished
t.tick(duration);
assert_eq!(t.times_finished(), 34);
assert_eq!(t.times_finished_this_tick(), 34);
}

#[test]
fn paused() {
let mut t = Timer::from_seconds(10.0, false);

t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
assert!(t.finished());
// A paused timer should change just_finished to false after a tick
t.pause();
t.tick(Duration::from_secs_f32(5.0));
assert!(!t.just_finished());
assert!(t.finished());
}

#[test]
fn paused_repeating() {
let mut t = Timer::from_seconds(10.0, true);

t.tick(Duration::from_secs_f32(10.0));
assert!(t.just_finished());
assert!(t.finished());
// A paused repeating timer should change finished and just_finished to false after a tick
t.pause();
t.tick(Duration::from_secs_f32(5.0));
assert!(!t.just_finished());
assert!(!t.finished());
}
}

0 comments on commit 00c1b9f

Please sign in to comment.