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}
15
16/// Despawns the given entity's children recursively
17#[derive(Debug)]
18pub struct DespawnChildrenRecursive {
19    /// Target entity
20    pub entity: Entity,
21}
22
23/// Function for despawning an entity and all its children
24pub fn despawn_with_children_recursive(world: &mut World, entity: Entity) {
25    // first, make the entity's own parent forget about it
26    if let Some(parent) = world.get::<Parent>(entity).map(|parent| parent.0) {
27        if let Some(mut children) = world.get_mut::<Children>(parent) {
28            children.0.retain(|c| *c != entity);
29        }
30    }
31
32    // then despawn the entity and all of its children
33    despawn_with_children_recursive_inner(world, entity);
34}
35
36// Should only be called by `despawn_with_children_recursive`!
37fn despawn_with_children_recursive_inner(world: &mut World, entity: Entity) {
38    if let Some(mut children) = world.get_mut::<Children>(entity) {
39        for e in std::mem::take(&mut children.0) {
40            despawn_with_children_recursive_inner(world, e);
41        }
42    }
43
44    if !world.despawn(entity) {
45        debug!("Failed to despawn entity {:?}", entity);
46    }
47}
48
49fn despawn_children_recursive(world: &mut World, entity: Entity) {
50    if let Some(children) = world.entity_mut(entity).take::<Children>() {
51        for e in children.0 {
52            despawn_with_children_recursive_inner(world, e);
53        }
54    }
55}
56
57impl Command for DespawnRecursive {
58    fn apply(self, world: &mut World) {
59        #[cfg(feature = "trace")]
60        let _span = bevy_utils::tracing::info_span!(
61            "command",
62            name = "DespawnRecursive",
63            entity = bevy_utils::tracing::field::debug(self.entity)
64        )
65        .entered();
66        despawn_with_children_recursive(world, self.entity);
67    }
68}
69
70impl Command for DespawnChildrenRecursive {
71    fn apply(self, world: &mut World) {
72        #[cfg(feature = "trace")]
73        let _span = bevy_utils::tracing::info_span!(
74            "command",
75            name = "DespawnChildrenRecursive",
76            entity = bevy_utils::tracing::field::debug(self.entity)
77        )
78        .entered();
79        despawn_children_recursive(world, self.entity);
80    }
81}
82
83/// Trait that holds functions for despawning recursively down the transform hierarchy
84pub trait DespawnRecursiveExt {
85    /// Despawns the provided entity alongside all descendants.
86    fn despawn_recursive(self);
87
88    /// Despawns all descendants of the given entity.
89    fn despawn_descendants(&mut self) -> &mut Self;
90}
91
92impl DespawnRecursiveExt for EntityCommands<'_> {
93    /// Despawns the provided entity and its children.
94    /// This will emit warnings for any entity that does not exist.
95    fn despawn_recursive(mut self) {
96        let entity = self.id();
97        self.commands().add(DespawnRecursive { entity });
98    }
99
100    fn despawn_descendants(&mut self) -> &mut Self {
101        let entity = self.id();
102        self.commands().add(DespawnChildrenRecursive { entity });
103        self
104    }
105}
106
107impl<'w> DespawnRecursiveExt for EntityWorldMut<'w> {
108    /// Despawns the provided entity and its children.
109    /// This will emit warnings for any entity that does not exist.
110    fn despawn_recursive(self) {
111        let entity = self.id();
112
113        #[cfg(feature = "trace")]
114        let _span = bevy_utils::tracing::info_span!(
115            "despawn_recursive",
116            entity = bevy_utils::tracing::field::debug(entity)
117        )
118        .entered();
119
120        despawn_with_children_recursive(self.into_world_mut(), entity);
121    }
122
123    fn despawn_descendants(&mut self) -> &mut Self {
124        let entity = self.id();
125
126        #[cfg(feature = "trace")]
127        let _span = bevy_utils::tracing::info_span!(
128            "despawn_descendants",
129            entity = bevy_utils::tracing::field::debug(entity)
130        )
131        .entered();
132
133        self.world_scope(|world| {
134            despawn_children_recursive(world, entity);
135        });
136        self
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use bevy_ecs::{
143        component::Component,
144        system::Commands,
145        world::{CommandQueue, World},
146    };
147
148    use super::DespawnRecursiveExt;
149    use crate::{child_builder::BuildChildren, components::Children};
150
151    #[derive(Component, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Debug)]
152    struct Idx(u32);
153
154    #[derive(Component, Clone, PartialEq, Eq, Ord, PartialOrd, Debug)]
155    struct N(String);
156
157    #[test]
158    fn despawn_recursive() {
159        let mut world = World::default();
160        let mut queue = CommandQueue::default();
161        let grandparent_entity;
162        {
163            let mut commands = Commands::new(&mut queue, &world);
164
165            commands
166                .spawn((N("Another parent".to_owned()), Idx(0)))
167                .with_children(|parent| {
168                    parent.spawn((N("Another child".to_owned()), Idx(1)));
169                });
170
171            // Create a grandparent entity which will _not_ be deleted
172            grandparent_entity = commands.spawn((N("Grandparent".to_owned()), Idx(2))).id();
173            commands.entity(grandparent_entity).with_children(|parent| {
174                // Add a child to the grandparent (the "parent"), which will get deleted
175                parent
176                    .spawn((N("Parent, to be deleted".to_owned()), Idx(3)))
177                    // All descendants of the "parent" should also be deleted.
178                    .with_children(|parent| {
179                        parent
180                            .spawn((N("First Child, to be deleted".to_owned()), Idx(4)))
181                            .with_children(|parent| {
182                                // child
183                                parent.spawn((
184                                    N("First grand child, to be deleted".to_owned()),
185                                    Idx(5),
186                                ));
187                            });
188                        parent.spawn((N("Second child, to be deleted".to_owned()), Idx(6)));
189                    });
190            });
191
192            commands.spawn((N("An innocent bystander".to_owned()), Idx(7)));
193        }
194        queue.apply(&mut world);
195
196        let parent_entity = world.get::<Children>(grandparent_entity).unwrap()[0];
197
198        {
199            let mut commands = Commands::new(&mut queue, &world);
200            commands.entity(parent_entity).despawn_recursive();
201            // despawning the same entity twice should not panic
202            commands.entity(parent_entity).despawn_recursive();
203        }
204        queue.apply(&mut world);
205
206        let mut results = world
207            .query::<(&N, &Idx)>()
208            .iter(&world)
209            .map(|(a, b)| (a.clone(), *b))
210            .collect::<Vec<_>>();
211        results.sort_unstable_by_key(|(_, index)| *index);
212
213        {
214            let children = world.get::<Children>(grandparent_entity).unwrap();
215            assert!(
216                !children.iter().any(|&i| i == parent_entity),
217                "grandparent should no longer know about its child which has been removed"
218            );
219        }
220
221        assert_eq!(
222            results,
223            vec![
224                (N("Another parent".to_owned()), Idx(0)),
225                (N("Another child".to_owned()), Idx(1)),
226                (N("Grandparent".to_owned()), Idx(2)),
227                (N("An innocent bystander".to_owned()), Idx(7))
228            ]
229        );
230    }
231
232    #[test]
233    fn despawn_descendants() {
234        let mut world = World::default();
235        let mut queue = CommandQueue::default();
236        let mut commands = Commands::new(&mut queue, &world);
237
238        let parent = commands.spawn_empty().id();
239        let child = commands.spawn_empty().id();
240
241        commands
242            .entity(parent)
243            .add_child(child)
244            .despawn_descendants();
245
246        queue.apply(&mut world);
247
248        // The parent's Children component should be removed.
249        assert!(world.entity(parent).get::<Children>().is_none());
250        // The child should be despawned.
251        assert!(world.get_entity(child).is_none());
252    }
253
254    #[test]
255    fn spawn_children_after_despawn_descendants() {
256        let mut world = World::default();
257        let mut queue = CommandQueue::default();
258        let mut commands = Commands::new(&mut queue, &world);
259
260        let parent = commands.spawn_empty().id();
261        let child = commands.spawn_empty().id();
262
263        commands
264            .entity(parent)
265            .add_child(child)
266            .despawn_descendants()
267            .with_children(|parent| {
268                parent.spawn_empty();
269                parent.spawn_empty();
270            });
271
272        queue.apply(&mut world);
273
274        // The parent's Children component should still have two children.
275        let children = world.entity(parent).get::<Children>();
276        assert!(children.is_some());
277        assert_eq!(children.unwrap().len(), 2_usize);
278        // The original child should be despawned.
279        assert!(world.get_entity(child).is_none());
280    }
281}