chron/
clock.rs

1//! Provides the game loop iterator and related types.
2
3use std::num;
4use std::thread;
5use std::time;
6
7/// The game loop iterator, whichs emits [`Tick`] events.
8///
9/// # Examples
10///
11/// See the [crate-level documentation](crate#examples).
12#[must_use = "iterators are lazy and do nothing unless consumed"]
13#[derive(Debug)]
14pub struct Clock {
15    time_per_update: time::Duration,
16    time_per_render: Option<time::Duration>,
17    max_updates_per_frame: Option<usize>,
18    updates: usize,
19
20    previous_update_time: time::Instant,
21    previous_render_time: time::Instant,
22}
23
24impl Clock {
25    /// Creates a new `Clock`.
26    ///
27    /// # Examples
28    ///
29    /// ```
30    /// use chron::Clock;
31    /// use std::num::NonZeroU32;
32    ///
33    /// # fn main() { test().unwrap(); }
34    /// # fn test() -> Option<()> {
35    /// let updates_per_second = NonZeroU32::new(50)?;
36    /// let clock = Clock::new(updates_per_second);
37    /// # Some(())
38    /// # }
39    /// ```
40    #[inline]
41    pub fn new(updates_per_second: num::NonZeroU32) -> Self {
42        let time_per_update = time::Duration::from_secs(1) / updates_per_second.get();
43
44        let now = time::Instant::now();
45
46        Clock {
47            time_per_update,
48            time_per_render: None,
49            max_updates_per_frame: None,
50
51            previous_update_time: now - time_per_update,
52            previous_render_time: now,
53            updates: 0,
54        }
55    }
56
57    /// Sets the maximum frame rate.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// use chron::Clock;
63    /// use std::num::NonZeroU32;
64    ///
65    /// # fn main() { test().unwrap(); }
66    /// # fn test() -> Option<()> {
67    /// let updates_per_second = NonZeroU32::new(50)?;
68    /// let frames_per_second = NonZeroU32::new(60)?;
69    ///
70    /// let clock = Clock::new(updates_per_second)
71    ///     .max_frame_rate(frames_per_second);
72    /// # Some(())
73    /// # }
74    /// ```
75    #[inline]
76    pub fn max_frame_rate(mut self, frames_per_second: num::NonZeroU32) -> Self {
77        let time_per_render = time::Duration::from_secs(1) / frames_per_second.get();
78        self.time_per_render = Some(time_per_render);
79        self.previous_render_time -= time_per_render;
80
81        self
82    }
83
84    /// See [`Clock::max_frame_rate`].
85    #[deprecated(note = "use `Clock::max_frame_rate` instead")]
86    #[inline]
87    pub fn with_frame_limit(self, frames_per_second: num::NonZeroU32) -> Self {
88        self.max_frame_rate(frames_per_second)
89    }
90
91    /// Sets the maximum number of updates to be emitted in between frames.
92    ///
93    /// When the loop cannot maintain the specified *updates per second* (e.g.
94    /// because updates are taking too long) it has to play catch-up by only
95    /// emitting updates and **no** renders. This setting limits how many
96    /// updates are emitted before a render is enforced. It prevents the game
97    /// from freezing up, at the cost of slowing down even more.
98    ///
99    /// # Examples
100    ///
101    /// ```
102    /// use chron::Clock;
103    /// use std::num::NonZeroU32;
104    ///
105    /// # fn main() { test().unwrap(); }
106    /// # fn test() -> Option<()> {
107    /// let updates_per_second = NonZeroU32::new(50)?;
108    ///
109    /// let clock = Clock::new(updates_per_second)
110    ///     .max_updates_per_frame(3);
111    /// # Some(())
112    /// # }
113    /// ```
114    #[inline]
115    pub fn max_updates_per_frame(mut self, max_updates_per_frame: usize) -> Self {
116        self.max_updates_per_frame = Some(max_updates_per_frame);
117
118        self
119    }
120
121    /// See [`Clock::max_updates_per_frame`].
122    #[deprecated(note = "use `Clock::max_updates_per_frame` instead")]
123    #[inline]
124    pub fn with_frame_skip(self, frame_skip: usize) -> Self {
125        self.max_updates_per_frame(frame_skip)
126    }
127}
128
129impl Iterator for Clock {
130    type Item = Tick;
131
132    #[inline]
133    fn next(&mut self) -> Option<Self::Item> {
134        let mut now = time::Instant::now();
135
136        if let Some(time_per_render) = self.time_per_render {
137            if now - self.previous_render_time < time_per_render
138                && now - self.previous_update_time < self.time_per_update
139            {
140                let sleep_time = std::cmp::min(
141                    self.previous_render_time + time_per_render - now,
142                    self.previous_update_time + self.time_per_update - now,
143                );
144
145                thread::sleep(sleep_time);
146
147                now += sleep_time;
148            }
149        }
150
151        if now - self.previous_update_time >= self.time_per_update
152            && self.updates < self.max_updates_per_frame.unwrap_or(usize::MAX)
153        {
154            self.previous_update_time += self.time_per_update;
155            self.updates += 1;
156
157            Some(Tick::Update)
158        } else {
159            self.previous_render_time = now;
160            self.updates = 0;
161
162            let interpolation = (now - self.previous_update_time).as_secs_f32()
163                / self.time_per_update.as_secs_f32();
164
165            Some(Tick::Render { interpolation })
166        }
167    }
168}
169
170/// A game loop event.
171#[derive(Clone, Copy, Debug)]
172pub enum Tick {
173    /// Indicates that it is time for an update to the game logic.
174    Update,
175
176    /// Indicates that it is time to render the game.
177    Render {
178        /// Indicates how much time elapsed between the previous update and the
179        /// next one.
180        interpolation: f32,
181    },
182}