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