Hierarchy System
In game development, parent-child hierarchy relationships between entities are common requirements. ECS Framework manages hierarchy relationships through a component-based approach using HierarchyComponent and HierarchySystem, fully adhering to ECS composition principles.
Design Philosophy
Section titled “Design Philosophy”Why Not Built-in Hierarchy in Entity?
Section titled “Why Not Built-in Hierarchy in Entity?”Traditional game object models build hierarchy into entities. ECS Framework chose a component-based approach because:
- ECS Composition Principle: Hierarchy is a “feature” that should be added through components, not inherent to all entities
- On-Demand Usage: Only entities that need hierarchy add
HierarchyComponent - Data-Logic Separation:
HierarchyComponentstores data,HierarchySystemhandles logic - Serialization-Friendly: Hierarchy as component data can be easily serialized/deserialized
Basic Concepts
Section titled “Basic Concepts”HierarchyComponent
Section titled “HierarchyComponent”Component storing hierarchy relationship data:
import { HierarchyComponent } from '@esengine/ecs-framework';
interface HierarchyComponent { parentId: number | null; // Parent entity ID, null means root childIds: number[]; // Child entity ID list depth: number; // Depth in hierarchy (maintained by system) bActiveInHierarchy: boolean; // Active in hierarchy (maintained by system)}HierarchySystem
Section titled “HierarchySystem”System handling hierarchy logic, provides all hierarchy operation APIs:
import { HierarchySystem } from '@esengine/ecs-framework';
const hierarchySystem = scene.getEntityProcessor(HierarchySystem);Quick Start
Section titled “Quick Start”Add System to Scene
Section titled “Add System to Scene”import { Scene, HierarchySystem } from '@esengine/ecs-framework';
class GameScene extends Scene { protected initialize(): void { this.addSystem(new HierarchySystem()); }}Establish Parent-Child Relationships
Section titled “Establish Parent-Child Relationships”const parent = scene.createEntity("Parent");const child1 = scene.createEntity("Child1");const child2 = scene.createEntity("Child2");
const hierarchySystem = scene.getEntityProcessor(HierarchySystem);
// Set parent-child relationship (auto-adds HierarchyComponent)hierarchySystem.setParent(child1, parent);hierarchySystem.setParent(child2, parent);Query Hierarchy
Section titled “Query Hierarchy”// Get parent entityconst parentEntity = hierarchySystem.getParent(child1);
// Get all childrenconst children = hierarchySystem.getChildren(parent);
// Get child countconst count = hierarchySystem.getChildCount(parent);
// Check if has childrenconst hasKids = hierarchySystem.hasChildren(parent);
// Get depth in hierarchyconst depth = hierarchySystem.getDepth(child1); // Returns 1API Reference
Section titled “API Reference”Parent-Child Operations
Section titled “Parent-Child Operations”// Set parenthierarchySystem.setParent(child, parent);
// Move to root (no parent)hierarchySystem.setParent(child, null);
// Insert child at positionhierarchySystem.insertChildAt(parent, child, 0);
// Remove child (becomes root)hierarchySystem.removeChild(parent, child);
// Remove all childrenhierarchySystem.removeAllChildren(parent);Hierarchy Queries
Section titled “Hierarchy Queries”// Get root of entityconst root = hierarchySystem.getRoot(deepChild);
// Get all root entitiesconst roots = hierarchySystem.getRootEntities();
// Check ancestor/descendant relationshipsconst isAncestor = hierarchySystem.isAncestorOf(grandparent, child);const isDescendant = hierarchySystem.isDescendantOf(child, grandparent);Hierarchy Traversal
Section titled “Hierarchy Traversal”// Find child by nameconst child = hierarchySystem.findChild(parent, "ChildName");
// Recursive searchconst deepChild = hierarchySystem.findChild(parent, "DeepChild", true);
// Find children by tagconst tagged = hierarchySystem.findChildrenByTag(parent, TAG_ENEMY, true);
// Iterate childrenhierarchySystem.forEachChild(parent, (child) => { console.log(child.name);}, true); // true for recursiveHierarchy State
Section titled “Hierarchy State”// Check if active in hierarchy (considers all ancestors)const activeInHierarchy = hierarchySystem.isActiveInHierarchy(child);
// Get depth (root = 0)const depth = hierarchySystem.getDepth(entity);Complete Example
Section titled “Complete Example”class GameScene extends Scene { private hierarchySystem!: HierarchySystem;
protected initialize(): void { this.hierarchySystem = new HierarchySystem(); this.addSystem(this.hierarchySystem); this.createPlayerHierarchy(); }
private createPlayerHierarchy(): void { const player = this.createEntity("Player"); player.addComponent(new Transform(0, 0));
const body = this.createEntity("Body"); body.addComponent(new Sprite("body.png")); this.hierarchySystem.setParent(body, player);
const weapon = this.createEntity("Weapon"); weapon.addComponent(new Sprite("sword.png")); this.hierarchySystem.setParent(weapon, body);
console.log(`Player depth: ${this.hierarchySystem.getDepth(player)}`); // 0 console.log(`Weapon depth: ${this.hierarchySystem.getDepth(weapon)}`); // 2 }}Best Practices
Section titled “Best Practices”- Avoid Deep Nesting: System limits max depth to 32 levels
- Batch Operations: Set up all parent-child relationships at once when building complex hierarchies
- On-Demand Addition: Only add
HierarchyComponentto entities that truly need hierarchy - Cache System Reference: Avoid getting
HierarchySystemon every call
// Good practiceclass MySystem extends EntitySystem { private hierarchySystem!: HierarchySystem;
onAddedToScene() { this.hierarchySystem = this.scene!.getEntityProcessor(HierarchySystem)!; }}