Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_time/src/real.rs
6598 views
1
use bevy_platform::time::Instant;
2
#[cfg(feature = "bevy_reflect")]
3
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
4
use core::time::Duration;
5
6
use crate::time::Time;
7
8
/// Real time clock representing elapsed wall clock time.
9
///
10
/// A specialization of the [`Time`] structure. **For method documentation, see
11
/// [`Time<Real>#impl-Time<Real>`].**
12
///
13
/// It is automatically inserted as a resource by
14
/// [`TimePlugin`](crate::TimePlugin) and updated with time instants according
15
/// to [`TimeUpdateStrategy`](crate::TimeUpdateStrategy).[^disclaimer]
16
///
17
/// Note:
18
/// Using [`TimeUpdateStrategy::ManualDuration`](crate::TimeUpdateStrategy::ManualDuration)
19
/// allows for mocking the wall clock for testing purposes.
20
/// Besides this use case, it is not recommended to do this, as it will no longer
21
/// represent "wall clock" time as intended.
22
///
23
/// The [`delta()`](Time::delta) and [`elapsed()`](Time::elapsed) values of this
24
/// clock should be used for anything which deals specifically with real time
25
/// (wall clock time). It will not be affected by relative game speed
26
/// adjustments, pausing or other adjustments.[^disclaimer]
27
///
28
/// The clock does not count time from [`startup()`](Time::startup) to
29
/// [`first_update()`](Time::first_update()) into elapsed, but instead will
30
/// start counting time from the first update call. [`delta()`](Time::delta) and
31
/// [`elapsed()`](Time::elapsed) will report zero on the first update as there
32
/// is no previous update instant. This means that a [`delta()`](Time::delta) of
33
/// zero must be handled without errors in application logic, as it may
34
/// theoretically also happen at other times.
35
///
36
/// [`Instant`]s for [`startup()`](Time::startup),
37
/// [`first_update()`](Time::first_update) and
38
/// [`last_update()`](Time::last_update) are recorded and accessible.
39
///
40
/// [^disclaimer]: When using [`TimeUpdateStrategy::ManualDuration`](crate::TimeUpdateStrategy::ManualDuration),
41
/// [`Time<Real>#impl-Time<Real>`] is only a *mock* of wall clock time.
42
///
43
#[derive(Debug, Copy, Clone)]
44
#[cfg_attr(feature = "bevy_reflect", derive(Reflect), reflect(Clone, Default))]
45
pub struct Real {
46
startup: Instant,
47
first_update: Option<Instant>,
48
last_update: Option<Instant>,
49
}
50
51
impl Default for Real {
52
fn default() -> Self {
53
Self {
54
startup: Instant::now(),
55
first_update: None,
56
last_update: None,
57
}
58
}
59
}
60
61
impl Time<Real> {
62
/// Constructs a new `Time<Real>` instance with a specific startup
63
/// [`Instant`].
64
pub fn new(startup: Instant) -> Self {
65
Self::new_with(Real {
66
startup,
67
..Default::default()
68
})
69
}
70
71
/// Updates the internal time measurements.
72
///
73
/// Calling this method as part of your app will most likely result in
74
/// inaccurate timekeeping, as the [`Time`] resource is ordinarily managed
75
/// by the [`TimePlugin`](crate::TimePlugin).
76
pub fn update(&mut self) {
77
let instant = Instant::now();
78
self.update_with_instant(instant);
79
}
80
81
/// Updates time with a specified [`Duration`].
82
///
83
/// This method is provided for use in tests.
84
///
85
/// Calling this method as part of your app will most likely result in
86
/// inaccurate timekeeping, as the [`Time`] resource is ordinarily managed
87
/// by the [`TimePlugin`](crate::TimePlugin).
88
pub fn update_with_duration(&mut self, duration: Duration) {
89
let last_update = self.context().last_update.unwrap_or(self.context().startup);
90
self.update_with_instant(last_update + duration);
91
}
92
93
/// Updates time with a specified [`Instant`].
94
///
95
/// This method is provided for use in tests.
96
///
97
/// Calling this method as part of your app will most likely result in inaccurate timekeeping,
98
/// as the [`Time`] resource is ordinarily managed by the [`TimePlugin`](crate::TimePlugin).
99
pub fn update_with_instant(&mut self, instant: Instant) {
100
let Some(last_update) = self.context().last_update else {
101
let context = self.context_mut();
102
context.first_update = Some(instant);
103
context.last_update = Some(instant);
104
return;
105
};
106
let delta = instant - last_update;
107
self.advance_by(delta);
108
self.context_mut().last_update = Some(instant);
109
}
110
111
/// Returns the [`Instant`] the clock was created.
112
///
113
/// This usually represents when the app was started.
114
#[inline]
115
pub fn startup(&self) -> Instant {
116
self.context().startup
117
}
118
119
/// Returns the [`Instant`] when [`Self::update`] was first called, if it
120
/// exists.
121
///
122
/// This usually represents when the first app update started.
123
#[inline]
124
pub fn first_update(&self) -> Option<Instant> {
125
self.context().first_update
126
}
127
128
/// Returns the [`Instant`] when [`Self::update`] was last called, if it
129
/// exists.
130
///
131
/// This usually represents when the current app update started.
132
#[inline]
133
pub fn last_update(&self) -> Option<Instant> {
134
self.context().last_update
135
}
136
}
137
138
#[cfg(test)]
139
mod test {
140
use super::*;
141
142
// Waits until Instant::now() has increased.
143
//
144
// ```
145
// let previous = Instant::now();
146
// wait();
147
// assert!(Instant::now() > previous);
148
// ```
149
fn wait() {
150
let start = Instant::now();
151
while Instant::now() <= start {}
152
}
153
154
#[test]
155
fn test_update() {
156
let startup = Instant::now();
157
let mut time = Time::<Real>::new(startup);
158
159
assert_eq!(time.startup(), startup);
160
assert_eq!(time.first_update(), None);
161
assert_eq!(time.last_update(), None);
162
assert_eq!(time.delta(), Duration::ZERO);
163
assert_eq!(time.elapsed(), Duration::ZERO);
164
165
wait();
166
time.update();
167
168
assert_ne!(time.first_update(), None);
169
assert_ne!(time.last_update(), None);
170
assert_eq!(time.delta(), Duration::ZERO);
171
assert_eq!(time.elapsed(), Duration::ZERO);
172
173
wait();
174
time.update();
175
176
assert_ne!(time.first_update(), None);
177
assert_ne!(time.last_update(), None);
178
assert_ne!(time.last_update(), time.first_update());
179
assert_ne!(time.delta(), Duration::ZERO);
180
assert_eq!(time.elapsed(), time.delta());
181
182
wait();
183
let prev_elapsed = time.elapsed();
184
time.update();
185
186
assert_ne!(time.delta(), Duration::ZERO);
187
assert_eq!(time.elapsed(), prev_elapsed + time.delta());
188
}
189
190
#[test]
191
fn test_update_with_instant() {
192
let startup = Instant::now();
193
let mut time = Time::<Real>::new(startup);
194
195
wait();
196
let first_update = Instant::now();
197
time.update_with_instant(first_update);
198
199
assert_eq!(time.startup(), startup);
200
assert_eq!(time.first_update(), Some(first_update));
201
assert_eq!(time.last_update(), Some(first_update));
202
assert_eq!(time.delta(), Duration::ZERO);
203
assert_eq!(time.elapsed(), Duration::ZERO);
204
205
wait();
206
let second_update = Instant::now();
207
time.update_with_instant(second_update);
208
209
assert_eq!(time.first_update(), Some(first_update));
210
assert_eq!(time.last_update(), Some(second_update));
211
assert_eq!(time.delta(), second_update - first_update);
212
assert_eq!(time.elapsed(), second_update - first_update);
213
214
wait();
215
let third_update = Instant::now();
216
time.update_with_instant(third_update);
217
218
assert_eq!(time.first_update(), Some(first_update));
219
assert_eq!(time.last_update(), Some(third_update));
220
assert_eq!(time.delta(), third_update - second_update);
221
assert_eq!(time.elapsed(), third_update - first_update);
222
}
223
224
#[test]
225
fn test_update_with_duration() {
226
let startup = Instant::now();
227
let mut time = Time::<Real>::new(startup);
228
229
time.update_with_duration(Duration::from_secs(1));
230
231
assert_eq!(time.startup(), startup);
232
assert_eq!(time.first_update(), Some(startup + Duration::from_secs(1)));
233
assert_eq!(time.last_update(), Some(startup + Duration::from_secs(1)));
234
assert_eq!(time.delta(), Duration::ZERO);
235
assert_eq!(time.elapsed(), Duration::ZERO);
236
237
time.update_with_duration(Duration::from_secs(1));
238
239
assert_eq!(time.first_update(), Some(startup + Duration::from_secs(1)));
240
assert_eq!(time.last_update(), Some(startup + Duration::from_secs(2)));
241
assert_eq!(time.delta(), Duration::from_secs(1));
242
assert_eq!(time.elapsed(), Duration::from_secs(1));
243
244
time.update_with_duration(Duration::from_secs(1));
245
246
assert_eq!(time.first_update(), Some(startup + Duration::from_secs(1)));
247
assert_eq!(time.last_update(), Some(startup + Duration::from_secs(3)));
248
assert_eq!(time.delta(), Duration::from_secs(1));
249
assert_eq!(time.elapsed(), Duration::from_secs(2));
250
}
251
}
252
253