1use crate::components::{Children, Parent};
2use bevy_ecs::{
3 entity::Entity,
4 system::EntityCommands,
5 world::{Command, EntityWorldMut, World},
6};
7use bevy_utils::tracing::debug;
8
9#[derive(Debug)]
11pub struct DespawnRecursive {
12 pub entity: Entity,
14 pub warn: bool,
16}
17
18#[derive(Debug)]
20pub struct DespawnChildrenRecursive {
21 pub entity: Entity,
23 pub warn: bool,
25}
26
27pub fn despawn_with_children_recursive(world: &mut World, entity: Entity, warn: bool) {
29 if let Some(parent) = world.get::<Parent>(entity).map(|parent| parent.0) {
31 if let Some(mut children) = world.get_mut::<Children>(parent) {
32 children.0.retain(|c| *c != entity);
33 }
34 }
35
36 despawn_with_children_recursive_inner(world, entity, warn);
38}
39
40fn despawn_with_children_recursive_inner(world: &mut World, entity: Entity, warn: bool) {
42 if let Some(mut children) = world.get_mut::<Children>(entity) {
43 for e in core::mem::take(&mut children.0) {
44 despawn_with_children_recursive_inner(world, e, warn);
45 }
46 }
47
48 if warn {
49 if !world.despawn(entity) {
50 debug!("Failed to despawn entity {:?}", entity);
51 }
52 } else if !world.try_despawn(entity) {
53 debug!("Failed to despawn entity {:?}", entity);
54 }
55}
56
57fn despawn_children_recursive(world: &mut World, entity: Entity, warn: bool) {
58 if let Some(children) = world.entity_mut(entity).take::<Children>() {
59 for e in children.0 {
60 despawn_with_children_recursive_inner(world, e, warn);
61 }
62 }
63}
64
65impl Command for DespawnRecursive {
66 fn apply(self, world: &mut World) {
67 #[cfg(feature = "trace")]
68 let _span = bevy_utils::tracing::info_span!(
69 "command",
70 name = "DespawnRecursive",
71 entity = bevy_utils::tracing::field::debug(self.entity),
72 warn = bevy_utils::tracing::field::debug(self.warn)
73 )
74 .entered();
75 despawn_with_children_recursive(world, self.entity, self.warn);
76 }
77}
78
79impl Command for DespawnChildrenRecursive {
80 fn apply(self, world: &mut World) {
81 #[cfg(feature = "trace")]
82 let _span = bevy_utils::tracing::info_span!(
83 "command",
84 name = "DespawnChildrenRecursive",
85 entity = bevy_utils::tracing::field::debug(self.entity),
86 warn = bevy_utils::tracing::field::debug(self.warn)
87 )
88 .entered();
89
90 despawn_children_recursive(world, self.entity, self.warn);
91 }
92}
93
94pub trait DespawnRecursiveExt {
96 fn despawn_recursive(self);
98
99 fn despawn_descendants(&mut self) -> &mut Self;
101
102 fn try_despawn_recursive(self);
104
105 fn try_despawn_descendants(&mut self) -> &mut Self;
107}
108
109impl DespawnRecursiveExt for EntityCommands<'_> {
110 fn despawn_recursive(mut self) {
113 let entity = self.id();
114 self.commands()
115 .queue(DespawnRecursive { entity, warn: true });
116 }
117
118 fn despawn_descendants(&mut self) -> &mut Self {
119 let entity = self.id();
120 self.commands()
121 .queue(DespawnChildrenRecursive { entity, warn: true });
122 self
123 }
124
125 fn try_despawn_recursive(mut self) {
128 let entity = self.id();
129 self.commands().queue(DespawnRecursive {
130 entity,
131 warn: false,
132 });
133 }
134
135 fn try_despawn_descendants(&mut self) -> &mut Self {
136 let entity = self.id();
137 self.commands().queue(DespawnChildrenRecursive {
138 entity,
139 warn: false,
140 });
141 self
142 }
143}
144
145fn despawn_recursive_inner(world: EntityWorldMut, warn: bool) {
146 let entity = world.id();
147
148 #[cfg(feature = "trace")]
149 let _span = bevy_utils::tracing::info_span!(
150 "despawn_recursive",
151 entity = bevy_utils::tracing::field::debug(entity),
152 warn = bevy_utils::tracing::field::debug(warn)
153 )
154 .entered();
155
156 despawn_with_children_recursive(world.into_world_mut(), entity, warn);
157}
158
159fn despawn_descendants_inner<'v, 'w>(
160 world: &'v mut EntityWorldMut<'w>,
161 warn: bool,
162) -> &'v mut EntityWorldMut<'w> {
163 let entity = world.id();
164
165 #[cfg(feature = "trace")]
166 let _span = bevy_utils::tracing::info_span!(
167 "despawn_descendants",
168 entity = bevy_utils::tracing::field::debug(entity),
169 warn = bevy_utils::tracing::field::debug(warn)
170 )
171 .entered();
172
173 world.world_scope(|world| {
174 despawn_children_recursive(world, entity, warn);
175 });
176 world
177}
178
179impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> {
180 fn despawn_recursive(self) {
183 despawn_recursive_inner(self, true);
184 }
185
186 fn despawn_descendants(&mut self) -> &mut Self {
187 despawn_descendants_inner(self, true)
188 }
189
190 fn try_despawn_recursive(self) {
193 despawn_recursive_inner(self, false);
194 }
195
196 fn try_despawn_descendants(&mut self) -> &mut Self {
197 despawn_descendants_inner(self, false)
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use bevy_ecs::{
204 component::Component,
205 system::Commands,
206 world::{CommandQueue, World},
207 };
208
209 use super::DespawnRecursiveExt;
210 use crate::{
211 child_builder::{BuildChildren, ChildBuild},
212 components::Children,
213 };
214
215 #[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
216 struct Idx(u32);
217
218 #[derive(Component, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
219 struct N(String);
220
221 #[test]
222 fn despawn_recursive() {
223 let mut world = World::default();
224 let mut queue = CommandQueue::default();
225 let grandparent_entity;
226 {
227 let mut commands = Commands::new(&mut queue, &world);
228
229 commands
230 .spawn((N("Another parent".to_owned()), Idx(0)))
231 .with_children(|parent| {
232 parent.spawn((N("Another child".to_owned()), Idx(1)));
233 });
234
235 grandparent_entity = commands.spawn((N("Grandparent".to_owned()), Idx(2))).id();
237 commands.entity(grandparent_entity).with_children(|parent| {
238 parent
240 .spawn((N("Parent, to be deleted".to_owned()), Idx(3)))
241 .with_children(|parent| {
243 parent
244 .spawn((N("First Child, to be deleted".to_owned()), Idx(4)))
245 .with_children(|parent| {
246 parent.spawn((
248 N("First grand child, to be deleted".to_owned()),
249 Idx(5),
250 ));
251 });
252 parent.spawn((N("Second child, to be deleted".to_owned()), Idx(6)));
253 });
254 });
255
256 commands.spawn((N("An innocent bystander".to_owned()), Idx(7)));
257 }
258 queue.apply(&mut world);
259
260 let parent_entity = world.get::<Children>(grandparent_entity).unwrap()[0];
261
262 {
263 let mut commands = Commands::new(&mut queue, &world);
264 commands.entity(parent_entity).despawn_recursive();
265 commands.entity(parent_entity).despawn_recursive();
267 }
268 queue.apply(&mut world);
269
270 let mut results = world
271 .query::<(&N, &Idx)>()
272 .iter(&world)
273 .map(|(a, b)| (a.clone(), *b))
274 .collect::<Vec<_>>();
275 results.sort_unstable_by_key(|(_, index)| *index);
276
277 {
278 let children = world.get::<Children>(grandparent_entity).unwrap();
279 assert!(
280 !children.iter().any(|&i| i == parent_entity),
281 "grandparent should no longer know about its child which has been removed"
282 );
283 }
284
285 assert_eq!(
286 results,
287 vec![
288 (N("Another parent".to_owned()), Idx(0)),
289 (N("Another child".to_owned()), Idx(1)),
290 (N("Grandparent".to_owned()), Idx(2)),
291 (N("An innocent bystander".to_owned()), Idx(7))
292 ]
293 );
294 }
295
296 #[test]
297 fn despawn_descendants() {
298 let mut world = World::default();
299 let mut queue = CommandQueue::default();
300 let mut commands = Commands::new(&mut queue, &world);
301
302 let parent = commands.spawn_empty().id();
303 let child = commands.spawn_empty().id();
304
305 commands
306 .entity(parent)
307 .add_child(child)
308 .despawn_descendants();
309
310 queue.apply(&mut world);
311
312 assert!(world.entity(parent).get::<Children>().is_none());
314 assert!(world.get_entity(child).is_err());
316 }
317
318 #[test]
319 fn spawn_children_after_despawn_descendants() {
320 let mut world = World::default();
321 let mut queue = CommandQueue::default();
322 let mut commands = Commands::new(&mut queue, &world);
323
324 let parent = commands.spawn_empty().id();
325 let child = commands.spawn_empty().id();
326
327 commands
328 .entity(parent)
329 .add_child(child)
330 .despawn_descendants()
331 .with_children(|parent| {
332 parent.spawn_empty();
333 parent.spawn_empty();
334 });
335
336 queue.apply(&mut world);
337
338 let children = world.entity(parent).get::<Children>();
340 assert!(children.is_some());
341 assert_eq!(children.unwrap().len(), 2_usize);
342 assert!(world.get_entity(child).is_err());
344 }
345}