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