实体组件系统中的继承

Inheritance in an Entity Component System

我在网上阅读的大多数关于实体组件系统的资料似乎都表明它是用来避免使用继承的。但我想知道的是,继续使用继承和 ECS 是否可行?

假设我在引擎中有特定数量的实体,它们将具有大部分相同的功能。例如,能够移动的实体。然后创建一个添加必要组件的 MovableEntity 对象是否可行?

public class MovableEntity extends Entity {
    public MovableEntity(int x, int y) {
        addComponent(new PositionComponent(x, y));
        addComponent(new VelocityComponent());
        ...
    }
}

如果您不需要组件 added/removed 动态地 to/from 您的实体,则可以使用继承来定义实体(在具有多重继承的语言中)。考虑来自 Evolve Your Hierarchy 的以下示例 ECS:

您的组件将对应于 classes:

  • class 位置
  • class 运动
  • ...

并且您的实体将对应于 classes:

  • class 外星人:位置,运动,渲染,...
  • class 球员:位置、运动、物理、...
  • ...

虽然不标准,但我发现这种做法非常有用。想象一个游戏,战士有生命值,攻击力和防御力。您可以编写一个带有枚举和值的统计组件。另一种方法是拥有一个包含其他实体的组件,并使每个统计数据成为具有值和类型组件的实体本身。我尝试了这两种方法,但我不喜欢早期的结果。 (后者工作正常但确实很复杂,但它确实严格遵守ECS设计)

最后我把每一个都做了一个继承自Component的StatComponent。这意味着我可以执行所有统计数据通用的操作,这非常方便。诀窍是让 OOP 的每一部分都陷入泡沫中。只要它是独立的,我就看不到任何重大问题。

我希望你不介意我直率的意见,但以你的方式,我认为这是个坏主意。

首先,您是否只是为了更容易地构建它们而定义新的组件类型?如果是这样,你可以像这样使用:

Entity movable_entity(int x, int y)
{
    Entity new_entity = ...;
    new_entity.addComponent(new PositionComponent(x, y));
    new_entity.addComponent(new VelocityComponent());
    ...
    return new_entity;
}

这可能看起来只是风格上的差异,但在不涉及继承时表现出更松散的耦合。

其次,如果您实际上要向 MovableEntity 添加 Entity 没有的新状态,那么您的实体不再是统一大小的,我们不能再通过直接的随机访问有效地在内存中连续地表示所有实体,并且它们也可能变得容易进行对象切片(例如:当想要克隆 MoveableEntity 时,从可移动的实体中克隆 Entity) .

所以至少对于实体而言,我真的认为最好不要扩展它们。我可以看到更多组件类型继承的用例,但我仍然建议不要使用它,因为 ECS 在访问组件的方式方面非常灵活,不需要通过组件基类型进行多态性。如果你想在另一个组件中重用一个组件的数据字段,你可以只使用组合。

继承向 ECS 添加了一种竞争性模型,它并没有那么漂亮,除非你这样做只是为了更容易地使用一组特定的组件构建特定类型的实体,再次, 可以用一个简单的函数来完成,而无需引入一个全新的实体类型。