simple_game_utils/
timing.rs

1#[cfg(feature = "serde")]
2use serde::{Deserialize, Serialize};
3use std::time::Instant;
4
5/// Used for single or repeated timed events, uses fractional seconds
6///
7/// # Update
8/// When using [Timer::update] or [Timer::update_secs], note that
9/// * for timers created with [Timer::new_once] those methods will continue to return true until [Timer::reset] is called
10/// * other timers will only return true once per duration then automatically reset
11///
12/// # Usage
13///
14/// ```
15///# use simple_game_utils::timing::Timer;
16///# let delta = 0.1;
17///let mut  timer = Timer::new_with_delay(1.0, 1.0);
18///loop {
19///    if timer.update_secs(delta) {
20///         break;
21///    }
22///}
23/// ```
24#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
25#[derive(Debug, Clone, PartialEq)]
26pub struct Timer {
27    /// amount of time remaining
28    remaining: f64,
29    /// amount of time to reset to once `remaining` <= 0
30    reset: f64,
31    /// if the timer should automatically reset
32    looping: bool,
33}
34
35impl Timer {
36    /// Create a timer that will wait `delay` seconds then trigger every `duration` seconds
37    ///
38    /// # Usage
39    ///
40    /// `Timer::new_with_delay(2.0, 0.1)` will wait 2s then trigger every 0.1s
41    pub fn new_with_delay(delay: f64, duration: f64) -> Self {
42        Self {
43            remaining: delay,
44            reset: duration,
45            looping: true,
46        }
47    }
48
49    /// Create a timer that will trigger every `duration` seconds
50    ///
51    /// # Usage
52    ///
53    /// `Timer::new(0.1)` will trigger immediately and then every 0.1s
54    pub fn new(duration: f64) -> Self {
55        Self {
56            remaining: 0.0,
57            reset: duration,
58            looping: true,
59        }
60    }
61
62    /// Create a timer with the duration of `after` seconds that only triggers once
63    /// Use `Timer::reset` to reset back to 0 and start counting again
64    pub fn new_once(after: f64) -> Self {
65        Self {
66            remaining: after,
67            reset: after,
68            looping: false,
69        }
70    }
71}
72
73impl Timer {
74    /// Update timer, returns true if triggered
75    #[inline]
76    pub fn update(&mut self, timing: &Timing) -> bool {
77        self.update_secs(timing.fixed_time_step)
78    }
79
80    /// Update timer, returns true if triggered
81    /// `delta` is fractional seconds passed since last call
82    pub fn update_secs(&mut self, delta: f64) -> bool {
83        self.remaining -= delta;
84        let triggered = self.remaining <= 0.0;
85        if triggered && self.looping {
86            self.remaining = self.reset;
87        }
88        triggered
89    }
90
91    /// Set remaining to reset value (the number passed into the constructor)
92    /// One time loops can trigger again after calling this
93    #[inline]
94    pub fn reset(&mut self) {
95        self.remaining = self.reset;
96    }
97
98    /// If the timer has reached 0, this will always be false for looping timers (unless reset is <= 0.0)
99    #[inline]
100    pub fn has_triggered(&self) -> bool {
101        self.remaining <= 0.0
102    }
103
104    /// Set remaining time to 0, triggering the timer
105    #[inline]
106    pub fn trigger(&mut self) {
107        self.remaining = 0.0;
108    }
109
110    /// Add `seconds` to remaining time, this can be higher than the duration
111    #[inline]
112    pub fn delay(&mut self, seconds: f64) {
113        self.remaining += seconds;
114    }
115}
116
117/// Used to track time in games
118#[derive(Debug, Clone, PartialEq)]
119pub struct Timing {
120    /// amount of time that has passed since last
121    pub delta: f64,
122    /// when execution started
123    pub started_at: Instant,
124    /// time at start of frame
125    pub now: Instant,
126    /// time at start of last frame
127    pub last: Instant,
128    /// number of updates so far
129    pub updates: usize,
130    /// number of renders so far
131    pub renders: usize,
132    pub accumulated_time: f64,
133    max_render_time: f64,
134    /// an fps independent value used to update animations, etc
135    pub fixed_time_step: f64,
136    /// an fps independent value used to update animations, etc
137    pub fixed_time_step_f32: f32,
138    /// FPS
139    pub stats: Stats,
140}
141
142#[derive(Debug, Clone, Eq, PartialEq)]
143pub struct Stats {
144    /// The number of frames shown in the last second
145    pub fps: usize,
146    /// Used to calculate fps
147    pub last_frame_count: usize,
148    /// Used to calculate fps
149    pub last_frame_check: Instant,
150}
151
152impl Timing {
153    pub fn new(speed: usize) -> Timing {
154        Timing {
155            delta: 0.0,
156            started_at: Instant::now(),
157            now: Instant::now(),
158            last: Instant::now(),
159            updates: 0,
160            renders: 0,
161            accumulated_time: 0.0,
162            max_render_time: 0.1,
163            fixed_time_step: 1.0 / (speed as f64),
164            fixed_time_step_f32: 1.0 / (speed as f32),
165            stats: Stats {
166                fps: 0,
167                last_frame_count: 0,
168                last_frame_check: Instant::now(),
169            },
170        }
171    }
172
173    pub fn update_fps(&mut self) {
174        if self
175            .now
176            .duration_since(self.stats.last_frame_check)
177            .as_secs_f32()
178            >= 1.0
179        {
180            self.stats.fps = self.renders - self.stats.last_frame_count;
181            self.stats.last_frame_check = self.now;
182            self.stats.last_frame_count = self.renders;
183        }
184    }
185
186    pub fn update(&mut self) {
187        self.now = Instant::now();
188        self.delta = self.now.duration_since(self.last).as_secs_f64();
189        self.accumulated_time += self.delta;
190        if self.delta > self.max_render_time {
191            self.delta = self.max_render_time;
192        }
193    }
194}
195
196#[cfg(test)]
197mod test {
198    use crate::timing::Timer;
199
200    #[test]
201    fn basic_test_delayed() {
202        let mut timer = Timer::new_with_delay(1.0, 1.0);
203        assert!(!timer.update_secs(0.4));
204        assert!(!timer.update_secs(0.4));
205        assert!(timer.update_secs(0.4));
206        assert!(!timer.has_triggered());
207    }
208
209    #[test]
210    fn basic_test() {
211        let mut timer = Timer::new(0.5);
212        assert!(timer.has_triggered());
213        timer.reset();
214        assert!(!timer.has_triggered());
215        assert!(!timer.update_secs(0.4));
216        assert!(timer.update_secs(0.4));
217        assert!(!timer.has_triggered());
218    }
219}