这里主要讲Entitas的执行原理,不讲Entitas的代码生成方面。缓存
ECS(实体-组件-系统)是一种经常使用于游戏开发的架构模式。
实体: 实体只是一个ID或一个容器,用来标记或存储一系列组件。
组件: 没有任何逻辑,单纯用来存储数据。
系统: 循环处理特定的组件。
ECS主要强调了两个方面:
1.用数据的组合去描述对象,而不是继承。
2.数据和逻辑的分离。架构
Unity采用了EC的设计思路,和传统ECS不一样,Unity的Component除了存储数据,还保留了操做Component中数据的方法。
Unity中的Entiy就是GameObject,Component就是GameObject上挂载的组件各类组件,如Transform。
GameObject是各类Component的容器,自己并无实际意义(与ECS中Entity的定义略有不一样,GameObject包含了tag、name、activeSelf等属性。若是是在纯粹的ECS系统中,tag等属性应该做为Component挂载在GameObject)。
好比场景中的一个Cube,由Transform、MeshFilter、MeshRenderer、BoxCollider四个组件组成。咱们能在场景中看到这个Cube是由于Unity从MeshFilter获得了Mesh信息,告诉了GPU这是一个立方体,从MeshRenderer中的到了渲染这个Mesh的信息,告诉GPU这个Mesh上的UV对应的是哪张贴图的坐标,渲染成什么颜色等信息。从Transform中得知了该将这个Cube渲染在哪一个位置,旋转多少度等。Unity经过BoxCollider和Transform信息去作碰撞检测。(在Cube的渲染这个例子中,能够把Unity自身看做ECS中的System的集合,由于Unity中的各个模块获取了这个Cube中特定Component中的信息,根据这些信息作一些事情)
Unity中的EC与传统ECS最大的两个区别就是:
1.Entity上带着一些属性数据name、tag等,Component不只有数据,还集成了大量的方法。好比在Unity中但愿旋转一个Transform会直接调用Transform的Rotate方法,而在传统ECS中,极可能是在Cube这个Entity上挂载一个Rotate组件,而后由专门的RotateSystem去处理这个转动。
2.没有System对Component进行统一的处理。框架
Entitas是一个用C#实现的ECS框架,提供了方便的代码生成功能。
用法的介绍官方项目写的比较详细,这里就很少作介绍。ide
也就是说整个ECS系统的内部数据维护(Group、Collector、EntityIndex)复杂度主要放在Entity的修改上了。
在给一个Entity添加一个Component时,不只仅是对Entity进行了修改,还会经过事件将这个添加传递给Context,Context遍历全部Group,找到知足此次修改条件的Group,对全部受到影响的Group进行修改。而后再经过Group将此次修改事件分发到Collector或其余监听该Group的模块中去。
这种方式带来的好处十分明显,那就是获取一种类型的Entity(也就是一个Group),只有第一次会遍历全部的Entity生成这个Group,以后再获取该类型Entity的复杂度就只有O(1)。
可是也有必定的隐患,当Group和Collector比较少时,这不是一个高消耗操做,可是Group、Collector不少,且在每一帧对Entity进行频繁修改的时候。这可能会成为一个高消耗操做。函数
1.在销毁一个Entity时,会移除Entity身上全部的Component,而后再进行回收。在移除Component时可能会经过Group把这个移除事件发送到监听Remove行为的Collector中,Collector会持有这个被销毁的Entity。因此在filter、或execute时不能直接依赖Collector的收集条件,还须要对Entity的Component作独立的判断。
其实任什么时候候filter都须要对Entity的Component作判断,由于Collector收集的Entity极可能在其余地方被改变。ui
2.Entity不该该被ECS系统外的模块持有,由于系统外对Entity的持有不会被自动引用计数(能够本身添加)。可能会致使一个Entity被销毁而后又从池子中从新取出来, 外部模块对这个Entity的引用没有改变,但已经可能不是本身持有的那个Entity了。
须要避免在外界持有Entity或经过持有uuid间接从context中持有这个Entity。设计
3.在replaceComponent时,发送了Remove、Add、Update三个事件,而不是只发送了Update事件。code
4.在代码生成时,对单Componet的Matcher进行了缓存,如游戏中经常使用的Postion和Name等Component,可是对组合Component的Matcher没有进行缓存。所在在两个不一样的ReactiveSystem中使用Matcher相同的Collector时,如:orm
//1,2表明Postion和Name的Index //在使用代码生成时会生成相似Matcher.Position、Matcher.Name的静态函数,方便开发者使用 context.CreateCollector(Matcher.AllOf(1,2));
这样会生成两个Matcher相同的Group实例。
若是在乎这一点的话能够本身对Matcher进行缓存。对象
在对ECS架构模式的理解和Entitas的使用上我仍是一个新手,只是刚刚开始使用,若是有什么写的不对的地方,各位大佬能够留言指正。