Improve timer ergonomics. Add tests (#923)
This commit is contained in:
parent
01ba7c4425
commit
2f408cf053
@ -9,8 +9,11 @@ use bevy_utils::Duration;
|
|||||||
/// 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.
|
/// 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.
|
||||||
#[derive(Clone, Debug, Default, Properties)]
|
#[derive(Clone, Debug, Default, Properties)]
|
||||||
pub struct Timer {
|
pub struct Timer {
|
||||||
|
/// Time elapsed on the timer. Guaranteed to be between 0.0 and `duration`, inclusive.
|
||||||
pub elapsed: f32,
|
pub elapsed: f32,
|
||||||
pub duration: f32,
|
pub duration: f32,
|
||||||
|
/// 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.
|
||||||
pub finished: bool,
|
pub finished: bool,
|
||||||
/// Will only be true on the tick `duration` is reached or exceeded.
|
/// Will only be true on the tick `duration` is reached or exceeded.
|
||||||
pub just_finished: bool,
|
pub just_finished: bool,
|
||||||
@ -36,16 +39,19 @@ impl Timer {
|
|||||||
|
|
||||||
/// Advances the timer by `delta` seconds.
|
/// Advances the timer by `delta` seconds.
|
||||||
pub fn tick(&mut self, delta: f32) -> &Self {
|
pub fn tick(&mut self, delta: f32) -> &Self {
|
||||||
let prev_finished = self.elapsed >= self.duration;
|
let prev_finished = self.finished;
|
||||||
if !prev_finished {
|
self.elapsed += delta;
|
||||||
self.elapsed += delta;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.finished = self.elapsed >= self.duration;
|
self.finished = self.elapsed >= self.duration;
|
||||||
self.just_finished = !prev_finished && self.finished;
|
self.just_finished = !prev_finished && self.finished;
|
||||||
|
if self.finished {
|
||||||
if self.repeating && self.finished {
|
if self.repeating {
|
||||||
self.elapsed %= self.duration;
|
// Repeating timers wrap around
|
||||||
|
self.elapsed %= self.duration;
|
||||||
|
} else {
|
||||||
|
// Non-repeating timers clamp to duration
|
||||||
|
self.elapsed = self.duration;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -55,6 +61,16 @@ impl Timer {
|
|||||||
self.just_finished = false;
|
self.just_finished = false;
|
||||||
self.elapsed = 0.0;
|
self.elapsed = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Percent timer has elapsed (goes from 0.0 to 1.0)
|
||||||
|
pub fn percent(&self) -> f32 {
|
||||||
|
self.elapsed / self.duration
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Percent left on timer (goes from 1.0 to 0.0)
|
||||||
|
pub fn percent_left(&self) -> f32 {
|
||||||
|
(self.duration - self.elapsed) / self.duration
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
|
pub(crate) fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
|
||||||
@ -62,3 +78,64 @@ pub(crate) fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
|
|||||||
timer.tick(time.delta_seconds);
|
timer.tick(time.delta_seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::Timer;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_non_repeating() {
|
||||||
|
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);
|
||||||
|
assert_eq!(t.finished, false);
|
||||||
|
assert_eq!(t.just_finished, false);
|
||||||
|
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.tick(500.0);
|
||||||
|
assert_eq!(t.elapsed, 10.0);
|
||||||
|
assert_eq!(t.finished, true);
|
||||||
|
assert_eq!(t.just_finished, true);
|
||||||
|
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.percent(), 1.0);
|
||||||
|
assert_eq!(t.percent_left(), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_repeating() {
|
||||||
|
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);
|
||||||
|
assert_eq!(t.finished, false);
|
||||||
|
assert_eq!(t.just_finished, false);
|
||||||
|
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);
|
||||||
|
assert_eq!(t.elapsed, 0.25);
|
||||||
|
assert_eq!(t.finished, true);
|
||||||
|
assert_eq!(t.just_finished, true);
|
||||||
|
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.percent(), 0.625);
|
||||||
|
assert_eq!(t.percent_left(), 0.375);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user