层级系统
在游戏开发中,实体间的父子层级关系是常见需求。ECS Framework 采用组件化方式管理层级关系,通过 HierarchyComponent 和 HierarchySystem 实现,完全遵循 ECS 组合原则。
为什么不在 Entity 中内置层级?
Section titled “为什么不在 Entity 中内置层级?”传统的游戏对象模型将层级关系内置于实体中。ECS Framework 选择组件化方案的原因:
- ECS 组合原则:层级是一种”功能”,应该通过组件添加,而非所有实体都具备
- 按需使用:只有需要层级关系的实体才添加
HierarchyComponent - 数据与逻辑分离:
HierarchyComponent存储数据,HierarchySystem处理逻辑 - 序列化友好:层级关系作为组件数据可以轻松序列化和反序列化
HierarchyComponent
Section titled “HierarchyComponent”存储层级关系数据的组件:
import { HierarchyComponent } from '@esengine/ecs-framework';
// HierarchyComponent 的核心属性interface HierarchyComponent { parentId: number | null; // 父实体 ID,null 表示根实体 childIds: number[]; // 子实体 ID 列表 depth: number; // 在层级中的深度(由系统维护) bActiveInHierarchy: boolean; // 在层级中是否激活(由系统维护)}HierarchySystem
Section titled “HierarchySystem”处理层级逻辑的系统,提供所有层级操作的 API:
import { HierarchySystem } from '@esengine/ecs-framework';
// 获取系统const hierarchySystem = scene.getEntityProcessor(HierarchySystem);添加系统到场景
Section titled “添加系统到场景”import { Scene, HierarchySystem } from '@esengine/ecs-framework';
class GameScene extends Scene { protected initialize(): void { // 添加层级系统 this.addSystem(new HierarchySystem());
// 添加其他系统... }}建立父子关系
Section titled “建立父子关系”// 创建实体const parent = scene.createEntity("Parent");const child1 = scene.createEntity("Child1");const child2 = scene.createEntity("Child2");
// 获取层级系统const hierarchySystem = scene.getEntityProcessor(HierarchySystem);
// 设置父子关系(自动添加 HierarchyComponent)hierarchySystem.setParent(child1, parent);hierarchySystem.setParent(child2, parent);
// 现在 parent 有两个子实体// 获取父实体const parentEntity = hierarchySystem.getParent(child1);
// 获取所有子实体const children = hierarchySystem.getChildren(parent);
// 获取子实体数量const count = hierarchySystem.getChildCount(parent);
// 检查是否有子实体const hasKids = hierarchySystem.hasChildren(parent);
// 获取在层级中的深度const depth = hierarchySystem.getDepth(child1); // 返回 1API 参考
Section titled “API 参考”父子关系操作
Section titled “父子关系操作”setParent
Section titled “setParent”设置实体的父级:
// 设置父级hierarchySystem.setParent(child, parent);
// 移动到根级(无父级)hierarchySystem.setParent(child, null);insertChildAt
Section titled “insertChildAt”在指定位置插入子实体:
// 在第一个位置插入hierarchySystem.insertChildAt(parent, child, 0);
// 追加到末尾hierarchySystem.insertChildAt(parent, child, -1);removeChild
Section titled “removeChild”从父级移除子实体(子实体变为根级):
const success = hierarchySystem.removeChild(parent, child);removeAllChildren
Section titled “removeAllChildren”移除所有子实体:
hierarchySystem.removeAllChildren(parent);getParent / getChildren
Section titled “getParent / getChildren”const parent = hierarchySystem.getParent(entity);const children = hierarchySystem.getChildren(entity);getRoot
Section titled “getRoot”获取实体的根节点:
const root = hierarchySystem.getRoot(deepChild);getRootEntities
Section titled “getRootEntities”获取所有根实体(没有父级的实体):
const roots = hierarchySystem.getRootEntities();isAncestorOf / isDescendantOf
Section titled “isAncestorOf / isDescendantOf”检查祖先/后代关系:
// grandparent -> parent -> childconst isAncestor = hierarchySystem.isAncestorOf(grandparent, child); // trueconst isDescendant = hierarchySystem.isDescendantOf(child, grandparent); // truefindChild
Section titled “findChild”根据名称查找子实体:
// 直接子级中查找const child = hierarchySystem.findChild(parent, "ChildName");
// 递归查找所有后代const deepChild = hierarchySystem.findChild(parent, "DeepChild", true);findChildrenByTag
Section titled “findChildrenByTag”根据标签查找子实体:
// 查找直接子级const tagged = hierarchySystem.findChildrenByTag(parent, TAG_ENEMY);
// 递归查找const allTagged = hierarchySystem.findChildrenByTag(parent, TAG_ENEMY, true);forEachChild
Section titled “forEachChild”遍历子实体:
// 遍历直接子级hierarchySystem.forEachChild(parent, (child) => { console.log(child.name);});
// 递归遍历所有后代hierarchySystem.forEachChild(parent, (child) => { console.log(child.name);}, true);isActiveInHierarchy
Section titled “isActiveInHierarchy”检查实体在层级中是否激活(考虑所有祖先的激活状态):
// 如果 parent.active = false,即使 child.active = true// isActiveInHierarchy(child) 也会返回 falseconst activeInHierarchy = hierarchySystem.isActiveInHierarchy(child);getDepth
Section titled “getDepth”获取实体在层级中的深度(根实体深度为 0):
const depth = hierarchySystem.getDepth(entity);扁平化层级(用于 UI 渲染)
Section titled “扁平化层级(用于 UI 渲染)”// 用于实现可展开/折叠的层级树视图const expandedIds = new Set([parent.id]);
const flatNodes = hierarchySystem.flattenHierarchy(expandedIds);// 返回 [{ entity, depth, bHasChildren, bIsExpanded }, ...]创建游戏角色层级
Section titled “创建游戏角色层级”import { Scene, HierarchySystem, HierarchyComponent} from '@esengine/ecs-framework';
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);
// 特效(挂载在武器上) const effect = this.createEntity("WeaponEffect"); effect.addComponent(new ParticleEmitter()); this.hierarchySystem.setParent(effect, weapon);
// 查询层级信息 console.log(`Player 层级深度: ${this.hierarchySystem.getDepth(player)}`); // 0 console.log(`Weapon 层级深度: ${this.hierarchySystem.getDepth(weapon)}`); // 2 console.log(`Effect 层级深度: ${this.hierarchySystem.getDepth(effect)}`); // 3 }
public equipNewWeapon(weaponName: string): void { const body = this.findEntity("Body"); const oldWeapon = this.hierarchySystem.findChild(body!, "Weapon");
if (oldWeapon) { // 移除旧武器的所有子实体 this.hierarchySystem.removeAllChildren(oldWeapon); oldWeapon.destroy(); }
// 创建新武器 const newWeapon = this.createEntity("Weapon"); newWeapon.addComponent(new Sprite(`${weaponName}.png`)); this.hierarchySystem.setParent(newWeapon, body!); }}层级变换系统
Section titled “层级变换系统”结合 Transform 组件实现层级变换:
import { EntitySystem, Matcher, HierarchySystem, HierarchyComponent } from '@esengine/ecs-framework';
class HierarchyTransformSystem extends EntitySystem { private hierarchySystem!: HierarchySystem;
constructor() { super(Matcher.empty().all(Transform, HierarchyComponent)); }
public onAddedToScene(): void { // 获取层级系统引用 this.hierarchySystem = this.scene!.getEntityProcessor(HierarchySystem)!; }
protected process(entities: readonly Entity[]): void { // 按深度排序,确保父级先更新 const sorted = [...entities].sort((a, b) => { return this.hierarchySystem.getDepth(a) - this.hierarchySystem.getDepth(b); });
for (const entity of sorted) { const transform = entity.getComponent(Transform)!; const parent = this.hierarchySystem.getParent(entity);
if (parent) { const parentTransform = parent.getComponent(Transform); if (parentTransform) { // 计算世界坐标 transform.worldX = parentTransform.worldX + transform.localX; transform.worldY = parentTransform.worldY + transform.localY; } } else { // 根实体,本地坐标即世界坐标 transform.worldX = transform.localX; transform.worldY = transform.localY; } } }}HierarchySystem 内置了缓存机制:
depth和bActiveInHierarchy由系统自动维护- 使用
bCacheDirty标记优化更新 - 层级变化时自动标记所有子级缓存为脏
- 避免深层嵌套:系统限制最大深度为 32 层
- 批量操作:构建复杂层级时,尽量一次性设置好所有父子关系
- 按需添加:只有真正需要层级关系的实体才添加
HierarchyComponent - 缓存系统引用:避免每次调用都获取
HierarchySystem
// 好的做法class MySystem extends EntitySystem { private hierarchySystem!: HierarchySystem;
onAddedToScene() { this.hierarchySystem = this.scene!.getEntityProcessor(HierarchySystem)!; }
process() { // 使用缓存的引用 const parent = this.hierarchySystem.getParent(entity); }}
// 避免的做法process() { // 每次都获取,性能较差 const system = this.scene!.getEntityProcessor(HierarchySystem);}如果你之前使用的是旧版 Entity 内置的层级 API,请参考以下迁移指南:
| 旧 API (已移除) | 新 API |
|---|---|
entity.parent | hierarchySystem.getParent(entity) |
entity.children | hierarchySystem.getChildren(entity) |
entity.addChild(child) | hierarchySystem.setParent(child, entity) |
entity.removeChild(child) | hierarchySystem.removeChild(entity, child) |
entity.findChild(name) | hierarchySystem.findChild(entity, name) |
entity.activeInHierarchy | hierarchySystem.isActiveInHierarchy(entity) |
// 旧代码const parent = scene.createEntity("Parent");const child = scene.createEntity("Child");parent.addChild(child);const found = parent.findChild("Child");
// 新代码const hierarchySystem = scene.getEntityProcessor(HierarchySystem);
const parent = scene.createEntity("Parent");const child = scene.createEntity("Child");hierarchySystem.setParent(child, parent);const found = hierarchySystem.findChild(parent, "Child");