Skip to content

Commit 2f408cf

Browse files
authored
Improve timer ergonomics. Add tests (#923)
1 parent 01ba7c4 commit 2f408cf

File tree

1 file changed

+84
-7
lines changed

1 file changed

+84
-7
lines changed

crates/bevy_core/src/time/timer.rs

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ use bevy_utils::Duration;
99
/// 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.
1010
#[derive(Clone, Debug, Default, Properties)]
1111
pub struct Timer {
12+
/// Time elapsed on the timer. Guaranteed to be between 0.0 and `duration`, inclusive.
1213
pub elapsed: f32,
1314
pub duration: f32,
15+
/// Non repeating timers will stop tracking and stay in the finished state until reset.
16+
/// 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.
1417
pub finished: bool,
1518
/// Will only be true on the tick `duration` is reached or exceeded.
1619
pub just_finished: bool,
@@ -36,16 +39,19 @@ impl Timer {
3639

3740
/// Advances the timer by `delta` seconds.
3841
pub fn tick(&mut self, delta: f32) -> &Self {
39-
let prev_finished = self.elapsed >= self.duration;
40-
if !prev_finished {
41-
self.elapsed += delta;
42-
}
42+
let prev_finished = self.finished;
43+
self.elapsed += delta;
4344

4445
self.finished = self.elapsed >= self.duration;
4546
self.just_finished = !prev_finished && self.finished;
46-
47-
if self.repeating && self.finished {
48-
self.elapsed %= self.duration;
47+
if self.finished {
48+
if self.repeating {
49+
// Repeating timers wrap around
50+
self.elapsed %= self.duration;
51+
} else {
52+
// Non-repeating timers clamp to duration
53+
self.elapsed = self.duration;
54+
}
4955
}
5056
self
5157
}
@@ -55,10 +61,81 @@ impl Timer {
5561
self.just_finished = false;
5662
self.elapsed = 0.0;
5763
}
64+
65+
/// Percent timer has elapsed (goes from 0.0 to 1.0)
66+
pub fn percent(&self) -> f32 {
67+
self.elapsed / self.duration
68+
}
69+
70+
/// Percent left on timer (goes from 1.0 to 0.0)
71+
pub fn percent_left(&self) -> f32 {
72+
(self.duration - self.elapsed) / self.duration
73+
}
5874
}
5975

6076
pub(crate) fn timer_system(time: Res<Time>, mut query: Query<&mut Timer>) {
6177
for mut timer in query.iter_mut() {
6278
timer.tick(time.delta_seconds);
6379
}
6480
}
81+
82+
#[cfg(test)]
83+
mod tests {
84+
use super::Timer;
85+
86+
#[test]
87+
fn test_non_repeating() {
88+
let mut t = Timer::from_seconds(10.0, false);
89+
// Tick once, check all attributes
90+
t.tick(0.25);
91+
assert_eq!(t.elapsed, 0.25);
92+
assert_eq!(t.duration, 10.0);
93+
assert_eq!(t.finished, false);
94+
assert_eq!(t.just_finished, false);
95+
assert_eq!(t.repeating, false);
96+
assert_eq!(t.percent(), 0.025);
97+
assert_eq!(t.percent_left(), 0.975);
98+
// Tick past the end and make sure elapsed doesn't go past 0.0 and other things update
99+
t.tick(500.0);
100+
assert_eq!(t.elapsed, 10.0);
101+
assert_eq!(t.finished, true);
102+
assert_eq!(t.just_finished, true);
103+
assert_eq!(t.percent(), 1.0);
104+
assert_eq!(t.percent_left(), 0.0);
105+
// Continuing to tick when finished should only change just_finished
106+
t.tick(1.0);
107+
assert_eq!(t.elapsed, 10.0);
108+
assert_eq!(t.finished, true);
109+
assert_eq!(t.just_finished, false);
110+
assert_eq!(t.percent(), 1.0);
111+
assert_eq!(t.percent_left(), 0.0);
112+
}
113+
114+
#[test]
115+
fn test_repeating() {
116+
let mut t = Timer::from_seconds(2.0, true);
117+
// Tick once, check all attributes
118+
t.tick(0.75);
119+
assert_eq!(t.elapsed, 0.75);
120+
assert_eq!(t.duration, 2.0);
121+
assert_eq!(t.finished, false);
122+
assert_eq!(t.just_finished, false);
123+
assert_eq!(t.repeating, true);
124+
assert_eq!(t.percent(), 0.375);
125+
assert_eq!(t.percent_left(), 0.625);
126+
// Tick past the end and make sure elapsed wraps
127+
t.tick(1.5);
128+
assert_eq!(t.elapsed, 0.25);
129+
assert_eq!(t.finished, true);
130+
assert_eq!(t.just_finished, true);
131+
assert_eq!(t.percent(), 0.125);
132+
assert_eq!(t.percent_left(), 0.875);
133+
// Continuing to tick should turn off both finished & just_finished for repeating timers
134+
t.tick(1.0);
135+
assert_eq!(t.elapsed, 1.25);
136+
assert_eq!(t.finished, false);
137+
assert_eq!(t.just_finished, false);
138+
assert_eq!(t.percent(), 0.625);
139+
assert_eq!(t.percent_left(), 0.375);
140+
}
141+
}

0 commit comments

Comments
 (0)