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}