bevy_hierarchy/
hierarchy.rs

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/// Despawns the given entity and all its children recursively
10#[derive(Debug)]
11pub struct DespawnRecursive {
12    /// Target entity
13    pub entity: Entity,
14    /// Whether or not this command should output a warning if the entity does not exist
15    pub warn: bool,
16}
17
18/// Despawns the given entity's children recursively
19#[derive(Debug)]
20pub struct DespawnChildrenRecursive {
21    /// Target entity
22    pub entity: Entity,
23    /// Whether or not this command should output a warning if the entity does not exist
24    pub warn: bool,
25}
26
27/// Function for despawning an entity and all its children
28pub fn despawn_with_children_recursive(world: &mut World, entity: Entity, warn: bool) {
29    // first, make the entity's own parent forget about it
30    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    // then despawn the entity and all of its children
37    despawn_with_children_recursive_inner(world, entity, warn);
38}
39
40// Should only be called by `despawn_with_children_recursive` and `try_despawn_with_children_recursive`!
41fn 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
94/// Trait that holds functions for despawning recursively down the transform hierarchy
95pub trait DespawnRecursiveExt {
96    /// Despawns the provided entity alongside all descendants.
97    fn despawn_recursive(self);
98
99    /// Despawns all descendants of the given entity.
100    fn despawn_descendants(&mut self) -> &mut Self;
101
102    /// Similar to [`Self::despawn_recursive`] but does not emit warnings
103    fn try_despawn_recursive(self);
104
105    /// Similar to [`Self::despawn_descendants`] but does not emit warnings
106    fn try_despawn_descendants(&mut self) -> &mut Self;
107}
108
109impl DespawnRecursiveExt for EntityCommands<'_> {
110    /// Despawns the provided entity and its children.
111    /// This will emit warnings for any entity that does not exist.
112    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    /// Despawns the provided entity and its children.
126    /// This will never emit warnings.
127    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    /// Despawns the provided entity and its children.
181    /// This will emit warnings for any entity that does not exist.
182    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    /// Despawns the provided entity and its children.
191    /// This will not emit warnings.
192    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            // Create a grandparent entity which will _not_ be deleted
236            grandparent_entity = commands.spawn((N("Grandparent".to_owned()), Idx(2))).id();
237            commands.entity(grandparent_entity).with_children(|parent| {
238                // Add a child to the grandparent (the "parent"), which will get deleted
239                parent
240                    .spawn((N("Parent, to be deleted".to_owned()), Idx(3)))
241                    // All descendants of the "parent" should also be deleted.
242                    .with_children(|parent| {
243                        parent
244                            .spawn((N("First Child, to be deleted".to_owned()), Idx(4)))
245                            .with_children(|parent| {
246                                // child
247                                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            // despawning the same entity twice should not panic
266            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        // The parent's Children component should be removed.
313        assert!(world.entity(parent).get::<Children>().is_none());
314        // The child should be despawned.
315        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        // The parent's Children component should still have two children.
339        let children = world.entity(parent).get::<Children>();
340        assert!(children.is_some());
341        assert_eq!(children.unwrap().len(), 2_usize);
342        // The original child should be despawned.
343        assert!(world.get_entity(child).is_err());
344    }
345}