bevy_time/
virt.rs

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