Unity框架探索——ECS架构Entitas篇之基础概念

Entitas是德国Wooga公司开源的一款ECS架构,不只是C#,还有其他多种语言版本,基本涵盖了目前主流的计算机语言
Entitas开源项目地址:https://github.com/sschmid/Entitas-CSharp

我自己还写了个Entitas框架代码生成工具,可以自动初始化系统部分,传送门:Entitas代码生成工具

Unity本身就是EC(Entity Component )模式的引擎,只不过在Unity中Entity 叫 GameObject,在GameObject上的脚本也能通过开放字段,修改数据,就和ECS的思路是差不多的,但是这样的缺点也很明显,它的逻辑和数据是耦合的,可能会出现后期的行为函数膨胀,多个组件之间的耦合过高的情况,所以进一步逻辑分离就是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) 本身是逻辑层面的框架,它并不提供渲染引擎或是物理引擎的一类东西,所以不要误会说,它会提供物理接口或是资源加载接口啥的,它负责的是游戏的逻辑,处理游戏对象之间的状态更新问题。

二、ECS优缺点

(1)ECS 框架优点:

1)高复用性:组件之间容易组合
2)扩展性强:面向对象的思想是对象本身是什么,而面向数据的思想是对象有什么,增加新的功能就是添加新的组件,无需修改之前的逻辑
3)性能方面提供了更大的优化空间
4)降低了代码之间的耦合度

(2)ECS 框架缺点:

1)面向数据的编程模式,势必造成大量的组件及系统组合的形式,对于代码重构会形成很大的困难
2)默写模块与系统的耦合过高,造成一些功能的重用要有整个ECS环境才能运行
3)全部是数据都是开放的,数据上设计不好会造成很大影响

三、Entitas基本构成

(1)Component(组件)

携带数据,也就是状态

[Event(EventTarget.Self)]
public sealed class MoveComponent : IComponent {

    [EntityIndex]
    public IntVector2 target;
}

组件是由我们自己定义的,所以它有几个需要注意的地方

  1. 组件需要实现自IComponent接口
  2. 组件的名字就作为关键字生成一系列类及方法,如 MoveComponent 它会自动剔除Component把Move作为关键字,生成如:添加组件的方法AddMove,移除的方法RemoveMove等等
  3. 它需要添加特性,作为自动生成代码的标记,如上的[Event(EventTarget.Self)]和[EntityIndex]还有很多其他的,下面介绍

(2)Entity(实体)

它是组件的容器,可以在实体上添加,删除,替换这些组件,但是实体本身并没有数据和行为,数据都是组件的

  		var entity = _contexts.game.CreateEntity();
  		//添加组件
        entity.AddMove(new IntVector2(x, y));
        //移除组件
        entity.RemoveMove();
        //替换组件
        entity.ReplaceMove(new IntVector2(x, y));

(3)System(系统)

编写游戏逻辑的地方,这里需要注意,之前也说过,系统内不能包含状态
Entitas中包含以下几个系统

  1. ICleanupSystem:实现接口用来创建一个执行后清理逻辑的系统
  2. IExecuteSystem:实现接口用来创建一个每帧执行的系统
  3. IInitializeSystem:实现接口用来创建一个开始时执行一次的初始化系统
  4. ITearDownSystem:实现接口用来实现一个结束时执行一次的拆除系统
  5. ReactiveSystem:继承这个类实现一个响应系统(后面的帖子会详细介绍)
  6. MultiReactiveSystem:继承这个类实现一个多上下文的响应系统(后面的帖子会详细介绍)
  7. JobSystem:Entitas的多线程系统

(4)Context(上下文)

这里的上下文规定了实体的逻辑边界,在上下文内,管理实体和组件的声明周期,类似于一个工厂模式一样
这部分的代码是自动生成的,不用自己编写

(5)Matcher(匹配器)

这部分是自动生成的代码,它代表的实际就是你感兴趣的一类实体的标签
如:你要获取含有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就代表了一类实体的条件,也就是同时含有规定的组件,才会匹配上

(6)Group(组)

组是由上下文(Context)管理的,并且始终保持最新,它会自动添加与匹配器匹配的实体,或当实体与匹配器不匹配时删除实体

也就是你可以在框架内,使用context.GetGroup(matcher) 来获取与指定Matcher(匹配器)匹配的一组实体

(7)Collector(搜集器)

顾名思义,就是用来收集实体的类,它会关注一个或多个组(Group),并根据指定的事件,收集更改的实体

结尾

基础部分就这些,之后我会写些应用方面以及源码讲解的文章,欢迎大家关注,不对的地方,也欢迎指正

我会在我的公众号上推送新的博文,也可以帮大家解答问题
微信公众号 Andy and Unity 搜索名称或扫描二维码
在这里插入图片描述
希望我们能共同成长,共同进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值