bones_framework/time/timer.rs
1use std::time::Duration;
2
3use crate::prelude::*;
4
5use super::stopwatch::Stopwatch;
6
7/// Tracks elapsed time. Enters the finished state once `duration` is reached.
8///
9/// Non repeating timers will stop tracking and stay in the finished state until reset.
10/// Repeating timers will only be in the finished state on each tick `duration` is reached or
11/// exceeded, and can still be reset at any given point.
12///
13/// Paused timers will not have elapsed time increased.
14#[derive(Clone, Debug, Default, HasSchema)]
15pub struct Timer {
16 finished: bool,
17 mode: TimerMode,
18 duration: Duration,
19 stopwatch: Stopwatch,
20 times_finished_this_tick: u32,
21}
22
23impl Timer {
24 /// Creates a new timer with a given duration.
25 ///
26 /// See also [`Timer::from_seconds`](Timer::from_seconds).
27 pub fn new(duration: Duration, mode: TimerMode) -> Self {
28 Self {
29 duration,
30 mode,
31 ..Default::default()
32 }
33 }
34
35 /// Creates a new timer with a given duration in seconds.
36 ///
37 /// # Example
38 /// ```no_run
39 /// # use bones_framework::prelude::*;
40 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
41 /// ```
42 pub fn from_seconds(duration: f32, mode: TimerMode) -> Self {
43 Self {
44 duration: Duration::from_secs_f32(duration),
45 mode,
46 ..Default::default()
47 }
48 }
49
50 /// Returns `true` if the timer has reached its duration at least once.
51 /// See also [`Timer::just_finished`](Timer::just_finished).
52 ///
53 /// # Examples
54 /// ```no_run
55 /// # use bones_framework::prelude::*;
56 /// use std::time::Duration;
57 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
58 /// timer.tick(Duration::from_secs_f32(1.5));
59 /// assert!(timer.finished());
60 /// timer.tick(Duration::from_secs_f32(0.5));
61 /// assert!(timer.finished());
62 /// ```
63 #[inline]
64 pub fn finished(&self) -> bool {
65 self.finished
66 }
67
68 /// Returns `true` only on the tick the timer reached its duration.
69 ///
70 /// # Examples
71 /// ```no_run
72 /// # use bones_framework::prelude::*;
73 /// use std::time::Duration;
74 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
75 /// timer.tick(Duration::from_secs_f32(1.5));
76 /// assert!(timer.just_finished());
77 /// timer.tick(Duration::from_secs_f32(0.5));
78 /// assert!(!timer.just_finished());
79 /// ```
80 #[inline]
81 pub fn just_finished(&self) -> bool {
82 self.times_finished_this_tick > 0
83 }
84
85 /// Returns the time elapsed on the timer. Guaranteed to be between 0.0 and `duration`.
86 /// Will only equal `duration` when the timer is finished and non repeating.
87 ///
88 /// See also [`Stopwatch::elapsed`](Stopwatch::elapsed).
89 ///
90 /// # Examples
91 /// ```no_run
92 /// # use bones_framework::prelude::*;
93 /// use std::time::Duration;
94 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
95 /// timer.tick(Duration::from_secs_f32(0.5));
96 /// assert_eq!(timer.elapsed(), Duration::from_secs_f32(0.5));
97 /// ```
98 #[inline]
99 pub fn elapsed(&self) -> Duration {
100 self.stopwatch.elapsed()
101 }
102
103 /// Returns the time elapsed on the timer as an `f32`.
104 /// See also [`Timer::elapsed`](Timer::elapsed).
105 #[inline]
106 pub fn elapsed_secs(&self) -> f32 {
107 self.stopwatch.elapsed_secs()
108 }
109
110 /// Sets the elapsed time of the timer without any other considerations.
111 ///
112 /// # Example
113 /// ```no_run
114 /// # use bones_framework::prelude::*;
115 /// use std::time::Duration;
116 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
117 /// timer.set_elapsed(Duration::from_secs(2));
118 /// assert_eq!(timer.elapsed(), Duration::from_secs(2));
119 /// // the timer is not finished even if the elapsed time is greater than the duration.
120 /// assert!(!timer.finished());
121 /// ```
122 #[inline]
123 pub fn set_elapsed(&mut self, time: Duration) {
124 self.stopwatch.set_elapsed(time);
125 }
126
127 /// Returns the duration of the timer.
128 ///
129 /// # Examples
130 /// ```no_run
131 /// # use bones_framework::prelude::*;
132 /// use std::time::Duration;
133 /// let timer = Timer::new(Duration::from_secs(1), TimerMode::Once);
134 /// assert_eq!(timer.duration(), Duration::from_secs(1));
135 /// ```
136 #[inline]
137 pub fn duration(&self) -> Duration {
138 self.duration
139 }
140
141 /// Sets the duration of the timer.
142 ///
143 /// # Examples
144 /// ```no_run
145 /// # use bones_framework::prelude::*;
146 /// use std::time::Duration;
147 /// let mut timer = Timer::from_seconds(1.5, TimerMode::Once);
148 /// timer.set_duration(Duration::from_secs(1));
149 /// assert_eq!(timer.duration(), Duration::from_secs(1));
150 /// ```
151 #[inline]
152 pub fn set_duration(&mut self, duration: Duration) {
153 self.duration = duration;
154 }
155
156 /// Returns the mode of the timer.
157 ///
158 /// # Examples
159 /// ```no_run
160 /// # use bones_framework::prelude::*;
161 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
162 /// assert_eq!(timer.mode(), TimerMode::Repeating);
163 /// ```
164 #[inline]
165 pub fn mode(&self) -> TimerMode {
166 self.mode
167 }
168
169 /// Sets the mode of the timer.
170 ///
171 /// # Examples
172 /// ```no_run
173 /// # use bones_framework::prelude::*;
174 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
175 /// timer.set_mode(TimerMode::Once);
176 /// assert_eq!(timer.mode(), TimerMode::Once);
177 /// ```
178 #[doc(alias = "repeating")]
179 #[inline]
180 pub fn set_mode(&mut self, mode: TimerMode) {
181 if self.mode != TimerMode::Repeating && mode == TimerMode::Repeating && self.finished {
182 self.stopwatch.reset();
183 self.finished = self.just_finished();
184 }
185 self.mode = mode;
186 }
187
188 /// Advance the timer by `delta` seconds.
189 /// Non repeating timer will clamp at duration.
190 /// Repeating timer will wrap around.
191 /// Will not affect paused timers.
192 ///
193 /// See also [`Stopwatch::tick`](Stopwatch::tick).
194 ///
195 /// # Examples
196 /// ```no_run
197 /// # use bones_framework::prelude::*;
198 /// use std::time::Duration;
199 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
200 /// let mut repeating = Timer::from_seconds(1.0, TimerMode::Repeating);
201 /// timer.tick(Duration::from_secs_f32(1.5));
202 /// repeating.tick(Duration::from_secs_f32(1.5));
203 /// assert_eq!(timer.elapsed_secs(), 1.0);
204 /// assert_eq!(repeating.elapsed_secs(), 0.5);
205 /// ```
206 pub fn tick(&mut self, delta: Duration) -> &Self {
207 if self.paused() {
208 self.times_finished_this_tick = 0;
209 if self.mode == TimerMode::Repeating {
210 self.finished = false;
211 }
212 return self;
213 }
214
215 if self.mode != TimerMode::Repeating && self.finished() {
216 self.times_finished_this_tick = 0;
217 return self;
218 }
219
220 self.stopwatch.tick(delta);
221 self.finished = self.elapsed() >= self.duration();
222
223 if self.finished() {
224 if self.mode == TimerMode::Repeating {
225 self.times_finished_this_tick =
226 (self.elapsed().as_nanos() / self.duration().as_nanos()) as u32;
227 // Duration does not have a modulo
228 self.set_elapsed(self.elapsed() - self.duration() * self.times_finished_this_tick);
229 } else {
230 self.times_finished_this_tick = 1;
231 self.set_elapsed(self.duration());
232 }
233 } else {
234 self.times_finished_this_tick = 0;
235 }
236
237 self
238 }
239
240 /// Pauses the Timer. Disables the ticking of the timer.
241 ///
242 /// See also [`Stopwatch::pause`](Stopwatch::pause).
243 ///
244 /// # Examples
245 /// ```no_run
246 /// # use bones_framework::prelude::*;
247 /// use std::time::Duration;
248 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
249 /// timer.pause();
250 /// timer.tick(Duration::from_secs_f32(0.5));
251 /// assert_eq!(timer.elapsed_secs(), 0.0);
252 /// ```
253 #[inline]
254 pub fn pause(&mut self) {
255 self.stopwatch.pause();
256 }
257
258 /// Unpauses the Timer. Resumes the ticking of the timer.
259 ///
260 /// See also [`Stopwatch::unpause()`](Stopwatch::unpause).
261 ///
262 /// # Examples
263 /// ```no_run
264 /// # use bones_framework::prelude::*;
265 /// use std::time::Duration;
266 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
267 /// timer.pause();
268 /// timer.tick(Duration::from_secs_f32(0.5));
269 /// timer.unpause();
270 /// timer.tick(Duration::from_secs_f32(0.5));
271 /// assert_eq!(timer.elapsed_secs(), 0.5);
272 /// ```
273 #[inline]
274 pub fn unpause(&mut self) {
275 self.stopwatch.unpause();
276 }
277
278 /// Returns `true` if the timer is paused.
279 ///
280 /// See also [`Stopwatch::paused`](Stopwatch::paused).
281 ///
282 /// # Examples
283 /// ```no_run
284 /// # use bones_framework::prelude::*;
285 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
286 /// assert!(!timer.paused());
287 /// timer.pause();
288 /// assert!(timer.paused());
289 /// timer.unpause();
290 /// assert!(!timer.paused());
291 /// ```
292 #[inline]
293 pub fn paused(&self) -> bool {
294 self.stopwatch.paused()
295 }
296
297 /// Resets the timer. The reset doesn't affect the `paused` state of the timer.
298 ///
299 /// See also [`Stopwatch::reset`](Stopwatch::reset).
300 ///
301 /// Examples
302 /// ```no_run
303 /// # use bones_framework::prelude::*;
304 /// use std::time::Duration;
305 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Once);
306 /// timer.tick(Duration::from_secs_f32(1.5));
307 /// timer.reset();
308 /// assert!(!timer.finished());
309 /// assert!(!timer.just_finished());
310 /// assert_eq!(timer.elapsed_secs(), 0.0);
311 /// ```
312 pub fn reset(&mut self) {
313 self.stopwatch.reset();
314 self.finished = false;
315 self.times_finished_this_tick = 0;
316 }
317
318 /// Returns the percentage of the timer elapsed time (goes from 0.0 to 1.0).
319 ///
320 /// # Examples
321 /// ```no_run
322 /// # use bones_framework::prelude::*;
323 /// use std::time::Duration;
324 /// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
325 /// timer.tick(Duration::from_secs_f32(0.5));
326 /// assert_eq!(timer.percent(), 0.25);
327 /// ```
328 #[inline]
329 pub fn percent(&self) -> f32 {
330 self.elapsed().as_secs_f32() / self.duration().as_secs_f32()
331 }
332
333 /// Returns the percentage of the timer remaining time (goes from 1.0 to 0.0).
334 ///
335 /// # Examples
336 /// ```no_run
337 /// # use bones_framework::prelude::*;
338 /// use std::time::Duration;
339 /// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
340 /// timer.tick(Duration::from_secs_f32(0.5));
341 /// assert_eq!(timer.percent_left(), 0.75);
342 /// ```
343 #[inline]
344 pub fn percent_left(&self) -> f32 {
345 1.0 - self.percent()
346 }
347
348 /// Returns the remaining time in seconds
349 ///
350 /// # Examples
351 /// ```no_run
352 /// # use bones_framework::prelude::*;
353 /// use std::cmp::Ordering;
354 /// use std::time::Duration;
355 /// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
356 /// timer.tick(Duration::from_secs_f32(0.5));
357 /// let result = timer.remaining_secs().total_cmp(&1.5);
358 /// assert_eq!(Ordering::Equal, result);
359 /// ```
360 #[inline]
361 pub fn remaining_secs(&self) -> f32 {
362 self.remaining().as_secs_f32()
363 }
364
365 /// Returns the remaining time using Duration
366 ///
367 /// # Examples
368 /// ```no_run
369 /// # use bones_framework::prelude::*;
370 /// use std::time::Duration;
371 /// let mut timer = Timer::from_seconds(2.0, TimerMode::Once);
372 /// timer.tick(Duration::from_secs_f32(0.5));
373 /// assert_eq!(timer.remaining(), Duration::from_secs_f32(1.5));
374 /// ```
375 #[inline]
376 pub fn remaining(&self) -> Duration {
377 self.duration() - self.elapsed()
378 }
379
380 /// Returns the number of times a repeating timer
381 /// finished during the last [`tick`](Timer<T>::tick) call.
382 ///
383 /// For non repeating-timers, this method will only ever
384 /// return 0 or 1.
385 ///
386 /// # Examples
387 /// ```no_run
388 /// # use bones_framework::prelude::*;
389 /// use std::time::Duration;
390 /// let mut timer = Timer::from_seconds(1.0, TimerMode::Repeating);
391 /// timer.tick(Duration::from_secs_f32(6.0));
392 /// assert_eq!(timer.times_finished_this_tick(), 6);
393 /// timer.tick(Duration::from_secs_f32(2.0));
394 /// assert_eq!(timer.times_finished_this_tick(), 2);
395 /// timer.tick(Duration::from_secs_f32(0.5));
396 /// assert_eq!(timer.times_finished_this_tick(), 0);
397 /// ```
398 #[inline]
399 pub fn times_finished_this_tick(&self) -> u32 {
400 self.times_finished_this_tick
401 }
402}
403
404/// Specifies [`Timer`] behavior.
405#[derive(
406 Debug, Clone, Copy, Eq, PartialEq, Hash, Default, serde::Deserialize, serde::Serialize,
407)]
408pub enum TimerMode {
409 /// Run once and stop.
410 #[default]
411 Once,
412 /// Reset when finished.
413 Repeating,
414}
415
416// To speed up CI, only do these on miri, where they complete without waiting for time to pass.
417#[cfg(all(test, miri))]
418#[allow(clippy::float_cmp)]
419mod tests {
420 use super::*;
421
422 #[test]
423 fn non_repeating_timer() {
424 let mut t = Timer::from_seconds(10.0, TimerMode::Once);
425 // Tick once, check all attributes
426 t.tick(Duration::from_secs_f32(0.25));
427 assert_eq!(t.elapsed_secs(), 0.25);
428 assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
429 assert!(!t.finished());
430 assert!(!t.just_finished());
431 assert_eq!(t.times_finished_this_tick(), 0);
432 assert_eq!(t.mode(), TimerMode::Once);
433 assert_eq!(t.percent(), 0.025);
434 assert_eq!(t.percent_left(), 0.975);
435 // Ticking while paused changes nothing
436 t.pause();
437 t.tick(Duration::from_secs_f32(500.0));
438 assert_eq!(t.elapsed_secs(), 0.25);
439 assert_eq!(t.duration(), Duration::from_secs_f32(10.0));
440 assert!(!t.finished());
441 assert!(!t.just_finished());
442 assert_eq!(t.times_finished_this_tick(), 0);
443 assert_eq!(t.mode(), TimerMode::Once);
444 assert_eq!(t.percent(), 0.025);
445 assert_eq!(t.percent_left(), 0.975);
446 // Tick past the end and make sure elapsed doesn't go past 0.0 and other things update
447 t.unpause();
448 t.tick(Duration::from_secs_f32(500.0));
449 assert_eq!(t.elapsed_secs(), 10.0);
450 assert!(t.finished());
451 assert!(t.just_finished());
452 assert_eq!(t.times_finished_this_tick(), 1);
453 assert_eq!(t.percent(), 1.0);
454 assert_eq!(t.percent_left(), 0.0);
455 // Continuing to tick when finished should only change just_finished
456 t.tick(Duration::from_secs_f32(1.0));
457 assert_eq!(t.elapsed_secs(), 10.0);
458 assert!(t.finished());
459 assert!(!t.just_finished());
460 assert_eq!(t.times_finished_this_tick(), 0);
461 assert_eq!(t.percent(), 1.0);
462 assert_eq!(t.percent_left(), 0.0);
463 }
464
465 #[test]
466 fn repeating_timer() {
467 let mut t = Timer::from_seconds(2.0, TimerMode::Repeating);
468 // Tick once, check all attributes
469 t.tick(Duration::from_secs_f32(0.75));
470 assert_eq!(t.elapsed_secs(), 0.75);
471 assert_eq!(t.duration(), Duration::from_secs_f32(2.0));
472 assert!(!t.finished());
473 assert!(!t.just_finished());
474 assert_eq!(t.times_finished_this_tick(), 0);
475 assert_eq!(t.mode(), TimerMode::Repeating);
476 assert_eq!(t.percent(), 0.375);
477 assert_eq!(t.percent_left(), 0.625);
478 // Tick past the end and make sure elapsed wraps
479 t.tick(Duration::from_secs_f32(1.5));
480 assert_eq!(t.elapsed_secs(), 0.25);
481 assert!(t.finished());
482 assert!(t.just_finished());
483 assert_eq!(t.times_finished_this_tick(), 1);
484 assert_eq!(t.percent(), 0.125);
485 assert_eq!(t.percent_left(), 0.875);
486 // Continuing to tick should turn off both finished & just_finished for repeating timers
487 t.tick(Duration::from_secs_f32(1.0));
488 assert_eq!(t.elapsed_secs(), 1.25);
489 assert!(!t.finished());
490 assert!(!t.just_finished());
491 assert_eq!(t.times_finished_this_tick(), 0);
492 assert_eq!(t.percent(), 0.625);
493 assert_eq!(t.percent_left(), 0.375);
494 }
495
496 #[test]
497 fn times_finished_repeating() {
498 let mut t = Timer::from_seconds(1.0, TimerMode::Repeating);
499 assert_eq!(t.times_finished_this_tick(), 0);
500 t.tick(Duration::from_secs_f32(3.5));
501 assert_eq!(t.times_finished_this_tick(), 3);
502 assert_eq!(t.elapsed_secs(), 0.5);
503 assert!(t.finished());
504 assert!(t.just_finished());
505 t.tick(Duration::from_secs_f32(0.2));
506 assert_eq!(t.times_finished_this_tick(), 0);
507 }
508
509 #[test]
510 fn times_finished_this_tick() {
511 let mut t = Timer::from_seconds(1.0, TimerMode::Once);
512 assert_eq!(t.times_finished_this_tick(), 0);
513 t.tick(Duration::from_secs_f32(1.5));
514 assert_eq!(t.times_finished_this_tick(), 1);
515 t.tick(Duration::from_secs_f32(0.5));
516 assert_eq!(t.times_finished_this_tick(), 0);
517 }
518
519 #[test]
520 fn times_finished_this_tick_precise() {
521 let mut t = Timer::from_seconds(0.01, TimerMode::Repeating);
522 let duration = Duration::from_secs_f64(0.333);
523
524 // total duration: 0.333 => 33 times finished
525 t.tick(duration);
526 assert_eq!(t.times_finished_this_tick(), 33);
527 // total duration: 0.666 => 33 times finished
528 t.tick(duration);
529 assert_eq!(t.times_finished_this_tick(), 33);
530 // total duration: 0.999 => 33 times finished
531 t.tick(duration);
532 assert_eq!(t.times_finished_this_tick(), 33);
533 // total duration: 1.332 => 34 times finished
534 t.tick(duration);
535 assert_eq!(t.times_finished_this_tick(), 34);
536 }
537
538 #[test]
539 fn paused() {
540 let mut t = Timer::from_seconds(10.0, TimerMode::Once);
541
542 t.tick(Duration::from_secs_f32(10.0));
543 assert!(t.just_finished());
544 assert!(t.finished());
545 // A paused timer should change just_finished to false after a tick
546 t.pause();
547 t.tick(Duration::from_secs_f32(5.0));
548 assert!(!t.just_finished());
549 assert!(t.finished());
550 }
551
552 #[test]
553 fn paused_repeating() {
554 let mut t = Timer::from_seconds(10.0, TimerMode::Repeating);
555
556 t.tick(Duration::from_secs_f32(10.0));
557 assert!(t.just_finished());
558 assert!(t.finished());
559 // A paused repeating timer should change finished and just_finished to false after a tick
560 t.pause();
561 t.tick(Duration::from_secs_f32(5.0));
562 assert!(!t.just_finished());
563 assert!(!t.finished());
564 }
565}