Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_time/src/time.rs
6598 views
1
use bevy_ecs::resource::Resource;
2
use core::time::Duration;
3
#[cfg(feature = "bevy_reflect")]
4
use {
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(BufferedEvent)]
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_platform::time::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))]
192
pub 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
206
impl<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
371
impl<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
389
fn 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)]
396
mod 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
}
546
547