avian3d/schedule/time.rs
1//! Clocks used for tracking physics simulation time.
2
3use crate::prelude::*;
4use bevy::prelude::*;
5
6/// The clock representing physics time, following the [`Time`] clock used by the schedule that physics runs in.
7///
8/// In [`FixedPostUpdate`] and other fixed schedules, this uses [`Time<Fixed>`](Fixed), while in schedules such as [`Update`],
9/// a variable timestep following [`Time<Virtual>`](Virtual) is used.
10///
11/// [`Time<Physics>`](Physics) is automatically set as the generic [`Time`] resource for
12/// the [`PhysicsSchedule`].
13///
14/// # Usage
15///
16/// ## Physics Speed
17///
18/// The relative speed of [`Time<Physics>`](Physics) can be configured at startup
19/// using [`with_relative_speed`], or when the app is running using [`set_relative_speed`]:
20///
21/// ```no_run
22#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
23#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
24/// use bevy::prelude::*;
25///
26/// fn main() {
27/// App::new()
28/// .add_plugins((DefaultPlugins, PhysicsPlugins::default()))
29/// // Run physics at 0.5 speed
30/// .insert_resource(Time::<Physics>::default().with_relative_speed(0.5))
31/// .run();
32/// }
33/// ```
34///
35/// [`with_relative_speed`]: PhysicsTime::with_relative_speed
36/// [`set_relative_speed`]: PhysicsTime::set_relative_speed
37///
38/// ## Pausing, Resuming, and Stepping Physics
39///
40/// [`Time<Physics>`](Physics) can be used to pause and resume the simulation:
41///
42/// ```
43#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
44#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
45/// use bevy::prelude::*;
46///
47/// fn pause(mut time: ResMut<Time<Physics>>) {
48/// time.pause();
49/// }
50///
51/// fn unpause(mut time: ResMut<Time<Physics>>) {
52/// time.unpause();
53/// }
54/// ```
55///
56/// To advance the simulation by a certain amount of time instantly, you can advance the
57/// [`Time<Physics>`](Physics) clock and manually run the [`PhysicsSchedule`] in an exclusive system:
58///
59/// ```
60#[cfg_attr(feature = "2d", doc = "use avian2d::prelude::*;")]
61#[cfg_attr(feature = "3d", doc = "use avian3d::prelude::*;")]
62/// use bevy::prelude::*;
63/// use core::time::Duration;
64///
65/// fn run_physics(world: &mut World) {
66/// // Advance the simulation by 10 steps at 120 Hz
67/// for _ in 0..10 {
68/// world
69/// .resource_mut::<Time<Physics>>()
70/// .advance_by(Duration::from_secs_f64(1.0 / 120.0));
71/// world.run_schedule(PhysicsSchedule);
72/// }
73/// }
74/// ```
75///
76/// ## When to Multiply by Delta Time?
77///
78/// Schedules like `Update` use a variable timestep, which can often cause frame rate dependent
79/// behavior when moving bodies. One way to help address the issue is by multiplying by delta time.
80///
81/// In general, if you're doing a *continuous* operation, you should always multiply by delta time,
82/// but for *instantaneous* operations it's not necessary.
83///
84/// Continuous operations move or accelerate bodies over time:
85///
86/// ```
87/// # use bevy::math::Vec3;
88/// #
89/// # let mut position = Vec3::default();
90/// # let mut velocity = Vec3::default();
91/// # let mut acceleration = Vec3::default();
92/// # let mut delta_time = 1.0 / 60.0;
93/// #
94/// // Move continuously over time
95/// position += velocity * delta_time;
96/// // Accelerate continuously
97/// velocity += acceleration * delta_time;
98/// ```
99///
100/// Instantaneous operations apply a singular sudden burst of velocity
101/// or set it to a specific value:
102///
103/// ```
104/// # use bevy::math::Vec3;
105/// #
106/// # let mut velocity = Vec3::default();
107/// # let mut impulse = Vec3::default();
108/// # let mut target_velocity = Vec3::default();
109/// #
110/// // Apply a burst of speed once (jumps, explosions and so on)
111/// velocity += impulse;
112/// // Set velocity to a specific value
113/// velocity = target_velocity;
114/// ```
115///
116/// For systems using a fixed timestep, using delta time is not necessary for frame rate
117/// independence, but it's still recommended so that the physical units are more logical.
118
119#[derive(Reflect, Clone, Copy, Debug, PartialEq)]
120#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
121#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
122#[reflect(Debug, PartialEq)]
123pub struct Physics {
124 paused: bool,
125 relative_speed: f64,
126}
127
128impl Default for Physics {
129 fn default() -> Self {
130 Self {
131 paused: false,
132 relative_speed: 1.0,
133 }
134 }
135}
136
137/// An extension trait for [`Time<Physics>`](Physics).
138pub trait PhysicsTime {
139 /// Returns the speed of physics relative to your system clock as an `f32`.
140 /// This is also known as "time scaling" or "time dilation" in other engines.
141 ///
142 /// The speed impacts the accuracy of the simulation, and large values may
143 /// cause jittering or missed collisions. You can improve simulation consistency
144 /// by adjusting your timestep at the cost of performance.
145 fn relative_speed(&self) -> f32;
146
147 /// Returns the speed of physics relative to your system clock as an `f64`.
148 /// This is also known as "time scaling" or "time dilation" in other engines.
149 ///
150 /// The speed impacts the accuracy of the simulation, and large values may
151 /// cause jittering or missed collisions. You can improve simulation consistency
152 /// by adjusting your timestep at the cost of performance.
153 fn relative_speed_f64(&self) -> f64;
154
155 /// Sets the speed of physics relative to your system clock, given as an `f32`.
156 ///
157 /// For example, setting this to `2.0` will make the physics clock advance twice
158 /// as fast as your system clock.
159 ///
160 /// The speed impacts the accuracy of the simulation, and large values may
161 /// cause jittering or missed collisions. You can improve simulation consistency
162 /// by adjusting your timestep at the cost of performance.
163 ///
164 /// # Panics
165 ///
166 /// Panics if `ratio` is negative or not finite.
167 fn with_relative_speed(self, ratio: f32) -> Self;
168
169 /// Sets the speed of physics relative to your system clock, given as an `f64`.
170 ///
171 /// For example, setting this to `2.0` will make the physics clock advance twice
172 /// as fast as your system clock.
173 ///
174 /// The speed impacts the accuracy of the simulation, and large values may
175 /// cause jittering or missed collisions. You can improve simulation consistency
176 /// by adjusting your timestep at the cost of performance.
177 ///
178 /// # Panics
179 ///
180 /// Panics if `ratio` is negative or not finite.
181 fn with_relative_speed_f64(self, ratio: f64) -> Self;
182
183 /// Sets the speed of physics relative to your system clock, given as an `f32`.
184 ///
185 /// For example, setting this to `2.0` will make the physics clock advance twice
186 /// as fast as your system clock.
187 ///
188 /// The speed impacts the accuracy of the simulation, and large values may
189 /// cause jittering or missed collisions. You can improve simulation consistency
190 /// by adjusting your timestep at the cost of performance.
191 ///
192 /// # Panics
193 ///
194 /// Panics if `ratio` is negative or not finite.
195 fn set_relative_speed(&mut self, ratio: f32);
196
197 /// Sets the speed of physics relative to your system clock, given as an `f64`.
198 ///
199 /// For example, setting this to `2.0` will make the physics clock advance twice
200 /// as fast as your system clock.
201 ///
202 /// The speed impacts the accuracy of the simulation, and large values may
203 /// cause jittering or missed collisions. You can improve simulation consistency
204 /// by adjusting your timestep at the cost of performance.
205 ///
206 /// # Panics
207 ///
208 /// Panics if `ratio` is negative or not finite.
209 fn set_relative_speed_f64(&mut self, ratio: f64);
210
211 /// Returns `true` if the physics clock is currently paused.
212 fn is_paused(&self) -> bool;
213
214 /// Stops the clock, preventing the physics simulation from advancing until resumed.
215 fn pause(&mut self);
216
217 /// Resumes the clock if paused.
218 #[doc(alias = "resume")]
219 fn unpause(&mut self);
220}
221
222impl PhysicsTime for Time<Physics> {
223 fn with_relative_speed(self, ratio: f32) -> Self {
224 self.with_relative_speed_f64(ratio as f64)
225 }
226
227 fn with_relative_speed_f64(mut self, ratio: f64) -> Self {
228 assert!(ratio.is_finite(), "tried to go infinitely fast");
229 assert!(ratio >= 0.0, "tried to go back in time");
230 self.context_mut().relative_speed = ratio;
231 self
232 }
233
234 fn relative_speed(&self) -> f32 {
235 self.relative_speed_f64() as f32
236 }
237
238 fn relative_speed_f64(&self) -> f64 {
239 self.context().relative_speed
240 }
241
242 fn set_relative_speed(&mut self, ratio: f32) {
243 self.set_relative_speed_f64(ratio as f64);
244 }
245
246 fn set_relative_speed_f64(&mut self, ratio: f64) {
247 assert!(ratio.is_finite(), "tried to go infinitely fast");
248 assert!(ratio >= 0.0, "tried to go back in time");
249 self.context_mut().relative_speed = ratio;
250 }
251
252 fn pause(&mut self) {
253 self.context_mut().paused = true;
254 }
255
256 fn unpause(&mut self) {
257 self.context_mut().paused = false;
258 }
259
260 fn is_paused(&self) -> bool {
261 self.context().paused
262 }
263}
264
265/// The clock representing physics substep time. It is updated based on
266/// [`Time<Physics>`](Physics) and the [`SubstepCount`] resource.
267///
268/// The clock is automatically set as the generic `Time` resource for
269/// the [`SubstepSchedule`].
270#[derive(Reflect, Clone, Copy, Debug, Default, PartialEq)]
271pub struct Substeps;
272
273pub(crate) trait TimePrecisionAdjusted {
274 /// Returns how much time has advanced since the last update
275 /// as [`Scalar`] seconds.
276 fn delta_seconds_adjusted(&self) -> Scalar;
277}
278
279impl TimePrecisionAdjusted for Time {
280 /// Returns how much time has advanced since the last [`update`](#method.update)
281 /// as [`Scalar`] seconds.
282 fn delta_seconds_adjusted(&self) -> Scalar {
283 #[cfg(feature = "f32")]
284 {
285 self.delta_secs()
286 }
287 #[cfg(feature = "f64")]
288 {
289 self.delta_secs_f64()
290 }
291 }
292}