Skip to main content

quicksilver/
timer.rs

1use core::num::NonZeroUsize;
2use instant::Instant;
3use std::time::Duration;
4
5#[derive(Debug, Clone)]
6/// A timer that you can use to fix the time between actions, for example updates or draw calls.
7///
8/// See the article [Fix Your Timestep](https://gafferongames.com/post/fix_your_timestep/) for more
9/// on how to use timers to ensure framerate-independence.
10pub struct Timer {
11    period: Duration,
12    init: Instant,
13}
14
15impl Timer {
16    /// Create a timer that ticks n many times per second
17    pub fn time_per_second(times: f32) -> Timer {
18        Timer::with_duration(Duration::from_secs_f32(1.0 / times))
19    }
20
21    /// Create a timer with a given period (time between ticks)
22    pub fn with_duration(period: Duration) -> Timer {
23        Timer {
24            period,
25            init: Instant::now(),
26        }
27    }
28
29    /// Look if the time has elapsed and if so, starts the countdown for the next tick.
30    ///
31    /// You can use a while loop instead of an if to catch up in the event that you were late. Each
32    /// tick will only 'consume' one period worth of time.
33    pub fn tick(&mut self) -> bool {
34        if self.init.elapsed() >= self.period {
35            self.init += self.period;
36            true
37        } else {
38            false
39        }
40    }
41
42    /// Similar to Self::tick() but tells you how many ticks have passed, rather than just if a tick has passed.
43    ///
44    /// This is useful in situations where catching up isn't needed or possible, like rendering to
45    /// the screen. If you've missed rendering three frames, there's no point in drawing them now:
46    /// just render the current state and move on.
47    pub fn exhaust(&mut self) -> Option<NonZeroUsize> {
48        let mut count = 0;
49        while self.tick() {
50            count += 1;
51        }
52        NonZeroUsize::new(count)
53    }
54
55    /// Resets the timer to count from this moment.
56    ///
57    /// This is the same as creating a new Timer with the same period
58    pub fn reset(&mut self) {
59        self.init = Instant::now();
60    }
61
62    /// Gets the time in between ticks
63    pub fn period(&self) -> Duration {
64        self.period
65    }
66
67    /// How much time has passed since the timer was last ticked
68    pub fn elapsed(&self) -> Duration {
69        self.init.elapsed()
70    }
71
72    /// Look how much time is still left before its time for next tick.
73    pub fn remaining(&self) -> Option<Duration> {
74        self.period.checked_sub(self.init.elapsed())
75    }
76
77    /// Look how late you are with calling Timer::tick() if you would call it right now
78    pub fn late_by(&self) -> Option<Duration> {
79        self.init.elapsed().checked_sub(self.period)
80    }
81}