Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Count number of times a repeating Timer wraps around in a tick #1112

Merged
merged 3 commits into from
Jan 19, 2021
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 36 additions & 11 deletions crates/bevy_core/src/time/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
num_times_just_finished: u32,
paused: bool,
repeating: bool,
}
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -84,16 +86,28 @@ 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.num_times_just_finished > 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 num_times_just_finished(&self) -> u32 {
reidbhuntley marked this conversation as resolved.
Show resolved Hide resolved
self.num_times_just_finished
}

#[inline]
pub fn repeating(&self) -> bool {
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
}

Expand All @@ -102,28 +116,32 @@ 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.num_times_just_finished = (self.elapsed / self.duration) as u32;
// Repeating timers wrap around
self.elapsed %= self.duration;
} else {
self.num_times_just_finished = if prev_finished { 0 } else { 1 };
// Non-repeating timers clamp to duration
self.elapsed = self.duration;
}
} else {
self.num_times_just_finished = 0;
}
self
}

#[inline]
pub fn reset(&mut self) {
self.finished = false;
self.just_finished = false;
self.num_times_just_finished = 0;
self.elapsed = 0.0;
}

Expand Down Expand Up @@ -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.num_times_just_finished(), 0);
assert_eq!(t.repeating(), false);
assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975);
Expand All @@ -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.num_times_just_finished(), 0);
assert_eq!(t.repeating(), false);
assert_eq!(t.percent(), 0.025);
assert_eq!(t.percent_left(), 0.975);
Expand All @@ -170,13 +190,15 @@ mod tests {
assert_eq!(t.elapsed(), 10.0);
assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), true);
assert_eq!(t.num_times_just_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);
assert_eq!(t.finished(), true);
assert_eq!(t.just_finished(), false);
assert_eq!(t.num_times_just_finished(), 0);
assert_eq!(t.percent(), 1.0);
assert_eq!(t.percent_left(), 0.0);
}
Expand All @@ -190,21 +212,24 @@ mod tests {
assert_eq!(t.duration(), 2.0);
assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false);
assert_eq!(t.num_times_just_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(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.num_times_just_finished(), 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
t.tick(1.0);
assert_eq!(t.elapsed(), 1.25);
assert_eq!(t.finished(), false);
assert_eq!(t.just_finished(), false);
assert_eq!(t.num_times_just_finished(), 0);
assert_eq!(t.percent(), 0.625);
assert_eq!(t.percent_left(), 0.375);
}
Expand Down