avian3d/dynamics/sleeping/
mod.rs

1//! Manages sleeping and waking for bodies, automatically deactivating them to save computational resources.
2//!
3//! See [`SleepingPlugin`].
4
5use crate::prelude::*;
6use bevy::{
7    ecs::{component::Tick, system::SystemChangeTick},
8    prelude::*,
9};
10
11/// Manages sleeping and waking for bodies, automatically deactivating them to save computational resources.
12///
13/// Bodies are marked as [`Sleeping`] when their linear and angular velocities are below the [`SleepingThreshold`]
14/// for a duration indicated by [`DeactivationTime`].
15///
16/// Bodies are woken up when an active body or constraint interacts with them, or when gravity changes,
17/// or when the body's position, rotation, velocity, or external forces are changed.
18///
19/// This plugin does *not* handle constraints waking up bodies. That is done by the [solver](dynamics::solver).
20///
21/// The sleeping systems run in [`PhysicsStepSet::Sleeping`].
22pub struct SleepingPlugin;
23
24impl Plugin for SleepingPlugin {
25    fn build(&self, app: &mut App) {
26        // TODO: This is only relevant for dynamic bodies. Different bodies should be distinguished with marker components.
27        // Add sleep timer for all rigid bodies.
28        let _ = app.try_register_required_components::<RigidBody, TimeSleeping>();
29
30        app.init_resource::<SleepingThreshold>()
31            .init_resource::<DeactivationTime>()
32            .init_resource::<LastPhysicsTick>();
33
34        let physics_schedule = app
35            .get_schedule_mut(PhysicsSchedule)
36            .expect("add PhysicsSchedule first");
37
38        // TODO: Where exactly should this be in the schedule?
39        physics_schedule.add_systems(
40            (
41                wake_on_changed,
42                wake_all_sleeping_bodies.run_if(resource_changed::<Gravity>),
43                mark_sleeping_bodies,
44            )
45                .chain()
46                .after(PhysicsStepSet::First)
47                .before(PhysicsStepSet::BroadPhase),
48        );
49
50        physics_schedule.add_systems(
51            (|mut last_physics_tick: ResMut<LastPhysicsTick>,
52              system_change_tick: SystemChangeTick| {
53                last_physics_tick.0 = system_change_tick.this_run();
54            })
55            .after(PhysicsStepSet::Last),
56        );
57    }
58}
59
60/// A threshold that indicates the maximum linear and angular velocity allowed for a body to be deactivated.
61///
62/// Setting a negative sleeping threshold disables sleeping entirely.
63///
64/// See [`Sleeping`] for further information about sleeping.
65#[derive(Reflect, Resource, Clone, Copy, PartialEq, PartialOrd, Debug)]
66#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
67#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
68#[reflect(Debug, Resource, PartialEq)]
69pub struct SleepingThreshold {
70    /// The maximum linear velocity allowed for a body to be marked as sleeping.
71    ///
72    /// This is implicitly scaled by the [`PhysicsLengthUnit`].
73    ///
74    /// Default: `0.15`
75    pub linear: Scalar,
76    /// The maximum angular velocity allowed for a body to be marked as sleeping.
77    ///
78    /// Default: `0.15`
79    pub angular: Scalar,
80}
81
82impl Default for SleepingThreshold {
83    fn default() -> Self {
84        Self {
85            linear: 0.15,
86            angular: 0.15,
87        }
88    }
89}
90
91/// How long in seconds the linear and angular velocity of a body need to be below
92/// the [`SleepingThreshold`] before the body is deactivated. Defaults to 1 second.
93///
94/// See [`Sleeping`] for further information about sleeping.
95///
96/// Default: `0.5`
97#[derive(Reflect, Resource, Clone, Copy, PartialEq, PartialOrd, Debug)]
98#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
99#[cfg_attr(feature = "serialize", reflect(Serialize, Deserialize))]
100#[reflect(Debug, Default, PartialEq)]
101pub struct DeactivationTime(pub Scalar);
102
103impl Default for DeactivationTime {
104    fn default() -> Self {
105        Self(0.5)
106    }
107}
108
109/// A [`Command`] that wakes up a [rigid body](RigidBody) by removing the [`Sleeping`] component
110/// and resetting the [`TimeSleeping`] to zero.
111pub struct WakeUpBody(pub Entity);
112
113impl Command for WakeUpBody {
114    fn apply(self, world: &mut World) {
115        let Ok(mut entity_mut) = world.get_entity_mut(self.0) else {
116            return;
117        };
118
119        entity_mut.remove::<Sleeping>();
120
121        if let Some(mut time_sleeping) = entity_mut.get_mut::<TimeSleeping>() {
122            time_sleeping.0 = 0.0;
123        };
124    }
125}
126
127/// Adds the [`Sleeping`] component to bodies whose linear and anigular velocities have been
128/// under the [`SleepingThreshold`] for a duration indicated by [`DeactivationTime`].
129#[allow(clippy::type_complexity)]
130pub fn mark_sleeping_bodies(
131    mut commands: Commands,
132    mut query: Query<
133        (
134            Entity,
135            &RigidBody,
136            &mut LinearVelocity,
137            &mut AngularVelocity,
138            &mut TimeSleeping,
139        ),
140        (Without<Sleeping>, Without<SleepingDisabled>),
141    >,
142    contact_graph: Res<ContactGraph>,
143    rb_query: Query<&RigidBody>,
144    deactivation_time: Res<DeactivationTime>,
145    sleep_threshold: Res<SleepingThreshold>,
146    length_unit: Res<PhysicsLengthUnit>,
147    time: Res<Time>,
148) {
149    let length_unit_sq = length_unit.powi(2);
150    let delta_secs = time.delta_seconds_adjusted();
151
152    for (entity, rb, mut lin_vel, mut ang_vel, mut time_sleeping) in &mut query {
153        let colliding_entities = contact_graph.collisions_with(entity).map(|c| {
154            if entity == c.collider1 {
155                c.collider2
156            } else {
157                c.collider1
158            }
159        });
160
161        // Only dynamic bodies can sleep, and only if they are not
162        // in contact with other dynamic bodies.
163        //
164        // Contacts with other types of bodies will be allowed once
165        // sleeping/waking is implemented with simulation islands.
166        if !rb.is_dynamic()
167            || rb_query
168                .iter_many(colliding_entities)
169                .any(|rb| rb.is_dynamic())
170        {
171            continue;
172        }
173
174        let lin_vel_sq = lin_vel.length_squared();
175
176        #[cfg(feature = "2d")]
177        let ang_vel_sq = ang_vel.0.powi(2);
178        #[cfg(feature = "3d")]
179        let ang_vel_sq = ang_vel.0.dot(ang_vel.0);
180
181        // Negative thresholds indicate that sleeping is disabled.
182        let lin_sleeping_threshold_sq =
183            length_unit_sq * sleep_threshold.linear * sleep_threshold.linear.abs();
184        let ang_sleeping_threshold_sq = sleep_threshold.angular * sleep_threshold.angular.abs();
185
186        // If linear and angular velocity are below the sleeping threshold,
187        // add delta time to the time sleeping, i.e. the time that the body has remained still.
188        if lin_vel_sq < lin_sleeping_threshold_sq && ang_vel_sq < ang_sleeping_threshold_sq {
189            time_sleeping.0 += delta_secs;
190        } else {
191            time_sleeping.0 = 0.0;
192        }
193
194        // If the body has been still for long enough, set it to sleep and reset velocities.
195        if time_sleeping.0 > deactivation_time.0 {
196            commands.entity(entity).try_insert(Sleeping);
197            *lin_vel = LinearVelocity::ZERO;
198            *ang_vel = AngularVelocity::ZERO;
199        }
200    }
201}
202
203/// A [`Tick`] corresponding to the end of the previous run of the [`PhysicsSchedule`].
204#[derive(Resource, Reflect, Default)]
205#[reflect(Resource, Default)]
206pub(crate) struct LastPhysicsTick(pub Tick);
207
208/// Removes the [`Sleeping`] component from sleeping bodies when properties like
209/// position, rotation, velocity and external forces are changed by the user.
210#[allow(clippy::type_complexity)]
211pub(crate) fn wake_on_changed(
212    mut commands: Commands,
213    mut query: ParamSet<(
214        // These could've been changed by physics too.
215        // We need to ignore non-user changes.
216        Query<
217            (
218                Entity,
219                Ref<Position>,
220                Ref<Rotation>,
221                Ref<LinearVelocity>,
222                Ref<AngularVelocity>,
223            ),
224            (
225                With<Sleeping>,
226                Or<(
227                    Changed<Position>,
228                    Changed<Rotation>,
229                    Changed<LinearVelocity>,
230                    Changed<AngularVelocity>,
231                )>,
232            ),
233        >,
234        // These are not modified by the physics engine
235        // and don't need special handling.
236        Query<
237            Entity,
238            Or<(
239                Changed<ExternalForce>,
240                Changed<ExternalTorque>,
241                Changed<ExternalImpulse>,
242                Changed<ExternalAngularImpulse>,
243                Changed<GravityScale>,
244            )>,
245        >,
246    )>,
247    last_physics_tick: Res<LastPhysicsTick>,
248    system_tick: SystemChangeTick,
249) {
250    let this_run = system_tick.this_run();
251
252    for (entity, pos, rot, lin_vel, ang_vel) in &query.p0() {
253        if is_changed_after_tick(pos, last_physics_tick.0, this_run)
254            || is_changed_after_tick(rot, last_physics_tick.0, this_run)
255            || is_changed_after_tick(lin_vel, last_physics_tick.0, this_run)
256            || is_changed_after_tick(ang_vel, last_physics_tick.0, this_run)
257        {
258            commands.queue(WakeUpBody(entity));
259        }
260    }
261
262    for entity in &query.p1() {
263        commands.queue(WakeUpBody(entity));
264    }
265}
266
267fn is_changed_after_tick<C: Component>(component_ref: Ref<C>, tick: Tick, this_run: Tick) -> bool {
268    let last_changed = component_ref.last_changed();
269    component_ref.is_changed() && last_changed.is_newer_than(tick, this_run)
270}
271
272/// Removes the [`Sleeping`] component from all sleeping bodies.
273/// Triggered automatically when [`Gravity`] is changed.
274fn wake_all_sleeping_bodies(mut commands: Commands, bodies: Query<Entity, With<Sleeping>>) {
275    for entity in &bodies {
276        commands.queue(WakeUpBody(entity));
277    }
278}