@@ -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 ) ]
1111pub 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
6076pub ( 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