avian2d/schedule/
mod.rs

1//! Sets up the default scheduling, system set configuration, and time resources for physics.
2//!
3//! See [`PhysicsSchedulePlugin`].
4
5mod time;
6use dynamics::solver::schedule::SubstepCount;
7pub use time::*;
8
9use core::time::Duration;
10
11// For doc links
12#[allow(unused_imports)]
13use crate::prelude::*;
14
15use bevy::{
16    ecs::{
17        change_detection::Tick,
18        intern::Interned,
19        schedule::{ExecutorKind, LogLevel, ScheduleBuildSettings, ScheduleLabel},
20        system::SystemChangeTick,
21    },
22    prelude::*,
23    transform::TransformSystems,
24};
25
26/// Sets up the default scheduling, system set configuration, and time resources for physics.
27///
28/// # Schedules and Sets
29///
30/// This plugin initializes and configures the following schedules and system sets:
31///
32/// - [`PhysicsSystems`]: High-level system sets for the main phases of the physics engine.
33///   You can use these to schedule your own systems before or after physics is run without
34///   having to worry about implementation details.
35/// - [`PhysicsSchedule`]: Responsible for advancing the simulation in [`PhysicsSystems::StepSimulation`].
36/// - [`PhysicsStepSystems`]: System sets for the steps of the actual physics simulation loop.
37pub struct PhysicsSchedulePlugin {
38    schedule: Interned<dyn ScheduleLabel>,
39}
40
41impl PhysicsSchedulePlugin {
42    /// Creates a [`PhysicsSchedulePlugin`] using the given schedule for running the [`PhysicsSchedule`].
43    ///
44    /// The default schedule is `FixedPostUpdate`.
45    pub fn new(schedule: impl ScheduleLabel) -> Self {
46        Self {
47            schedule: schedule.intern(),
48        }
49    }
50}
51
52impl Default for PhysicsSchedulePlugin {
53    fn default() -> Self {
54        Self::new(FixedPostUpdate)
55    }
56}
57
58impl Plugin for PhysicsSchedulePlugin {
59    fn build(&self, app: &mut App) {
60        // Register types with generics.
61        app.register_type::<Time<Physics>>();
62
63        app.init_resource::<Time<Physics>>()
64            .insert_resource(Time::new_with(Substeps))
65            .init_resource::<SubstepCount>()
66            .init_resource::<LastPhysicsTick>();
67
68        // TODO: Where should this be initialized?
69        app.init_resource::<PhysicsLengthUnit>();
70
71        // Configure higher level system sets for the given schedule
72        let schedule = self.schedule;
73
74        app.configure_sets(
75            schedule,
76            (
77                PhysicsSystems::First,
78                PhysicsSystems::Prepare,
79                PhysicsSystems::StepSimulation,
80                PhysicsSystems::Writeback,
81                PhysicsSystems::Last,
82            )
83                .chain()
84                .before(TransformSystems::Propagate),
85        );
86
87        // Set up the physics schedule, the schedule that advances the physics simulation
88        app.edit_schedule(PhysicsSchedule, |schedule| {
89            schedule
90                .set_executor_kind(ExecutorKind::SingleThreaded)
91                .set_build_settings(ScheduleBuildSettings {
92                    ambiguity_detection: LogLevel::Error,
93                    ..default()
94                });
95
96            schedule.configure_sets(
97                (
98                    PhysicsStepSystems::First,
99                    PhysicsStepSystems::BroadPhase,
100                    PhysicsStepSystems::NarrowPhase,
101                    PhysicsStepSystems::Solver,
102                    PhysicsStepSystems::Sleeping,
103                    PhysicsStepSystems::SpatialQuery,
104                    PhysicsStepSystems::Finalize,
105                    PhysicsStepSystems::Last,
106                )
107                    .chain(),
108            );
109        });
110
111        app.add_systems(
112            schedule,
113            run_physics_schedule.in_set(PhysicsSystems::StepSimulation),
114        );
115
116        app.add_systems(
117            PhysicsSchedule,
118            update_last_physics_tick.after(PhysicsStepSystems::Last),
119        );
120
121        #[cfg(debug_assertions)]
122        app.add_systems(
123            schedule,
124            assert_components_finite.in_set(PhysicsSystems::First),
125        );
126    }
127}
128
129/// True if a system is running for the first time.
130struct IsFirstRun(bool);
131
132impl Default for IsFirstRun {
133    fn default() -> Self {
134        Self(true)
135    }
136}
137
138/// Responsible for advancing the physics simulation. This is run in [`PhysicsSystems::StepSimulation`].
139///
140/// See [`PhysicsStepSystems`] for the system sets that are run in this schedule.
141#[derive(Debug, Hash, PartialEq, Eq, Clone, ScheduleLabel)]
142pub struct PhysicsSchedule;
143
144/// High-level system sets for the main phases of the physics engine.
145/// You can use these to schedule your own systems before or after physics is run without
146/// having to worry about implementation details.
147///
148/// 1. `First`: Runs right before any of Avian's physics systems. Empty by default.
149/// 2. `Prepare`: Responsible for preparing data for the physics simulation, such as updating
150///    physics transforms or mass properties.
151/// 3. `StepSimulation`: Responsible for advancing the simulation by running the steps in [`PhysicsStepSystems`].
152/// 4. `Writeback`: Responsible for writing back the results of the physics simulation to other data,
153///    such as updating [`Transform`] based on the new [`Position`] and [`Rotation`].
154/// 5. `Last`: Runs right after all of Avian's physics systems. Empty by default.
155///
156/// # See Also
157///
158/// - [`PhysicsSchedule`]: Responsible for advancing the simulation in [`PhysicsSystems::StepSimulation`].
159/// - [`PhysicsStepSystems`]: System sets for the steps of the actual physics simulation loop, like
160///   the broad phase and the substepping loop.
161/// - [`SubstepSchedule`]: Responsible for running the substepping loop in [`PhysicsStepSystems::Solver`].
162#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
163pub enum PhysicsSystems {
164    /// Runs right before any of Avian's physics systems. Empty by default.
165    First,
166    /// Responsible for preparing data for the physics simulation, such as updating
167    /// physics transforms or mass properties.
168    Prepare,
169    /// Responsible for advancing the simulation by running the steps in [`PhysicsStepSystems`].
170    /// Systems in this set are run in the [`PhysicsSchedule`].
171    StepSimulation,
172    /// Responsible for writing back the results of the physics simulation to other data,
173    /// such as updating [`Transform`] based on the new [`Position`] and [`Rotation`].
174    Writeback,
175    /// Runs right after all of Avian's physics systems. Empty by default.
176    Last,
177}
178
179/// A deprecated alias for [`PhysicsSystems`].
180#[deprecated(since = "0.4.0", note = "Renamed to `PhysicsSystems`")]
181pub type PhysicsSet = PhysicsSystems;
182
183/// System sets for the main steps in the physics simulation loop. These are typically run in the [`PhysicsSchedule`].
184///
185/// 1. First
186/// 2. Broad phase
187/// 3. Narrow phase
188/// 4. Solver
189/// 5. Sleeping
190/// 6. Spatial queries
191/// 7. Last
192#[derive(SystemSet, Clone, Copy, Debug, PartialEq, Eq, Hash)]
193pub enum PhysicsStepSystems {
194    /// Runs at the start of the [`PhysicsSchedule`].
195    First,
196    /// Responsible for finding pairs of entities with overlapping [`ColliderAabb`]
197    /// and creating contact pairs for them in the [`ContactGraph`].
198    ///
199    /// See the [`broad_phase`](crate::collision::broad_phase) module.
200    BroadPhase,
201    /// Responsible for updating contacts in the [`ContactGraph`] and processing contact state changes.
202    ///
203    /// See [`NarrowPhasePlugin`].
204    NarrowPhase,
205    /// Responsible for running the solver and its substepping loop.
206    ///
207    /// See [`SolverPlugin`] and [`SubstepSchedule`].
208    Solver,
209    /// Responsible for controlling when bodies should be deactivated and marked as [`Sleeping`].
210    Sleeping,
211    /// Responsible for spatial queries like [raycasting](`RayCaster`) and shapecasting.
212    ///
213    /// See [`SpatialQueryPlugin`].
214    SpatialQuery,
215    /// Responsible for logic that runs after the core physics step and prepares for the next one.
216    Finalize,
217    /// Runs at the end of the [`PhysicsSchedule`].
218    Last,
219}
220
221/// A deprecated alias for [`PhysicsStepSystems`].
222#[deprecated(since = "0.4.0", note = "Renamed to `PhysicsStepSystems`")]
223pub type PhysicsStepSet = PhysicsStepSystems;
224
225/// A [`Tick`] corresponding to the end of the previous run of the [`PhysicsSchedule`].
226#[derive(Resource, Reflect, Default)]
227#[reflect(Resource, Default)]
228pub struct LastPhysicsTick(pub Tick);
229
230pub(crate) fn is_changed_after_tick<C: Component>(
231    component_ref: Ref<C>,
232    tick: Tick,
233    this_run: Tick,
234) -> bool {
235    let last_changed = component_ref.last_changed();
236    component_ref.is_changed() && last_changed.is_newer_than(tick, this_run)
237}
238
239/// Runs the [`PhysicsSchedule`].
240fn run_physics_schedule(world: &mut World, mut is_first_run: Local<IsFirstRun>) {
241    let _ = world.try_schedule_scope(PhysicsSchedule, |world, schedule| {
242        let is_paused = world.resource::<Time<Physics>>().is_paused();
243        let old_clock = world.resource::<Time>().as_generic();
244        let physics_clock = world.resource_mut::<Time<Physics>>();
245
246        // Get the scaled timestep delta time based on the timestep mode.
247        let timestep = old_clock
248            .delta()
249            .mul_f64(physics_clock.relative_speed_f64());
250
251        // Advance the physics clock by the timestep if not paused.
252        if !is_paused {
253            world.resource_mut::<Time<Physics>>().advance_by(timestep);
254
255            // Advance the substep clock already so that systems running
256            // before the substepping loop have the right delta.
257            let SubstepCount(substeps) = *world.resource::<SubstepCount>();
258            let sub_delta = timestep.div_f64(substeps as f64);
259            world.resource_mut::<Time<Substeps>>().advance_by(sub_delta);
260        }
261
262        // Set the generic `Time` resource to `Time<Physics>`.
263        *world.resource_mut::<Time>() = world.resource::<Time<Physics>>().as_generic();
264
265        // Advance the simulation.
266        if !world.resource::<Time>().delta().is_zero() {
267            trace!("running PhysicsSchedule");
268            schedule.run(world);
269        }
270
271        // If physics is paused, reset delta time to stop the simulation
272        // unless users manually advance `Time<Physics>`.
273        if is_paused {
274            world
275                .resource_mut::<Time<Physics>>()
276                .advance_by(Duration::ZERO);
277        }
278
279        // Set the generic `Time` resource back to the clock that was active before physics.
280        *world.resource_mut::<Time>() = old_clock;
281    });
282
283    is_first_run.0 = false;
284}
285
286fn update_last_physics_tick(
287    mut last_physics_tick: ResMut<LastPhysicsTick>,
288    system_change_tick: SystemChangeTick,
289) {
290    last_physics_tick.0 = system_change_tick.this_run();
291}
292
293/// Debug system that checks for NaNs and infinities in Avian components and
294/// reports the last location they were written to.
295#[cfg(debug_assertions)]
296fn assert_components_finite(
297    pos_query: Query<(Entity, Ref<Position>)>,
298    lin_vel_query: Query<(Entity, Ref<LinearVelocity>)>,
299    ang_vel_query: Query<(Entity, Ref<AngularVelocity>)>,
300) {
301    macro_rules! assert_finite {
302        ($ent:expr, $val:ident, $ty:ty) => {
303            debug_assert!(
304                $val.is_finite(),
305                "NaN or infinity found in Avian component: type={} entity={} location='{}' (enable feature \"bevy/track_location\" if location is empty)",
306                stringify!($ty),
307                $ent,
308                $val.changed_by()
309            );
310        };
311    }
312    for (entity, position) in pos_query {
313        assert_finite!(entity, position, Position);
314    }
315    for (entity, velocity) in lin_vel_query {
316        assert_finite!(entity, velocity, LinearVelocity);
317    }
318    for (entity, velocity) in ang_vel_query {
319        assert_finite!(entity, velocity, AngularVelocity);
320    }
321}