Unity项目软件平台架构解析:构建坚实基础

我自己还写了个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),并根据指定的事件,收集更改的实体



免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删

QR Code
微信扫一扫,欢迎咨询~

联系我们
武汉格发信息技术有限公司
湖北省武汉市经开区科技园西路6号103孵化器
电话:155-2731-8020 座机:027-59821821
邮件:tanzw@gofarlic.com
Copyright © 2023 Gofarsoft Co.,Ltd. 保留所有权利
遇到许可问题?该如何解决!?
评估许可证实际采购量? 
不清楚软件许可证使用数据? 
收到软件厂商律师函!?  
想要少购买点许可证,节省费用? 
收到软件厂商侵权通告!?  
有正版license,但许可证不够用,需要新购? 
联系方式 155-2731-8020
预留信息,一起解决您的问题
* 姓名:
* 手机:

* 公司名称:

姓名不为空

手机不正确

公司不为空