bevy_time/
time.rs

1use bevy_ecs::system::Resource;
2use bevy_utils::Duration;
3#[cfg(feature = "bevy_reflect")]
4use {
5    bevy_ecs::reflect::ReflectResource,
6    bevy_reflect::{std_traits::ReflectDefault, Reflect},
7};
8
9/// A generic clock resource that tracks how much it has advanced since its
10/// previous update and since its creation.
11///
12/// Multiple instances of this resource are inserted automatically by
13/// [`TimePlugin`](crate::TimePlugin):
14///
15/// - [`Time<Real>`](crate::real::Real) tracks real wall-clock time elapsed.
16/// - [`Time<Virtual>`](crate::virt::Virtual) tracks virtual game time that may
17///   be paused or scaled.
18/// - [`Time<Fixed>`](crate::fixed::Fixed) tracks fixed timesteps based on
19///   virtual time.
20/// - [`Time`] is a generic clock that corresponds to "current" or "default"
21///   time for systems. It contains [`Time<Virtual>`](crate::virt::Virtual)
22///   except inside the [`FixedMain`](bevy_app::FixedMain) schedule when it
23///   contains [`Time<Fixed>`](crate::fixed::Fixed).
24///
25/// The time elapsed since the previous time this clock was advanced is saved as
26/// [`delta()`](Time::delta) and the total amount of time the clock has advanced
27/// is saved as [`elapsed()`](Time::elapsed). Both are represented as exact
28/// [`Duration`] values with fixed nanosecond precision. The clock does not
29/// support time moving backwards, but it can be updated with [`Duration::ZERO`]
30/// which will set [`delta()`](Time::delta) to zero.
31///
32/// These values are also available in seconds as `f32` via
33/// [`delta_secs()`](Time::delta_secs) and
34/// [`elapsed_secs()`](Time::elapsed_secs), and also in seconds as `f64`
35/// via [`delta_secs_f64()`](Time::delta_secs_f64) and
36/// [`elapsed_secs_f64()`](Time::elapsed_secs_f64).
37///
38/// Since [`elapsed_secs()`](Time::elapsed_secs) will grow constantly and
39/// is `f32`, it will exhibit gradual precision loss. For applications that
40/// require an `f32` value but suffer from gradual precision loss there is
41/// [`elapsed_secs_wrapped()`](Time::elapsed_secs_wrapped) available. The
42/// same wrapped value is also available as [`Duration`] and `f64` for
43/// consistency. The wrap period is by default 1 hour, and can be set by
44/// [`set_wrap_period()`](Time::set_wrap_period).
45///
46/// # Accessing clocks
47///
48/// By default, any systems requiring current [`delta()`](Time::delta) or
49/// [`elapsed()`](Time::elapsed) should use `Res<Time>` to access the default
50/// time configured for the program. By default, this refers to
51/// [`Time<Virtual>`](crate::virt::Virtual) except during the
52/// [`FixedMain`](bevy_app::FixedMain) schedule when it refers to
53/// [`Time<Fixed>`](crate::fixed::Fixed). This ensures your system can be used
54/// either in [`Update`](bevy_app::Update) or
55/// [`FixedUpdate`](bevy_app::FixedUpdate) schedule depending on what is needed.
56///
57/// ```
58/// # use bevy_ecs::prelude::*;
59/// # use bevy_time::prelude::*;
60/// #
61/// fn ambivalent_system(time: Res<Time>) {
62///     println!("this how I see time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
63/// }
64/// ```
65///
66/// If your system needs to react based on real time (wall clock time), like for
67/// user interfaces, it should use `Res<Time<Real>>`. The
68/// [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values will always
69/// correspond to real time and will not be affected by pause, time scaling or
70/// other tweaks.
71///
72/// ```
73/// # use bevy_ecs::prelude::*;
74/// # use bevy_time::prelude::*;
75/// #
76/// fn real_time_system(time: Res<Time<Real>>) {
77///     println!("this will always be real time: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
78/// }
79/// ```
80///
81/// If your system specifically needs to access fixed timestep clock, even when
82/// placed in `Update` schedule, you should use `Res<Time<Fixed>>`. The
83/// [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values will
84/// correspond to the latest fixed timestep that has been run.
85///
86/// ```
87/// # use bevy_ecs::prelude::*;
88/// # use bevy_time::prelude::*;
89/// #
90/// fn fixed_time_system(time: Res<Time<Fixed>>) {
91///     println!("this will always be the last executed fixed timestep: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
92/// }
93/// ```
94///
95/// Finally, if your system specifically needs to know the current virtual game
96/// time, even if placed inside [`FixedUpdate`](bevy_app::FixedUpdate), for
97/// example to know if the game is [`was_paused()`](Time::was_paused) or to use
98/// [`effective_speed()`](Time::effective_speed), you can use
99/// `Res<Time<Virtual>>`. However, if the system is placed in
100/// [`FixedUpdate`](bevy_app::FixedUpdate), extra care must be used because your
101/// system might be run multiple times with the same [`delta()`](Time::delta)
102/// and [`elapsed()`](Time::elapsed) values as the virtual game time has not
103/// changed between the iterations.
104///
105/// ```
106/// # use bevy_ecs::prelude::*;
107/// # use bevy_time::prelude::*;
108/// #
109/// fn fixed_time_system(time: Res<Time<Virtual>>) {
110///     println!("this will be virtual time for this update: delta {:?}, elapsed {:?}", time.delta(), time.elapsed());
111///     println!("also the relative speed of the game is now {}", time.effective_speed());
112/// }
113/// ```
114///
115/// If you need to change the settings for any of the clocks, for example to
116/// [`pause()`](Time::pause) the game, you should use `ResMut<Time<Virtual>>`.
117///
118/// ```
119/// # use bevy_ecs::prelude::*;
120/// # use bevy_time::prelude::*;
121/// #
122/// #[derive(Event)]
123/// struct PauseEvent(bool);
124///
125/// fn pause_system(mut time: ResMut<Time<Virtual>>, mut events: EventReader<PauseEvent>) {
126///     for ev in events.read() {
127///         if ev.0 {
128///             time.pause();
129///         } else {
130///             time.unpause();
131///         }
132///     }
133/// }
134/// ```
135///
136/// # Adding custom clocks
137///
138/// New custom clocks can be created by creating your own struct as a context
139/// and passing it to [`new_with()`](Time::new_with). These clocks can be
140/// inserted as resources as normal and then accessed by systems. You can use
141/// the [`advance_by()`](Time::advance_by) or [`advance_to()`](Time::advance_to)
142/// methods to move the clock forwards based on your own logic.
143///
144/// If you want to add methods for your time instance and they require access to
145/// both your context and the generic time part, it's probably simplest to add a
146/// custom trait for them and implement it for `Time<Custom>`.
147///
148/// Your context struct will need to implement the [`Default`] trait because
149/// [`Time`] structures support reflection. It also makes initialization trivial
150/// by being able to call `app.init_resource::<Time<Custom>>()`.
151///
152/// You can also replace the "generic" `Time` clock resource if the "default"
153/// time for your game should not be the default virtual time provided. You can
154/// get a "generic" snapshot of your clock by calling `as_generic()` and then
155/// overwrite the [`Time`] resource with it. The default systems added by
156/// [`TimePlugin`](crate::TimePlugin) will overwrite the [`Time`] clock during
157/// [`First`](bevy_app::First) and [`FixedUpdate`](bevy_app::FixedUpdate)
158/// schedules.
159///
160/// ```
161/// # use bevy_ecs::prelude::*;
162/// # use bevy_time::prelude::*;
163/// # use bevy_utils::Instant;
164/// #
165/// #[derive(Debug)]
166/// struct Custom {
167///     last_external_time: Instant,
168/// }
169///
170/// impl Default for Custom {
171///     fn default() -> Self {
172///         Self {
173///             last_external_time: Instant::now(),
174///         }
175///     }
176/// }
177///
178/// trait CustomTime {
179///     fn update_from_external(&mut self, instant: Instant);
180/// }
181///
182/// impl CustomTime for Time<Custom> {
183///     fn update_from_external(&mut self, instant: Instant) {
184///          let delta = instant - self.context().last_external_time;
185///          self.advance_by(delta);
186///          self.context_mut().last_external_time = instant;
187///     }
188/// }
189/// ```
190#[derive(Resource, Debug, Copy, Clone)]
191#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Resource, Default))]
192pub struct Time<T: Default = ()> {
193    context: T,
194    wrap_period: Duration,
195    delta: Duration,
196    delta_secs: f32,
197    delta_secs_f64: f64,
198    elapsed: Duration,
199    elapsed_secs: f32,
200    elapsed_secs_f64: f64,
201    elapsed_wrapped: Duration,
202    elapsed_secs_wrapped: f32,
203    elapsed_secs_wrapped_f64: f64,
204}
205
206impl<T: Default> Time<T> {
207    const DEFAULT_WRAP_PERIOD: Duration = Duration::from_secs(3600); // 1 hour
208
209    /// Create a new clock from context with [`Self::delta`] and [`Self::elapsed`] starting from
210    /// zero.
211    pub fn new_with(context: T) -> Self {
212        Self {
213            context,
214            ..Default::default()
215        }
216    }
217
218    /// Advance this clock by adding a `delta` duration to it.
219    ///
220    /// The added duration will be returned by [`Self::delta`] and
221    /// [`Self::elapsed`] will be increased by the duration. Adding
222    /// [`Duration::ZERO`] is allowed and will set [`Self::delta`] to zero.
223    pub fn advance_by(&mut self, delta: Duration) {
224        self.delta = delta;
225        self.delta_secs = self.delta.as_secs_f32();
226        self.delta_secs_f64 = self.delta.as_secs_f64();
227        self.elapsed += delta;
228        self.elapsed_secs = self.elapsed.as_secs_f32();
229        self.elapsed_secs_f64 = self.elapsed.as_secs_f64();
230        self.elapsed_wrapped = duration_rem(self.elapsed, self.wrap_period);
231        self.elapsed_secs_wrapped = self.elapsed_wrapped.as_secs_f32();
232        self.elapsed_secs_wrapped_f64 = self.elapsed_wrapped.as_secs_f64();
233    }
234
235    /// Advance this clock to a specific `elapsed` time.
236    ///
237    /// [`Self::delta()`] will return the amount of time the clock was advanced
238    /// and [`Self::elapsed()`] will be the `elapsed` value passed in. Cannot be
239    /// used to move time backwards.
240    ///
241    /// # Panics
242    ///
243    /// Panics if `elapsed` is less than `Self::elapsed()`.
244    pub fn advance_to(&mut self, elapsed: Duration) {
245        assert!(
246            elapsed >= self.elapsed,
247            "tried to move time backwards to an earlier elapsed moment"
248        );
249        self.advance_by(elapsed - self.elapsed);
250    }
251
252    /// Returns the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped).
253    ///
254    /// **Note:** The default modulus is one hour.
255    #[inline]
256    pub fn wrap_period(&self) -> Duration {
257        self.wrap_period
258    }
259
260    /// Sets the modulus used to calculate [`elapsed_wrapped`](#method.elapsed_wrapped).
261    ///
262    /// **Note:** This will not take effect until the next update.
263    ///
264    /// # Panics
265    ///
266    /// Panics if `wrap_period` is a zero-length duration.
267    #[inline]
268    pub fn set_wrap_period(&mut self, wrap_period: Duration) {
269        assert!(!wrap_period.is_zero(), "division by zero");
270        self.wrap_period = wrap_period;
271    }
272
273    /// Returns how much time has advanced since the last [`update`](#method.update), as a
274    /// [`Duration`].
275    #[inline]
276    pub fn delta(&self) -> Duration {
277        self.delta
278    }
279
280    /// Returns how much time has advanced since the last [`update`](#method.update), as [`f32`]
281    /// seconds.
282    #[inline]
283    pub fn delta_secs(&self) -> f32 {
284        self.delta_secs
285    }
286
287    /// Returns how much time has advanced since the last [`update`](#method.update), as [`f64`]
288    /// seconds.
289    #[inline]
290    pub fn delta_secs_f64(&self) -> f64 {
291        self.delta_secs_f64
292    }
293
294    /// Returns how much time has advanced since [`startup`](#method.startup), as [`Duration`].
295    #[inline]
296    pub fn elapsed(&self) -> Duration {
297        self.elapsed
298    }
299
300    /// Returns how much time has advanced since [`startup`](#method.startup), as [`f32`] seconds.
301    ///
302    /// **Note:** This is a monotonically increasing value. Its precision will degrade over time.
303    /// If you need an `f32` but that precision loss is unacceptable,
304    /// use [`elapsed_secs_wrapped`](#method.elapsed_secs_wrapped).
305    #[inline]
306    pub fn elapsed_secs(&self) -> f32 {
307        self.elapsed_secs
308    }
309
310    /// Returns how much time has advanced since [`startup`](#method.startup), as [`f64`] seconds.
311    #[inline]
312    pub fn elapsed_secs_f64(&self) -> f64 {
313        self.elapsed_secs_f64
314    }
315
316    /// Returns how much time has advanced since [`startup`](#method.startup) modulo
317    /// the [`wrap_period`](#method.wrap_period), as [`Duration`].
318    #[inline]
319    pub fn elapsed_wrapped(&self) -> Duration {
320        self.elapsed_wrapped
321    }
322
323    /// Returns how much time has advanced since [`startup`](#method.startup) modulo
324    /// the [`wrap_period`](#method.wrap_period), as [`f32`] seconds.
325    ///
326    /// This method is intended for applications (e.g. shaders) that require an [`f32`] value but
327    /// suffer from the gradual precision loss of [`elapsed_secs`](#method.elapsed_secs).
328    #[inline]
329    pub fn elapsed_secs_wrapped(&self) -> f32 {
330        self.elapsed_secs_wrapped
331    }
332
333    /// Returns how much time has advanced since [`startup`](#method.startup) modulo
334    /// the [`wrap_period`](#method.wrap_period), as [`f64`] seconds.
335    #[inline]
336    pub fn elapsed_secs_wrapped_f64(&self) -> f64 {
337        self.elapsed_secs_wrapped_f64
338    }
339
340    /// Returns a reference to the context of this specific clock.
341    #[inline]
342    pub fn context(&self) -> &T {
343        &self.context
344    }
345
346    /// Returns a mutable reference to the context of this specific clock.
347    #[inline]
348    pub fn context_mut(&mut self) -> &mut T {
349        &mut self.context
350    }
351
352    /// Returns a copy of this clock as fully generic clock without context.
353    #[inline]
354    pub fn as_generic(&self) -> Time<()> {
355        Time {
356            context: (),
357            wrap_period: self.wrap_period,
358            delta: self.delta,
359            delta_secs: self.delta_secs,
360            delta_secs_f64: self.delta_secs_f64,
361            elapsed: self.elapsed,
362            elapsed_secs: self.elapsed_secs,
363            elapsed_secs_f64: self.elapsed_secs_f64,
364            elapsed_wrapped: self.elapsed_wrapped,
365            elapsed_secs_wrapped: self.elapsed_secs_wrapped,
366            elapsed_secs_wrapped_f64: self.elapsed_secs_wrapped_f64,
367        }
368    }
369}
370
371impl<T: Default> Default for Time<T> {
372    fn default() -> Self {
373        Self {
374            context: Default::default(),
375            wrap_period: Self::DEFAULT_WRAP_PERIOD,
376            delta: Duration::ZERO,
377            delta_secs: 0.0,
378            delta_secs_f64: 0.0,
379            elapsed: Duration::ZERO,
380            elapsed_secs: 0.0,
381            elapsed_secs_f64: 0.0,
382            elapsed_wrapped: Duration::ZERO,
383            elapsed_secs_wrapped: 0.0,
384            elapsed_secs_wrapped_f64: 0.0,
385        }
386    }
387}
388
389fn duration_rem(dividend: Duration, divisor: Duration) -> Duration {
390    // `Duration` does not have a built-in modulo operation
391    let quotient = (dividend.as_nanos() / divisor.as_nanos()) as u32;
392    dividend - (quotient * divisor)
393}
394
395#[cfg(test)]
396mod test {
397    use super::*;
398
399    #[test]
400    fn test_initial_state() {
401        let time: Time = Time::default();
402
403        assert_eq!(time.wrap_period(), Time::<()>::DEFAULT_WRAP_PERIOD);
404        assert_eq!(time.delta(), Duration::ZERO);
405        assert_eq!(time.delta_secs(), 0.0);
406        assert_eq!(time.delta_secs_f64(), 0.0);
407        assert_eq!(time.elapsed(), Duration::ZERO);
408        assert_eq!(time.elapsed_secs(), 0.0);
409        assert_eq!(time.elapsed_secs_f64(), 0.0);
410        assert_eq!(time.elapsed_wrapped(), Duration::ZERO);
411        assert_eq!(time.elapsed_secs_wrapped(), 0.0);
412        assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);
413    }
414
415    #[test]
416    fn test_advance_by() {
417        let mut time: Time = Time::default();
418
419        time.advance_by(Duration::from_millis(250));
420
421        assert_eq!(time.delta(), Duration::from_millis(250));
422        assert_eq!(time.delta_secs(), 0.25);
423        assert_eq!(time.delta_secs_f64(), 0.25);
424        assert_eq!(time.elapsed(), Duration::from_millis(250));
425        assert_eq!(time.elapsed_secs(), 0.25);
426        assert_eq!(time.elapsed_secs_f64(), 0.25);
427
428        time.advance_by(Duration::from_millis(500));
429
430        assert_eq!(time.delta(), Duration::from_millis(500));
431        assert_eq!(time.delta_secs(), 0.5);
432        assert_eq!(time.delta_secs_f64(), 0.5);
433        assert_eq!(time.elapsed(), Duration::from_millis(750));
434        assert_eq!(time.elapsed_secs(), 0.75);
435        assert_eq!(time.elapsed_secs_f64(), 0.75);
436
437        time.advance_by(Duration::ZERO);
438
439        assert_eq!(time.delta(), Duration::ZERO);
440        assert_eq!(time.delta_secs(), 0.0);
441        assert_eq!(time.delta_secs_f64(), 0.0);
442        assert_eq!(time.elapsed(), Duration::from_millis(750));
443        assert_eq!(time.elapsed_secs(), 0.75);
444        assert_eq!(time.elapsed_secs_f64(), 0.75);
445    }
446
447    #[test]
448    fn test_advance_to() {
449        let mut time: Time = Time::default();
450
451        time.advance_to(Duration::from_millis(250));
452
453        assert_eq!(time.delta(), Duration::from_millis(250));
454        assert_eq!(time.delta_secs(), 0.25);
455        assert_eq!(time.delta_secs_f64(), 0.25);
456        assert_eq!(time.elapsed(), Duration::from_millis(250));
457        assert_eq!(time.elapsed_secs(), 0.25);
458        assert_eq!(time.elapsed_secs_f64(), 0.25);
459
460        time.advance_to(Duration::from_millis(750));
461
462        assert_eq!(time.delta(), Duration::from_millis(500));
463        assert_eq!(time.delta_secs(), 0.5);
464        assert_eq!(time.delta_secs_f64(), 0.5);
465        assert_eq!(time.elapsed(), Duration::from_millis(750));
466        assert_eq!(time.elapsed_secs(), 0.75);
467        assert_eq!(time.elapsed_secs_f64(), 0.75);
468
469        time.advance_to(Duration::from_millis(750));
470
471        assert_eq!(time.delta(), Duration::ZERO);
472        assert_eq!(time.delta_secs(), 0.0);
473        assert_eq!(time.delta_secs_f64(), 0.0);
474        assert_eq!(time.elapsed(), Duration::from_millis(750));
475        assert_eq!(time.elapsed_secs(), 0.75);
476        assert_eq!(time.elapsed_secs_f64(), 0.75);
477    }
478
479    #[test]
480    #[should_panic]
481    fn test_advance_to_backwards_panics() {
482        let mut time: Time = Time::default();
483
484        time.advance_to(Duration::from_millis(750));
485
486        time.advance_to(Duration::from_millis(250));
487    }
488
489    #[test]
490    fn test_wrapping() {
491        let mut time: Time = Time::default();
492        time.set_wrap_period(Duration::from_secs(3));
493
494        time.advance_by(Duration::from_secs(2));
495
496        assert_eq!(time.elapsed_wrapped(), Duration::from_secs(2));
497        assert_eq!(time.elapsed_secs_wrapped(), 2.0);
498        assert_eq!(time.elapsed_secs_wrapped_f64(), 2.0);
499
500        time.advance_by(Duration::from_secs(2));
501
502        assert_eq!(time.elapsed_wrapped(), Duration::from_secs(1));
503        assert_eq!(time.elapsed_secs_wrapped(), 1.0);
504        assert_eq!(time.elapsed_secs_wrapped_f64(), 1.0);
505
506        time.advance_by(Duration::from_secs(2));
507
508        assert_eq!(time.elapsed_wrapped(), Duration::ZERO);
509        assert_eq!(time.elapsed_secs_wrapped(), 0.0);
510        assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);
511
512        time.advance_by(Duration::new(3, 250_000_000));
513
514        assert_eq!(time.elapsed_wrapped(), Duration::from_millis(250));
515        assert_eq!(time.elapsed_secs_wrapped(), 0.25);
516        assert_eq!(time.elapsed_secs_wrapped_f64(), 0.25);
517    }
518
519    #[test]
520    fn test_wrapping_change() {
521        let mut time: Time = Time::default();
522        time.set_wrap_period(Duration::from_secs(5));
523
524        time.advance_by(Duration::from_secs(8));
525
526        assert_eq!(time.elapsed_wrapped(), Duration::from_secs(3));
527        assert_eq!(time.elapsed_secs_wrapped(), 3.0);
528        assert_eq!(time.elapsed_secs_wrapped_f64(), 3.0);
529
530        time.set_wrap_period(Duration::from_secs(2));
531
532        assert_eq!(time.elapsed_wrapped(), Duration::from_secs(3));
533        assert_eq!(time.elapsed_secs_wrapped(), 3.0);
534        assert_eq!(time.elapsed_secs_wrapped_f64(), 3.0);
535
536        time.advance_by(Duration::ZERO);
537
538        // Time will wrap to modulo duration from full `elapsed()`, not to what
539        // is left in `elapsed_wrapped()`. This test of values is here to ensure
540        // that we notice if we change that behavior.
541        assert_eq!(time.elapsed_wrapped(), Duration::from_secs(0));
542        assert_eq!(time.elapsed_secs_wrapped(), 0.0);
543        assert_eq!(time.elapsed_secs_wrapped_f64(), 0.0);
544    }
545}