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