bevy_time/
virt.rs

1#[cfg(feature = "bevy_reflect")]
2use bevy_reflect::Reflect;
3use core::time::Duration;
4use log::debug;
5
6use crate::{real::Real, time::Time};
7
8/// The virtual game clock representing game time.
9///
10/// A specialization of the [`Time`] structure. **For method documentation, see
11/// [`Time<Virtual>#impl-Time<Virtual>`].**
12///
13/// Normally used as `Time<Virtual>`. It is automatically inserted as a resource
14/// by [`TimePlugin`](crate::TimePlugin) and updated based on
15/// [`Time<Real>`](Real). The virtual clock is automatically set as the default
16/// generic [`Time`] resource for the update.
17///
18/// The virtual clock differs from real time clock in that it can be paused, sped up
19/// and slowed down. It also limits how much it can advance in a single update
20/// in order to prevent unexpected behavior in cases where updates do not happen
21/// at regular intervals (e.g. coming back after the program was suspended a long time).
22///
23/// The virtual clock can be paused by calling [`pause()`](Time::pause) and
24/// unpaused by calling [`unpause()`](Time::unpause). When the game clock is
25/// paused [`delta()`](Time::delta) will be zero on each update, and
26/// [`elapsed()`](Time::elapsed) will not grow.
27/// [`effective_speed()`](Time::effective_speed) will return `0.0`. Calling
28/// [`pause()`](Time::pause) will not affect value the [`delta()`](Time::delta)
29/// value for the update currently being processed.
30///
31/// The speed of the virtual clock can be changed by calling
32/// [`set_relative_speed()`](Time::set_relative_speed). A value of `2.0` means
33/// that virtual clock should advance twice as fast as real time, meaning that
34/// [`delta()`](Time::delta) values will be double of what
35/// [`Time<Real>::delta()`](Time::delta) reports and
36/// [`elapsed()`](Time::elapsed) will go twice as fast as
37/// [`Time<Real>::elapsed()`](Time::elapsed). Calling
38/// [`set_relative_speed()`](Time::set_relative_speed) will not affect the
39/// [`delta()`](Time::delta) value for the update currently being processed.
40///
41/// The maximum amount of delta time that can be added by a single update can be
42/// set by [`set_max_delta()`](Time::set_max_delta). This value serves a dual
43/// purpose in the virtual clock.
44///
45/// If the game temporarily freezes due to any reason, such as disk access, a
46/// blocking system call, or operating system level suspend, reporting the full
47/// elapsed delta time is likely to cause bugs in game logic. Usually if a
48/// laptop is suspended for an hour, it doesn't make sense to try to simulate
49/// the game logic for the elapsed hour when resuming. Instead it is better to
50/// lose the extra time and pretend a shorter duration of time passed. Setting
51/// [`max_delta()`](Time::max_delta) to a relatively short time means that the
52/// impact on game logic will be minimal.
53///
54/// If the game lags for some reason, meaning that it will take a longer time to
55/// compute a frame than the real time that passes during the computation, then
56/// we would fall behind in processing virtual time. If this situation persists,
57/// and computing a frame takes longer depending on how much virtual time has
58/// passed, the game would enter a "death spiral" where computing each frame
59/// takes longer and longer and the game will appear to freeze. By limiting the
60/// maximum time that can be added at once, we also limit the amount of virtual
61/// time the game needs to compute for each frame. This means that the game will
62/// run slow, and it will run slower than real time, but it will not freeze and
63/// it will recover as soon as computation becomes fast again.
64///
65/// You should set [`max_delta()`](Time::max_delta) to a value that is
66/// approximately the minimum FPS your game should have even if heavily lagged
67/// for a moment. The actual FPS when lagged will be somewhat lower than this,
68/// depending on how much more time it takes to compute a frame compared to real
69/// time. You should also consider how stable your FPS is, as the limit will
70/// also dictate how big of an FPS drop you can accept without losing time and
71/// falling behind real time.
72#[derive(Debug, Copy, Clone)]
73#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone))]
74pub struct Virtual {
75    max_delta: Duration,
76    paused: bool,
77    relative_speed: f64,
78    effective_speed: f64,
79}
80
81impl Time<Virtual> {
82    /// The default amount of time that can added in a single update.
83    ///
84    /// Equal to 250 milliseconds.
85    const DEFAULT_MAX_DELTA: Duration = Duration::from_millis(250);
86
87    /// Create new virtual clock with given maximum delta step [`Duration`]
88    ///
89    /// # Panics
90    ///
91    /// Panics if `max_delta` is zero.
92    pub fn from_max_delta(max_delta: Duration) -> Self {
93        let mut ret = Self::default();
94        ret.set_max_delta(max_delta);
95        ret
96    }
97
98    /// Returns the maximum amount of time that can be added to this clock by a
99    /// single update, as [`Duration`].
100    ///
101    /// This is the maximum value [`Self::delta()`] will return and also to
102    /// maximum time [`Self::elapsed()`] will be increased by in a single
103    /// update.
104    ///
105    /// This ensures that even if no updates happen for an extended amount of time,
106    /// the clock will not have a sudden, huge advance all at once. This also indirectly
107    /// limits the maximum number of fixed update steps that can run in a single update.
108    ///
109    /// The default value is 250 milliseconds.
110    #[inline]
111    pub fn max_delta(&self) -> Duration {
112        self.context().max_delta
113    }
114
115    /// Sets the maximum amount of time that can be added to this clock by a
116    /// single update, as [`Duration`].
117    ///
118    /// This is the maximum value [`Self::delta()`] will return and also to
119    /// maximum time [`Self::elapsed()`] will be increased by in a single
120    /// update.
121    ///
122    /// This is used to ensure that even if the game freezes for a few seconds,
123    /// or is suspended for hours or even days, the virtual clock doesn't
124    /// suddenly jump forward for that full amount, which would likely cause
125    /// gameplay bugs or having to suddenly simulate all the intervening time.
126    ///
127    /// If no updates happen for an extended amount of time, this limit prevents
128    /// having a sudden, huge advance all at once. This also indirectly limits
129    /// the maximum number of fixed update steps that can run in a single
130    /// update.
131    ///
132    /// The default value is 250 milliseconds. If you want to disable this
133    /// feature, set the value to [`Duration::MAX`].
134    ///
135    /// # Panics
136    ///
137    /// Panics if `max_delta` is zero.
138    #[inline]
139    pub fn set_max_delta(&mut self, max_delta: Duration) {
140        assert_ne!(max_delta, Duration::ZERO, "tried to set max delta to zero");
141        self.context_mut().max_delta = max_delta;
142    }
143
144    /// Returns the speed the clock advances relative to your system clock, as [`f32`].
145    /// This is known as "time scaling" or "time dilation" in other engines.
146    #[inline]
147    pub fn relative_speed(&self) -> f32 {
148        self.relative_speed_f64() as f32
149    }
150
151    /// Returns the speed the clock advances relative to your system clock, as [`f64`].
152    /// This is known as "time scaling" or "time dilation" in other engines.
153    #[inline]
154    pub fn relative_speed_f64(&self) -> f64 {
155        self.context().relative_speed
156    }
157
158    /// Returns the speed the clock advanced relative to your system clock in
159    /// this update, as [`f32`].
160    ///
161    /// Returns `0.0` if the game was paused or what the `relative_speed` value
162    /// was at the start of this update.
163    #[inline]
164    pub fn effective_speed(&self) -> f32 {
165        self.context().effective_speed as f32
166    }
167
168    /// Returns the speed the clock advanced relative to your system clock in
169    /// this update, as [`f64`].
170    ///
171    /// Returns `0.0` if the game was paused or what the `relative_speed` value
172    /// was at the start of this update.
173    #[inline]
174    pub fn effective_speed_f64(&self) -> f64 {
175        self.context().effective_speed
176    }
177
178    /// Sets the speed the clock advances relative to your system clock, given as an [`f32`].
179    ///
180    /// For example, setting this to `2.0` will make the clock advance twice as fast as your system
181    /// clock.
182    ///
183    /// # Panics
184    ///
185    /// Panics if `ratio` is negative or not finite.
186    #[inline]
187    pub fn set_relative_speed(&mut self, ratio: f32) {
188        self.set_relative_speed_f64(ratio as f64);
189    }
190
191    /// Sets the speed the clock advances relative to your system clock, given as an [`f64`].
192    ///
193    /// For example, setting this to `2.0` will make the clock advance twice as fast as your system
194    /// clock.
195    ///
196    /// # Panics
197    ///
198    /// Panics if `ratio` is negative or not finite.
199    #[inline]
200    pub fn set_relative_speed_f64(&mut self, ratio: f64) {
201        assert!(ratio.is_finite(), "tried to go infinitely fast");
202        assert!(ratio >= 0.0, "tried to go back in time");
203        self.context_mut().relative_speed = ratio;
204    }
205
206    /// Stops the clock, preventing it from advancing until resumed.
207    #[inline]
208    pub fn pause(&mut self) {
209        self.context_mut().paused = true;
210    }
211
212    /// Resumes the clock if paused.
213    #[inline]
214    pub fn unpause(&mut self) {
215        self.context_mut().paused = false;
216    }
217
218    /// Returns `true` if the clock is currently paused.
219    #[inline]
220    pub fn is_paused(&self) -> bool {
221        self.context().paused
222    }
223
224    /// Returns `true` if the clock was paused at the start of this update.
225    #[inline]
226    pub fn was_paused(&self) -> bool {
227        self.context().effective_speed == 0.0
228    }
229
230    /// Updates the elapsed duration of `self` by `raw_delta`, up to the `max_delta`.
231    fn advance_with_raw_delta(&mut self, raw_delta: Duration) {
232        let max_delta = self.context().max_delta;
233        let clamped_delta = if raw_delta > max_delta {
234            debug!(
235                "delta time larger than maximum delta, clamping delta to {:?} and skipping {:?}",
236                max_delta,
237                raw_delta - max_delta
238            );
239            max_delta
240        } else {
241            raw_delta
242        };
243        let effective_speed = if self.context().paused {
244            0.0
245        } else {
246            self.context().relative_speed
247        };
248        let delta = if effective_speed != 1.0 {
249            clamped_delta.mul_f64(effective_speed)
250        } else {
251            // avoid rounding when at normal speed
252            clamped_delta
253        };
254        self.context_mut().effective_speed = effective_speed;
255        self.advance_by(delta);
256    }
257}
258
259impl Default for Virtual {
260    fn default() -> Self {
261        Self {
262            max_delta: Time::<Virtual>::DEFAULT_MAX_DELTA,
263            paused: false,
264            relative_speed: 1.0,
265            effective_speed: 1.0,
266        }
267    }
268}
269
270/// Advances [`Time<Virtual>`] and [`Time`] based on the elapsed [`Time<Real>`].
271///
272/// The virtual time will be advanced up to the provided [`Time::max_delta`].
273pub fn update_virtual_time(current: &mut Time, virt: &mut Time<Virtual>, real: &Time<Real>) {
274    let raw_delta = real.delta();
275    virt.advance_with_raw_delta(raw_delta);
276    *current = virt.as_generic();
277}
278
279#[cfg(test)]
280mod test {
281    use super::*;
282
283    #[test]
284    fn test_default() {
285        let time = Time::<Virtual>::default();
286
287        assert!(!time.is_paused()); // false
288        assert_eq!(time.relative_speed(), 1.0);
289        assert_eq!(time.max_delta(), Time::<Virtual>::DEFAULT_MAX_DELTA);
290        assert_eq!(time.delta(), Duration::ZERO);
291        assert_eq!(time.elapsed(), Duration::ZERO);
292    }
293
294    #[test]
295    fn test_advance() {
296        let mut time = Time::<Virtual>::default();
297
298        time.advance_with_raw_delta(Duration::from_millis(125));
299
300        assert_eq!(time.delta(), Duration::from_millis(125));
301        assert_eq!(time.elapsed(), Duration::from_millis(125));
302
303        time.advance_with_raw_delta(Duration::from_millis(125));
304
305        assert_eq!(time.delta(), Duration::from_millis(125));
306        assert_eq!(time.elapsed(), Duration::from_millis(250));
307
308        time.advance_with_raw_delta(Duration::from_millis(125));
309
310        assert_eq!(time.delta(), Duration::from_millis(125));
311        assert_eq!(time.elapsed(), Duration::from_millis(375));
312
313        time.advance_with_raw_delta(Duration::from_millis(125));
314
315        assert_eq!(time.delta(), Duration::from_millis(125));
316        assert_eq!(time.elapsed(), Duration::from_millis(500));
317    }
318
319    #[test]
320    fn test_relative_speed() {
321        let mut time = Time::<Virtual>::default();
322
323        time.advance_with_raw_delta(Duration::from_millis(250));
324
325        assert_eq!(time.relative_speed(), 1.0);
326        assert_eq!(time.effective_speed(), 1.0);
327        assert_eq!(time.delta(), Duration::from_millis(250));
328        assert_eq!(time.elapsed(), Duration::from_millis(250));
329
330        time.set_relative_speed_f64(2.0);
331
332        assert_eq!(time.relative_speed(), 2.0);
333        assert_eq!(time.effective_speed(), 1.0);
334
335        time.advance_with_raw_delta(Duration::from_millis(250));
336
337        assert_eq!(time.relative_speed(), 2.0);
338        assert_eq!(time.effective_speed(), 2.0);
339        assert_eq!(time.delta(), Duration::from_millis(500));
340        assert_eq!(time.elapsed(), Duration::from_millis(750));
341
342        time.set_relative_speed_f64(0.5);
343
344        assert_eq!(time.relative_speed(), 0.5);
345        assert_eq!(time.effective_speed(), 2.0);
346
347        time.advance_with_raw_delta(Duration::from_millis(250));
348
349        assert_eq!(time.relative_speed(), 0.5);
350        assert_eq!(time.effective_speed(), 0.5);
351        assert_eq!(time.delta(), Duration::from_millis(125));
352        assert_eq!(time.elapsed(), Duration::from_millis(875));
353    }
354
355    #[test]
356    fn test_pause() {
357        let mut time = Time::<Virtual>::default();
358
359        time.advance_with_raw_delta(Duration::from_millis(250));
360
361        assert!(!time.is_paused()); // false
362        assert!(!time.was_paused()); // false
363        assert_eq!(time.relative_speed(), 1.0);
364        assert_eq!(time.effective_speed(), 1.0);
365        assert_eq!(time.delta(), Duration::from_millis(250));
366        assert_eq!(time.elapsed(), Duration::from_millis(250));
367
368        time.pause();
369
370        assert!(time.is_paused()); // true
371        assert!(!time.was_paused()); // false
372        assert_eq!(time.relative_speed(), 1.0);
373        assert_eq!(time.effective_speed(), 1.0);
374
375        time.advance_with_raw_delta(Duration::from_millis(250));
376
377        assert!(time.is_paused()); // true
378        assert!(time.was_paused()); // true
379        assert_eq!(time.relative_speed(), 1.0);
380        assert_eq!(time.effective_speed(), 0.0);
381        assert_eq!(time.delta(), Duration::ZERO);
382        assert_eq!(time.elapsed(), Duration::from_millis(250));
383
384        time.unpause();
385
386        assert!(!time.is_paused()); // false
387        assert!(time.was_paused()); // true
388        assert_eq!(time.relative_speed(), 1.0);
389        assert_eq!(time.effective_speed(), 0.0);
390
391        time.advance_with_raw_delta(Duration::from_millis(250));
392
393        assert!(!time.is_paused()); // false
394        assert!(!time.was_paused()); // false
395        assert_eq!(time.relative_speed(), 1.0);
396        assert_eq!(time.effective_speed(), 1.0);
397        assert_eq!(time.delta(), Duration::from_millis(250));
398        assert_eq!(time.elapsed(), Duration::from_millis(500));
399    }
400
401    #[test]
402    fn test_max_delta() {
403        let mut time = Time::<Virtual>::default();
404        time.set_max_delta(Duration::from_millis(500));
405
406        time.advance_with_raw_delta(Duration::from_millis(250));
407
408        assert_eq!(time.delta(), Duration::from_millis(250));
409        assert_eq!(time.elapsed(), Duration::from_millis(250));
410
411        time.advance_with_raw_delta(Duration::from_millis(500));
412
413        assert_eq!(time.delta(), Duration::from_millis(500));
414        assert_eq!(time.elapsed(), Duration::from_millis(750));
415
416        time.advance_with_raw_delta(Duration::from_millis(750));
417
418        assert_eq!(time.delta(), Duration::from_millis(500));
419        assert_eq!(time.elapsed(), Duration::from_millis(1250));
420
421        time.set_max_delta(Duration::from_secs(1));
422
423        assert_eq!(time.max_delta(), Duration::from_secs(1));
424
425        time.advance_with_raw_delta(Duration::from_millis(750));
426
427        assert_eq!(time.delta(), Duration::from_millis(750));
428        assert_eq!(time.elapsed(), Duration::from_millis(2000));
429
430        time.advance_with_raw_delta(Duration::from_millis(1250));
431
432        assert_eq!(time.delta(), Duration::from_millis(1000));
433        assert_eq!(time.elapsed(), Duration::from_millis(3000));
434    }
435}