using Ghost.Entities; using Ghost.Engine.Components; namespace Ghost.Engine.Systems; /// /// Provides utility methods for working with entity hierarchies. /// public static class HierarchyUtility { /// /// Sets the parent of an entity, updating the Hierarchy component accordingly. /// /// The world containing the entities. /// The child entity. /// The parent entity, or Entity.Invalid to make the entity a root. public static void SetParent(World world, Entity child, Entity parent) { if (!world.EntityManager.HasComponent(child)) { world.EntityManager.AddComponent(child, Hierarchy.Root); } ref var childHierarchy = ref world.EntityManager.GetComponent(child); // Remove from old parent's children list if (childHierarchy.parent.IsValid) { RemoveFromSiblingList(world, child, childHierarchy.parent); } // Set new parent childHierarchy.parent = parent; if (parent.IsValid) { if (!world.EntityManager.HasComponent(parent)) { world.EntityManager.AddComponent(parent, Hierarchy.Root); } ref var parentHierarchy = ref world.EntityManager.GetComponent(parent); // Add to parent's children list childHierarchy.nextSibling = parentHierarchy.firstChild; parentHierarchy.firstChild = child; } else { childHierarchy.nextSibling = Entity.Invalid; } } /// /// Gets the parent of an entity. /// /// The world containing the entity. /// The entity to get the parent of. /// The parent entity, or Entity.Invalid if the entity has no parent. public static Entity GetParent(World world, Entity entity) { if (!world.EntityManager.HasComponent(entity)) { return Entity.Invalid; } ref var hierarchy = ref world.EntityManager.GetComponent(entity); return hierarchy.parent; } /// /// Gets all children of an entity. /// /// The world containing the entity. /// The parent entity. /// Span to store the children. /// The number of children written to the span. public static int GetChildren(World world, Entity parent, Span children) { if (!world.EntityManager.HasComponent(parent)) { return 0; } ref var hierarchy = ref world.EntityManager.GetComponent(parent); var currentChild = hierarchy.firstChild; var count = 0; while (currentChild.IsValid && count < children.Length) { children[count++] = currentChild; if (world.EntityManager.HasComponent(currentChild)) { ref var childHierarchy = ref world.EntityManager.GetComponent(currentChild); currentChild = childHierarchy.nextSibling; } else { break; } } return count; } /// /// Gets all descendants of an entity (children, grandchildren, etc.) in depth-first order. /// /// The world containing the entity. /// The root entity. /// List to store the descendants. public static void GetDescendants(World world, Entity root, List descendants) { Span children = stackalloc Entity[32]; var childCount = GetChildren(world, root, children); for (int i = 0; i < childCount; i++) { var child = children[i]; descendants.Add(child); GetDescendants(world, child, descendants); } } /// /// Removes a child from its parent's sibling list. /// private static void RemoveFromSiblingList(World world, Entity child, Entity parent) { if (!world.EntityManager.HasComponent(parent)) { return; } ref var parentHierarchy = ref world.EntityManager.GetComponent(parent); ref var childHierarchy = ref world.EntityManager.GetComponent(child); // If child is the first child if (parentHierarchy.firstChild.Equals(child)) { parentHierarchy.firstChild = childHierarchy.nextSibling; childHierarchy.nextSibling = Entity.Invalid; return; } // Find the previous sibling var currentSibling = parentHierarchy.firstChild; while (currentSibling.IsValid) { if (!world.EntityManager.HasComponent(currentSibling)) { break; } ref var siblingHierarchy = ref world.EntityManager.GetComponent(currentSibling); if (siblingHierarchy.nextSibling.Equals(child)) { siblingHierarchy.nextSibling = childHierarchy.nextSibling; childHierarchy.nextSibling = Entity.Invalid; return; } currentSibling = siblingHierarchy.nextSibling; } } /// /// Checks if an entity is an ancestor of another entity. /// /// The world containing the entities. /// The potential ancestor entity. /// The descendant entity. /// True if potentialAncestor is an ancestor of descendant, false otherwise. public static bool IsAncestor(World world, Entity potentialAncestor, Entity descendant) { var current = GetParent(world, descendant); while (current.IsValid) { if (current.Equals(potentialAncestor)) { return true; } current = GetParent(world, current); } return false; } }