我自己还写了个Entitas框架代码生成工具,可以自动初始化系统部分,传送门:Entitas代码生成工具
Unity本身就是EC(Entity Component )模式的引擎,只不过在Unity中Entity 叫 GameObject,在GameObject上的脚本也能通过开放字段,修改数据,就和ECS的思路是差不多的,但是这样的缺点也很明显,它的逻辑和数据是耦合的,可能会出现后期的行为函数膨胀,多个组件之间的耦合过高的情况,所以进一步逻辑分离就是ECS的写法了。
Entity:代表游戏中的实体,是 Component 的容器。本身并 无数据和逻辑。
Component:代表实体“有什么”,一个或多个 Component 组成了游戏中的逻辑实体。 只有数据 ,不涉及逻辑。
System:对 Component 集中进行 逻辑操作 的部分。一个 System 可以操作一类或多类 Component。同一个 Component 在不同的 System 中,可以关联不同的逻辑。
也就是组件(Component)只携带数据,它没有函数,也就不涉及逻辑,而系统(System)则负责逻辑部分,所以它没有字段,不需要携带状态,实体(Entity)就是组件的载体,它并不携带数据和逻辑
举个例子:
我们实现一个Human类,他有Run,Jump,Eat三种状态,保存状态的字段叫做State
面向对象编程
我们通常的实现思路是,有一个名为Human的类,带有这三个行为的函数,然后有一个状态机,根据State的改变用switch调用对应的行为函数
面向数据编程
也就是在ECS中的思路是有一个携带了State的组件,而每一个行为对应了一个system,每个system只关心自己的行为,所以不需要携带状态
这样就完成了解耦,而且清晰了逻辑,面向对象的思维是我当前是什么,而面向数据的思维是我当前有什么
Entity Component System (ECS) 本身是逻辑层面的框架,它并不提供渲染引擎或是物理引擎的一类东西,所以不要误会说,它会提供物理接口或是资源加载接口啥的,它负责的是游戏的逻辑,处理游戏对象之间的状态更新问题。
1)高复用性:组件之间容易组合
2)扩展性强:面向对象的思想是对象本身是什么,而面向数据的思想是对象有什么,增加新的功能就是添加新的组件,无需修改之前的逻辑
3)性能方面提供了更大的优化空间
4)降低了代码之间的耦合度
1)面向数据的编程模式,势必造成大量的组件及系统组合的形式,对于代码重构会形成很大的困难
2)默写模块与系统的耦合过高,造成一些功能的重用要有整个ECS环境才能运行
3)全部是数据都是开放的,数据上设计不好会造成很大影响
携带数据,也就是状态
登录后复制
[Event(EventTarget.Self)]
public sealed class MoveComponent : IComponent {
[EntityIndex]
public IntVector2 target;
}
组件是由我们自己定义的,所以它有几个需要注意的地方
它是组件的容器,可以在实体上添加,删除,替换这些组件,但是实体本身并没有数据和行为,数据都是组件的
登录后复制
var entity = _contexts.game.CreateEntity();
//添加组件
entity.AddMove(new IntVector2(x, y));
//移除组件
entity.RemoveMove();
//替换组件
entity.ReplaceMove(new IntVector2(x, y));
编写游戏逻辑的地方,这里需要注意,之前也说过,系统内不能包含状态
Entitas中包含以下几个系统
这里的上下文规定了实体的逻辑边界,在上下文内,管理实体和组件的声明周期,类似于一个工厂模式一样
这部分的代码是自动生成的,不用自己编写
这部分是自动生成的代码,它代表的实际就是你感兴趣的一类实体的标签
如:你要获取含有Move组件的实体,那么它会自动生成这样一个属性
登录后复制
public static Entitas.IMatcher<GameEntity> Move {
get {
if (_matcherMoveComplete == null) {
var matcher = (Entitas.Matcher<GameEntity>)Entitas.Matcher<GameEntity>.AllOf(GameComponentsLookup.Move);
matcher.componentNames = GameComponentsLookup.componentNames;
_matcherMove = matcher;
}
return _matcherMove;
}
}
其中GameComponentsLookup.Move是组件的ID,是自动生成的,在框架内,类型为int的组件的标识
实际这个Matcher就代表了一类实体的条件,也就是同时含有规定的组件,才会匹配上
组是由上下文(Context)管理的,并且始终保持最新,它会自动添加与匹配器匹配的实体,或当实体与匹配器不匹配时删除实体
登录后复制
也就是你可以在框架内,使用context.GetGroup(matcher) 来获取与指定Matcher(匹配器)匹配的一组实体
顾名思义,就是用来收集实体的类,它会关注一个或多个组(Group),并根据指定的事件,收集更改的实体
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删