在 ECS
(Entity-Component-System)架构 中,Entity(实体)
本身不需要显式分类型。
这是 ECS 的核心设计思想之一:实体是纯粹的、无类型的标识符(ID),其行为和属性完全由 组合的组件(Component) 决定,而非通过类型继承或分类实现。
为什么不需要分类型?
动态组合性
实体通过添加/移除组件来改变行为,而非通过类型。例如:
- 一个“怪物”实体可以通过添加 PlayerControlled 组件变成“玩家角色”。
- 一个“道具”实体可以通过添加 CombatStats 组件变成“武器”。
避免类型爆炸
传统 OOP 中,类型继承会导致类层级膨胀(如 Monster → Boss → FireBoss)。ECS 中只需组合组件
:
// 用组件组合替代类型继承
const boss = {
id: 1,
components: [CombatStats, BossAI, FireEffects, LootTable]
};
系统查询的灵活性
系统通过组件组合
查询实体,而非类型。例如:
- MovementSystem 查询所有包含 Position 和 Velocity 组件的实体。
- TurnSystem 查询所有包含 TurnOrder 组件的实体。
如何替代“类型”概念?
传统类型思维 | ECS 替代方案 |
---|---|
类型继承(class Monster) | 组件组合([Health, AI, Sprite]) |
类型标签(enum EntityType) | 用 标记组件(如 IsPlayer) |
类型检查(if (entity is Monster)) | 查询组件(hasComponent(entity, “Health”)) |
实战案例:回合制战斗中的实体设计
场景需求
- 玩家、敌人、技能、障碍物等需要不同的行为。
- 某些敌人是“Boss”,需要特殊逻辑。
ECS 实现
实体定义(无类型)
// 玩家实体
const player = {
id: 1,
components: [Position, Health, PlayerControlled, Skills]
};
// 普通敌人
const enemy = {
id: 2,
components: [Position, Health, AI, Sprite]
};
// Boss 敌人(仅多一个标记组件)
const boss = {
id: 3,
components: [Position, Health, AI, Sprite, IsBoss, LootTable]
};
系统逻辑
- TurnSystem:查询所有含 TurnOrder 的实体。
- AISystem:查询含 AI 但不含 PlayerControlled 的实体。
- BossRewardSystem:查询含 IsBoss 和 Health(且血量≤0)的实体。
动态改变类型
// 敌人被玩家招募?移除 AI 组件,添加 PlayerControlled
removeComponent(enemy, "AI");
addComponent(enemy, PlayerControlled);
ECS 的优势正是通过 消除类型耦合 来实现高度灵活的设计,这在回合制战斗中尤其重要(如动态技能效果、状态叠加等)。