GitHub地址:https://github.com/sschmid/Entitas-CSharp
Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
简单来说就是个基于C#和unity的ECS框架
https://github.com/sschmid/Entitas-CSharp/releases 最新应该是1.8.0
官方实例 https://github.com/sschmid/Entitas-CSharp/wiki/Unity-Tutorial-Hello-World
官方描述的很详细这里就不做赘述了
需要看懂下面的代码最好看下官方的案例,直接观看可能无法理解
在这里解释几个概念
这里这个Attribute是在
是在Preferences下的Contexts里面定义的
Context是一种为Entity服务的的管理性数据结构。一个Entity不能自己独立创建,它必须通过context.CreateEntity()
创建。通过这种方式,Context可以管理我们创建的所有Entity的生命周期。
在程序中是通过
var contexts = Contexts.sharedInstance;
获得contexts(需要重新生成如果新添加了contexts)
再获得定义的game,input等等的entity
再通过CreateEntity创建, 然后便可以进行添加组件的操作
这个Attribute也可以不写,同时还有除了Contexts的其他定义 比如Unique Component
[Unique]
public class DebugMessageComponent : IComponent
{
public string Message;
}
在每个应用程序中,有许多情况下你只想要一个物体的一个实例。这个想法一般呈现在大家多知道并且常常讨厌的单例模式
中。在Entitas中,有类似的但更好的东西,我们之前讨论的每种类型的组件都可以定义为一个唯一组件。
就可以给entity添加组件
ps: (addxxx和编写的xxxcomponent是一一对应,同时还会有hasxxx,Replacexxx等等)
Context管理着所有实体 因此我们可以向Context请求并遍历所有Entity 从而收集具有我们关心的Component的那些Entites 这将是一个非常(naive)的实现。针对这种情况下,我们在Entitas中应对措施就是Group。
通过匹配器(matcher)获得我们的group 通过里面的
AllOf(包含输入的所有组件),AnyOf(包含输入的其中的一种组件)等等来获得我们想要的Group(实际上就是获得所有满足条件的entity)
Matcher是我们如何描述我们感兴趣的Entity的一种方式。你可以说它就是我们的小型查询语言(query language)。 GameMatcher
意味着我们有一个Game
Context,我们可以访问与此Context相关的所有Component类型。如果我们写context.GetGroup(GameMatcher.
DebugMessage);
我们会得到一组包含DebugMessage组件的Entites。为了定义更复杂的Group,我们可以使用AllOf
,AnyOf
和NoneOf
方法。 “AllOf”表示所有列出的Component都必须出现在Entity中才能使此Entity成为Group的一部分;“AnyOf”表示必须存在列出的组件中的一个;而在NoneOf
的情况下,我们则不希望任何列出的组件存在。 NoneOf
不是一个独立的描述,这意味着你将无法编写;
因为它会创建一个非常大的集合,所以是被禁止单独使用的。 NoneOf
只能与AllOf
或AnyOf
结合使用。
ECS或是面向数据设计最主要的目标就是从行为中分理出状态(State from behaviour)。System是我们定义行为的地方(简单来说就是逻辑主要都放在这里)我们可以在system种利用代码来创建新的状态,改变或是删除已有的状态。
Entitas里面System的最基础的接口是ISystem
Entitas内部写了不少种类的system,就不一一说明 说几个常用的
IExecuteSystem是一种可持续执行的system
这个接口只有一个方法void Execute();
。这是我们写需要每tick(或者unity内的update内)都需要执行的代码的地方。
实例
结果
同样也是可持续执行这个system是为了我们在执行完所有的IExecuteSystem
之后执行的逻辑所设立的。像他名字建议的一样,你应该放清理(clean up)的代码到它的void Cleanup();
方法中去。他们都只是接口,你可以有用一个类同时实现这些协议,有时候这样做从游戏逻辑上来说非常有意义。
Reactive System是一个当有需要我们处理Entity才会被调用的system。
通过里面的GetTrgger方法 只关心我们只关心的含有我们想要的组件(Component)的实体(Entity)
通过里面Filter方法 过滤掉不含有我们关心的组件的实体
Hello World实例内的 DebugMessageSystem 过滤器和收集器关心的都是同一个组件 显得有些重复 实际应用当中 随着组件的增多 二者的作用也会更具体现
实例
响应式的系统就像执行式系统一样,会每隔一段时间或是在每一个Update
中被触发,然而Execute(List
方法只会在收集器距离上一次Execute
收集到新的Entity才会被执行。(这句是重点,这个系统会将上次收集到的复合条件的执行Execute方法然后清空buffer和collect
)
Entitas提供了一个Sysetms
类,实现了 IInitializeSystem, IExecuteSystem, ICleanupSystem, ITearDownSystem
接口。我们可以Add
一个system到Systems
的实例中,然后当我们调用Systems上的Execute(), Cleanup(), Initialize(), TearDown()
方法,它会调用所有添加了的system的对应方法。
在这里,Entitas扩展了一个Feature
类。根据我们选择是否开启 visual debugging ,会生成一个Feature 类集成Systems
或者DebugSystems
类。VIsual debugging消耗非常多的资源,所以当你在移动设备上运行或者制作产品Build的时候,你应该将VIsual debugging关闭掉。所以Feature就是为了让我们的代码更加简单而创造出来的类。
在这里需要注意的是 类似于ReactiveSystem筛选Entity是在过滤器和收集器当中的进行的 所以再初始化System后添加Component
而有些System的筛选是在构造当中 需要在初始化之前添加Component
关于上述的组件使用可以看Entitas的git上面的wiki上面又详细的描述 只不过是纯英文的
在https://github.com/mzaks/EntitasCookBook/tree/master/chapters/1_ingredients 作者的cookbook里面有具体的描述
(觉得英文看起来比较吃力的话推荐https://www.jianshu.com/u/f26ca74e2fd7 )
Entitas 本身是一个非常好用的框架,帮助程序员编写完善,健壮以及高复用性的代码。框架本身对UNITY进行过高度优化,大量的缓存对象管理,使得Entity/Component的访问以及Grouping非常的简单快速。此外附带代码生成器确保了TypeSafety并且节省了一些重复构建代码的时间,Entitas 同时也有自己无法避免的缺点,代码的重构比较困难(不是ecs的缺点)如果你对代码进行添加还好,如果进行修改,一般来说会产生很多的错误,而这些错误使得Entitas无法重新生成新的代码,同时这些错误还会嵌套其中,代码少还可以,多的话,可以想像修改代码的量还是很可观的以及代码量会大量增加。
相对来说使用起来可能还不是呢么方便 下一章研究下unity2018带来的ecs框架
参考文献
https://www.jianshu.com/u/f26ca74e2fd7
http://www.benmutou.com/archives/2431
http://piemaster.net/2011/07/entity-component-primer/
https://github.com/sschmid/Entitas-CSharp/wiki/Unity-Installation-Guide