Skip to main content

gizmo_core/
hierarchy.rs

1use crate::component::{Children, Parent};
2use crate::entity::Entity;
3use crate::world::World;
4
5/// Extends `World` with hierarchy manipulation methods.
6pub trait HierarchyExt {
7    /// Despawns an entity and all of its descendants recursively.
8    fn despawn_recursive(&mut self, entity: Entity);
9    
10    /// Adds a child to a parent entity, updating both `Parent` and `Children` components.
11    fn add_child(&mut self, parent: Entity, child: Entity);
12    
13    /// Removes a child from a parent entity.
14    fn remove_child(&mut self, parent: Entity, child: Entity);
15}
16
17impl HierarchyExt for World {
18    fn despawn_recursive(&mut self, entity: Entity) {
19        let mut children_to_despawn = Vec::new();
20        
21        if let Some(children_ptr) = self.get_component_ptr(entity, std::any::TypeId::of::<Children>()) {
22            let children = unsafe { &*(children_ptr as *const Children) };
23            for &child_id in &children.0 {
24                if let Some(child_entity) = self.reconstruct_entity(child_id) {
25                    children_to_despawn.push(child_entity);
26                }
27            }
28        }
29        
30        // Remove child from parent's list if it has a Parent
31        if let Some(parent_ptr) = self.get_component_ptr(entity, std::any::TypeId::of::<Parent>()) {
32            let parent_id = unsafe { (*(parent_ptr as *const Parent)).0 };
33            if let Some(parent_entity) = self.reconstruct_entity(parent_id) {
34                self.remove_child(parent_entity, entity);
35            }
36        }
37
38        // Recursively despawn children
39        for child in children_to_despawn {
40            self.despawn_recursive(child);
41        }
42        
43        self.despawn(entity);
44    }
45
46    fn add_child(&mut self, parent: Entity, child: Entity) {
47        // Remove from old parent first
48        if let Some(parent_ptr) = self.get_component_ptr(child, std::any::TypeId::of::<Parent>()) {
49            let old_parent_id = unsafe { (*(parent_ptr as *const Parent)).0 };
50            if old_parent_id != parent.id() {
51                if let Some(old_parent) = self.reconstruct_entity(old_parent_id) {
52                    self.remove_child(old_parent, child);
53                }
54            }
55        }
56
57        // Add Parent component to child
58        self.add_component(child, Parent(parent.id()));
59
60        // Add to new parent's Children list
61        if let Some(children_ptr) = self.get_component_mut_ptr(parent, std::any::TypeId::of::<Children>()) {
62            let children = unsafe { &mut *(children_ptr as *mut Children) };
63            if !children.0.contains(&child.id()) {
64                children.0.push(child.id());
65            }
66        } else {
67            self.add_component(parent, Children(vec![child.id()]));
68        }
69    }
70
71    fn remove_child(&mut self, parent: Entity, child: Entity) {
72        self.remove_component::<Parent>(child);
73        
74        if let Some(children_ptr) = self.get_component_mut_ptr(parent, std::any::TypeId::of::<Children>()) {
75            let children = unsafe { &mut *(children_ptr as *mut Children) };
76            children.0.retain(|&id| id != child.id());
77        }
78    }
79}
80
81#[cfg(test)]
82mod tests {
83    use super::*;
84    use crate::world::World;
85
86    #[test]
87    fn test_hierarchy_add_remove() {
88        let mut world = World::new();
89        let parent = world.spawn();
90        let child = world.spawn();
91
92        world.add_child(parent, child);
93
94        // Check if Parent component is added to child
95        if let Some(parent_ptr) = world.get_component_ptr(child, std::any::TypeId::of::<Parent>()) {
96            let parent_id = unsafe { (*(parent_ptr as *const Parent)).0 };
97            assert_eq!(parent_id, parent.id());
98        } else {
99            panic!("Child missing Parent component");
100        }
101
102        // Check if Children component is updated
103        if let Some(children_ptr) = world.get_component_ptr(parent, std::any::TypeId::of::<Children>()) {
104            let children = unsafe { &*(children_ptr as *const Children) };
105            assert_eq!(children.0.len(), 1);
106            assert_eq!(children.0[0], child.id());
107        } else {
108            panic!("Parent missing Children component");
109        }
110
111        // Remove child
112        world.remove_child(parent, child);
113
114        // Child should not have Parent component anymore
115        assert!(world.get_component_ptr(child, std::any::TypeId::of::<Parent>()).is_none());
116
117        // Parent should have empty Children list
118        if let Some(children_ptr) = world.get_component_ptr(parent, std::any::TypeId::of::<Children>()) {
119            let children = unsafe { &*(children_ptr as *const Children) };
120            assert_eq!(children.0.len(), 0);
121        }
122    }
123
124    #[test]
125    fn test_despawn_recursive() {
126        let mut world = World::new();
127        let p1 = world.spawn();
128        let c1 = world.spawn();
129        let c2 = world.spawn();
130        let gc1 = world.spawn();
131
132        world.add_child(p1, c1);
133        world.add_child(p1, c2);
134        world.add_child(c1, gc1);
135
136        assert_eq!(world.entity_count(), 4);
137
138        // Despawn root
139        world.despawn_recursive(p1);
140
141        // Entities should be marked for despawn, process them by calling despawn queue? 
142        // Wait, despawn is immediate through `entities_to_despawn` loop
143        assert_eq!(world.entity_count(), 0);
144    }
145}