time_tick/
lib.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/piot/time-tick
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5
6//! # Time Tick
7//!
8//! `time-tick` is a Rust crate designed to manage and calculate time intervals for game loops.
9//! It provides a simple interface to track elapsed time and determine how many ticks should
10//! be processed in a single update cycle. This is particularly useful in games or simulations
11//! that require consistent timing for updates and actions.
12//!
13//! ## Features
14//! - Track time intervals for game updates.
15//! - Specify tick durations and maximum ticks per update.
16//! - Reset and adjust tick timing dynamically.
17//! ## Usage
18//!
19//! To use this crate, add the following to your `Cargo.toml`:
20//!
21//! ```toml
22//! [dependencies]
23//! time-tick = "0.1" // Replace with the latest version
24//! ```
25
26use monotonic_time_rs::{Millis, MillisDuration};
27
28/// A struct to manage and calculate the number of ticks for a game loop based on elapsed time.
29///
30/// The `TimeTick` struct is designed to track time intervals for game updates, allowing you to
31/// specify a duration for each tick and the maximum number of ticks that can occur in a single
32/// update. This is particularly useful for games that require consistent timing for game logic
33/// updates.
34#[derive(Debug)]
35pub struct TimeTick {
36    tick_duration: MillisDuration,
37    consumed_absolute_time: Millis,
38    max_tick_per_update: u16,
39}
40
41impl TimeTick {
42    /// Creates a new `TimeTick` instance.
43    ///
44    /// # Arguments
45    ///
46    /// * `now` - The current absolute time in milliseconds.
47    /// * `tick_duration` - The duration of each tick in milliseconds.
48    /// * `max_tick_per_update` - The maximum number of ticks that can be processed in a single update.
49    ///
50    /// # Returns
51    ///
52    /// A new instance of `TimeTick` initialized with the provided values.
53    pub const fn new(now: Millis, tick_duration: MillisDuration, max_tick_per_update: u16) -> Self {
54        TimeTick {
55            tick_duration,
56            consumed_absolute_time: now,
57            max_tick_per_update,
58        }
59    }
60
61    /// Sets a new tick duration.
62    ///
63    /// This method allows you to change the duration of each tick dynamically.
64    ///
65    /// # Arguments
66    ///
67    /// * `tick_duration` - The new duration for each tick in milliseconds.
68    pub fn set_tick_duration(&mut self, tick_duration: MillisDuration) {
69        self.tick_duration = tick_duration;
70    }
71
72    /// Resets the consumed absolute time to the current time.
73    ///
74    /// This method is useful when you want to restart the timing calculations, for example,
75    /// when restarting a game or resetting the state.
76    ///
77    /// # Arguments
78    ///
79    /// * `now` - The current absolute time in milliseconds to reset to.
80    pub fn reset(&mut self, now: Millis) {
81        self.consumed_absolute_time = now;
82    }
83
84    /// Calculates the number of ticks that should be performed based on the elapsed time since the last update.
85    ///
86    /// This method should be called each frame in the game loop. It computes how many ticks can
87    /// be processed based on the time that has passed since the last update, clamping the result
88    /// to the maximum ticks allowed per update.
89    ///
90    /// # Arguments
91    ///
92    /// * `now` - The current absolute time in milliseconds.
93    ///
94    /// # Returns
95    ///
96    /// The number of ticks that should be performed this frame.
97    #[inline]
98    pub fn calculate_ticks(&mut self, now: Millis) -> u16 {
99        let time_ahead = now - self.consumed_absolute_time;
100        let tick_count = (time_ahead.as_millis() / self.tick_duration.as_millis()) as u16;
101        //        trace!("time ahead is: {time_ahead} tick_count:{tick_count}");
102        if tick_count >= self.max_tick_per_update {
103            self.max_tick_per_update
104        } else {
105            tick_count
106        }
107    }
108
109    /// Updates the consumed absolute time based on the number of ticks performed.
110    ///
111    /// This method should be called after processing the ticks to adjust the internal timer
112    /// accordingly. It calculates the new consumed absolute time based on the ticks that have
113    /// been processed in this update.
114    ///
115    /// In most cases, the value passed to this method should be the same as the number of ticks
116    /// returned by `calculate_ticks`. However, it can be a lower value if fewer ticks were actually
117    /// processed (e.g., due to frame rate limitations or logic constraints). It is important to note
118    /// that this value must not exceed the number of ticks returned by `calculate_ticks`.
119    ///
120    /// # Arguments
121    ///
122    /// * `tick_count` - The number of ticks that were performed during this update. This value should
123    ///   typically match the value returned by `calculate_ticks`, but can be lower in certain cases.
124    #[inline]
125    pub fn performed_ticks(&mut self, tick_count: u16) {
126        self.consumed_absolute_time +=
127            MillisDuration::from_millis(tick_count as u64 * self.tick_duration.as_millis())
128    }
129}