组件 Component
Component是ECS中最简单的原料。它是数据的最原子性的呈现。它可以是空的、有一个或多个属性,甚至可以被标记为唯一的(类似单例)。接下来我将解释Entitas的组件实现方式。
最简单的组件 - 标志组件 (Flag Component)
我会用 Match-One 项目中定义的组件作为例子。
using Entitas;
public sealed class MovableComponent : IComponent {
}
正如你所看到的,一个组件是一个实现了IComponent
接口的类。它没有任何属性,因此它是标志组件。Flag component是用于定义Flag Entity。在这种情况下,我们说有些东西是可移动的(is Movable对应MovableComponent)。所以如果我们有一个Entity,我们可以通过查询entity.isMovable
并返回true或false。我们也可以要求所有拥有MovableComponent
的Entity,但是我想稍后再讨论这种用法。
数据组件 (Data Component)
Data Component可以有多个可以存储纯数据的属性:
using Entitas;
public sealed class PositionComponent : IComponent {
public int x;
public int y;
}
在Entitas-CSharp中,我们可以使用以下语句为Entity添加一个Position:
entity.AddPosition(1, 2);
有一种方法来检查一个实体是否有对应的组件(hasPosition
)。我们也可以获取(position
),替换(ReplacePosition
)和删除(RemovePosition
)组件。每个实体只能有一种类型的组件集。这就是为什么我们有Replace
方法。但我们可以将所有不同类型的组件结合在一个实体中。这就是为什么最好将组件尽可能粒子化(thin)。这在肆意组合(improvisation)方面给了你很大的好处。
引用组件 (Reference Component)
一个Reference Component在技术上等同于Data Component,主要是逻辑上的差别。
using Entitas;
using UnityEngine;
public sealed class ViewComponent : IComponent {
public GameObject gameObject;
}
从技术上讲,Reference Component也是具有多个属性的Component,但是这些属性不代表数据。它们引用一个复杂的对象,这有一个更深一层的含义。这些组件很难被序列化,它们指向在运行时创建的一些对象,因此持久化指针是没有用的。我们将在Receipt章节深入研究Reference Component。
操作组件 (Action Component)
这又是一个Data Component的衍生物。只是在这种情况下,属性是属于一个功能/操作。
using Entitas;
using System;
public sealed class DelegateComponent : IComponent {
public Action action;
}
在这种情况下,我们可以将一个函数/委托/操作存储在一个组件中,并将其附加到一个Entity中。虽然组件能够这样使用,但它的坏处比好处更多,而不是我们将会在Reciept部分讨论的那样。
唯一组件 (Unique Component)
在每个应用程序中,有许多情况下你只想要一个物体的一个实例。这个想法一般呈现在大家多知道并且常常讨厌的单例模式
中。在Entitas中,我们有类似的但更好的东西。
我们之前讨论的每种类型的组件都可以定义为一个唯一组件。
using Entitas;
using Entitas.CodeGeneration.Attributes;
[Unique]
public sealed class GameBoardComponent : IComponent {
public int columns;
public int rows;
}
为此,我们只需要将该类(通过Attribute)注释为唯一(Unique)的。
该框架(Entitas)将确保在上下文(Context)中只有一个唯一组件实例可以存在(请参阅Context章节)。这就是为什么在Entitas-CSharp中,我们可以用下面的表达式 - context.gameBoard
来获得一个独特组件的实例。
怎么样,是不是比单例模式
更好呢?由于我们将状态与行为分开,这会比单列模式更好。这个组件也可以被替换和移除。所以它打破了单例模式
的习惯用法 - 一个对象在整个应用程序生命周期中是唯一且持久的。一个独特的组件比单例更像是一个全局变量。
通常来说一个程序需要多少个组件呢?
这个问题始终是专门针对ECS的新手。和往常一样,正确答案是 - 看!情!况!不过从我的经验来看,中端核心手机游戏上,150个组件相当不错。事实上,我对比了我参与制作的两个完全不同的手机游戏,大约都在150个组件左右。也就是说,我用EntitasKit构建的iOS应用程序(Swift实现)包含大约50个组件,也并不令人意外,因为游戏往往比应用程序复杂得多。