Skip to main content

bevy_rapier2d/plugin/systems/
remove.rs

1use crate::dynamics::ImpulseJoint;
2use crate::dynamics::MultibodyJoint;
3use crate::dynamics::RapierImpulseJointHandle;
4use crate::dynamics::RapierMultibodyJointHandle;
5use crate::dynamics::RapierRigidBodyHandle;
6use crate::dynamics::RigidBody;
7use crate::geometry::Collider;
8use crate::geometry::ColliderDisabled;
9use crate::geometry::RapierColliderHandle;
10use crate::plugin::context::{
11    RapierContextColliders, RapierContextJoints, RapierContextSimulation, RapierRigidBodySet,
12};
13use crate::prelude::MassModifiedEvent;
14use crate::prelude::RigidBodyDisabled;
15use crate::prelude::Sensor;
16use bevy::ecs::query::IterQueryData;
17use bevy::prelude::*;
18
19/// System responsible for removing from Rapier the rigid-bodies/colliders/joints which had
20/// their related `bevy_rapier` components removed by the user (through component removal or
21/// despawn).
22pub fn sync_removals(
23    mut commands: Commands,
24    mut context_writer: Query<(
25        &mut RapierContextSimulation,
26        &mut RapierContextColliders,
27        &mut RapierContextJoints,
28        &mut RapierRigidBodySet,
29    )>,
30    // Sometimes a Remove immediately followed by Add happens. These `q_has_*` queries prevent that immediate Add
31    // from being removed by this system by verifying it's still removed.
32    (
33        q_has_rigidbody_handle,
34        q_has_collider_handle,
35        q_has_multibody_joint_handle,
36        q_has_impulse_joint_handle,
37    ): (
38        Query<(), With<RapierRigidBodyHandle>>,
39        Query<(), With<RapierColliderHandle>>,
40        Query<(), With<RapierMultibodyJointHandle>>,
41        Query<(), With<RapierImpulseJointHandle>>,
42    ),
43    mut removed_bodies: RemovedComponents<RapierRigidBodyHandle>,
44    mut removed_colliders: RemovedComponents<RapierColliderHandle>,
45    mut removed_impulse_joints: RemovedComponents<RapierImpulseJointHandle>,
46    mut removed_multibody_joints: RemovedComponents<RapierMultibodyJointHandle>,
47    orphan_bodies: Query<Entity, (With<RapierRigidBodyHandle>, Without<RigidBody>)>,
48    orphan_colliders: Query<Entity, (With<RapierColliderHandle>, Without<Collider>)>,
49    orphan_impulse_joints: Query<Entity, (With<RapierImpulseJointHandle>, Without<ImpulseJoint>)>,
50    orphan_multibody_joints: Query<
51        Entity,
52        (With<RapierMultibodyJointHandle>, Without<MultibodyJoint>),
53    >,
54
55    mut removed_sensors: RemovedComponents<Sensor>,
56    mut removed_rigid_body_disabled: RemovedComponents<RigidBodyDisabled>,
57    mut removed_colliders_disabled: RemovedComponents<ColliderDisabled>,
58
59    mut mass_modified: MessageWriter<MassModifiedEvent>,
60) {
61    /*
62     * Rigid-bodies removal detection.
63     */
64    for entity in removed_bodies
65        .read()
66        .filter(|e| !q_has_rigidbody_handle.contains(*e))
67    {
68        let Some(((mut context, mut context_colliders, mut joints, mut rigidbody_set), handle)) =
69            find_context(&mut context_writer, |res| res.3.entity2body.remove(&entity))
70        else {
71            continue;
72        };
73        let context = &mut *context;
74        let joints = &mut *joints;
75
76        let _ = rigidbody_set.last_body_transform_set.remove(&handle);
77        rigidbody_set.bodies.remove(
78            handle,
79            &mut context.islands,
80            &mut context_colliders.colliders,
81            &mut joints.impulse_joints,
82            &mut joints.multibody_joints,
83            false,
84        );
85    }
86
87    for entity in orphan_bodies.iter() {
88        if let Some(((mut context, mut context_colliders, mut joints, mut rigidbody_set), handle)) =
89            find_context(&mut context_writer, |res| res.3.entity2body.remove(&entity))
90        {
91            let context = &mut *context;
92            let joints = &mut *joints;
93            let _ = rigidbody_set.last_body_transform_set.remove(&handle);
94            rigidbody_set.bodies.remove(
95                handle,
96                &mut context.islands,
97                &mut context_colliders.colliders,
98                &mut joints.impulse_joints,
99                &mut joints.multibody_joints,
100                false,
101            );
102        }
103        commands.entity(entity).remove::<RapierRigidBodyHandle>();
104    }
105
106    /*
107     * Collider removal detection.
108     */
109    for entity in removed_colliders
110        .read()
111        .filter(|e| !q_has_collider_handle.contains(*e))
112    {
113        let Some(((mut context, mut context_colliders, _, mut rigidbody_set), handle)) =
114            find_context(&mut context_writer, |res| {
115                res.1.entity2collider.remove(&entity)
116            })
117        else {
118            continue;
119        };
120        let context = &mut *context;
121        if let Some(parent) = context_colliders.collider_parent(&rigidbody_set, entity) {
122            mass_modified.write(parent.into());
123        }
124
125        context_colliders.colliders.remove(
126            handle,
127            &mut context.islands,
128            &mut rigidbody_set.bodies,
129            true,
130        );
131        context.deleted_colliders.insert(handle, entity);
132    }
133
134    for entity in orphan_colliders.iter() {
135        if let Some(((mut context, mut context_colliders, _, mut rigidbody_set), handle)) =
136            find_context(&mut context_writer, |res| {
137                res.1.entity2collider.remove(&entity)
138            })
139        {
140            let context = &mut *context;
141            let context_colliders = &mut *context_colliders;
142            if let Some(parent) = context_colliders.collider_parent(&rigidbody_set, entity) {
143                mass_modified.write(parent.into());
144            }
145
146            context_colliders.colliders.remove(
147                handle,
148                &mut context.islands,
149                &mut rigidbody_set.bodies,
150                true,
151            );
152            context.deleted_colliders.insert(handle, entity);
153        }
154        commands.entity(entity).remove::<RapierColliderHandle>();
155    }
156
157    /*
158     * Impulse joint removal detection.
159     */
160    for entity in removed_impulse_joints
161        .read()
162        .filter(|e| !q_has_impulse_joint_handle.contains(*e))
163    {
164        let Some(((_, _, mut joints, _), handle)) = find_context(&mut context_writer, |res| {
165            res.2.entity2impulse_joint.remove(&entity)
166        }) else {
167            continue;
168        };
169        joints.impulse_joints.remove(handle, true);
170    }
171
172    for entity in orphan_impulse_joints.iter() {
173        if let Some(((_, _, mut joints, _), handle)) = find_context(&mut context_writer, |res| {
174            res.2.entity2impulse_joint.remove(&entity)
175        }) {
176            joints.impulse_joints.remove(handle, true);
177        }
178        commands.entity(entity).remove::<RapierImpulseJointHandle>();
179    }
180
181    /*
182     * Multibody joint removal detection.
183     */
184    for entity in removed_multibody_joints
185        .read()
186        .filter(|e| !q_has_multibody_joint_handle.contains(*e))
187    {
188        let Some(((_, _, mut joints, _), handle)) = find_context(&mut context_writer, |res| {
189            res.2.entity2multibody_joint.remove(&entity)
190        }) else {
191            continue;
192        };
193        joints.multibody_joints.remove(handle, true);
194    }
195
196    for entity in orphan_multibody_joints.iter() {
197        if let Some(((_, _, mut joints, _), handle)) = find_context(&mut context_writer, |res| {
198            res.2.entity2multibody_joint.remove(&entity)
199        }) {
200            joints.multibody_joints.remove(handle, true);
201        }
202        commands
203            .entity(entity)
204            .remove::<RapierMultibodyJointHandle>();
205    }
206
207    /*
208     * Marker components removal detection.
209     */
210    for entity in removed_sensors.read() {
211        if let Some((mut context, handle)) = find_context(&mut context_writer, |context| {
212            context.1.entity2collider.get(&entity).copied()
213        }) {
214            if let Some(co) = context.1.colliders.get_mut(handle) {
215                co.set_sensor(false);
216            }
217        }
218    }
219
220    for entity in removed_colliders_disabled.read() {
221        if let Some((mut context, handle)) = find_context(&mut context_writer, |context| {
222            context.1.entity2collider.get(&entity).copied()
223        }) {
224            if let Some(co) = context.1.colliders.get_mut(handle) {
225                co.set_enabled(true);
226            }
227        }
228    }
229
230    for entity in removed_rigid_body_disabled.read() {
231        if let Some(((_, _, _, mut rigidbody_set), handle)) =
232            find_context(&mut context_writer, |res| {
233                res.3.entity2body.get(&entity).copied()
234            })
235        {
236            if let Some(rb) = rigidbody_set.bodies.get_mut(handle) {
237                rb.set_enabled(true);
238            }
239        }
240    }
241
242    // TODO: what about removing forces?
243}
244
245fn find_context<'a, TReturn, TQueryParams: IterQueryData>(
246    context_writer: &'a mut Query<TQueryParams>,
247    item_finder: impl Fn(&mut TQueryParams::Item<'_, '_>) -> Option<TReturn>,
248) -> Option<(TQueryParams::Item<'a, 'a>, TReturn)> {
249    let ret: Option<(TQueryParams::Item<'_, '_>, TReturn)> = context_writer
250        .iter_mut()
251        .find_map(|mut context| item_finder(&mut context).map(|handle| (context, handle)));
252    ret
253}