avian3d/dynamics/sleeping/
mod.rs1use crate::prelude::*;
6use bevy::{
7 ecs::{component::Tick, system::SystemChangeTick},
8 prelude::*,
9};
10
11pub struct SleepingPlugin;
23
24impl Plugin for SleepingPlugin {
25 fn build(&self, app: &mut App) {
26 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 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#[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 pub linear: Scalar,
76 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#[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
109pub 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#[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 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 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 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 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#[derive(Resource, Reflect, Default)]
205#[reflect(Resource, Default)]
206pub(crate) struct LastPhysicsTick(pub Tick);
207
208#[allow(clippy::type_complexity)]
211pub(crate) fn wake_on_changed(
212 mut commands: Commands,
213 mut query: ParamSet<(
214 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 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
272fn wake_all_sleeping_bodies(mut commands: Commands, bodies: Query<Entity, With<Sleeping>>) {
275 for entity in &bodies {
276 commands.queue(WakeUpBody(entity));
277 }
278}