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}