Skip to main content

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