菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
485
0

Unity DOTS 中的 ECS

原创
05/13 14:22
阅读数 10770

因为本身就是忠实的 Overwatch 玩家,所以天然的对其应用的 ECS 架构有所兴趣。再加上最近在 Unity Connect 上看见一篇使用 Unity DOTS 实现的一个爆炸 Demo,所以就决定了这个分享的内容。

一、What 什么是 DOTS

DOTS(Data-Oriented Technology Stack 面向数据技术栈)是 Unity 提出的一个高性能多线程式数据导向的技术堆栈,能够充分利用多线程优势。

目前包含以下几个包。(除了 Job System 和 Burst,后面几个都是预览版本,API 一直在变化)

  • c# Job System:一个能够安全快速利用多核处理器的 System
  • Burst:一个新的基于 LLVM 的后端编译器,能够生成高度优化后的机器码
  • Entites:ECS架构的核心库,使用面向数据的方式能够更容易的提供 Job System 和 Burst 所需要的良好数据结构
  • Unity Physics:基于 DOTS 构建的新的物理系统当前依旧是很早期状态
  • Unity NetCode:基于 ECS 构建的带有客户端预测的服务器模型,可以用于创建多人游戏
  • DSPGraph:新的混音系统,c#编写,使用了 Job System,能够使用 Burst 编译,
  • Unity Animation:DOTS的动画系统,当前并不能用于商业生成

二、Why 为什么提出新的 ECS 架构

使用 OOP/传统模式开发会存在的问题:

  • 开发后期会出现大量的类,理解子类需要掌握父类
  • 数据和其处理过程耦合在一起
  • 高度依赖引用类型

虽然可以通过 Interface 来进行解耦,但是在开发中依旧需要提倡「高内聚,低耦合」,因此 Unity 和 UE4 都提出了组件的概念来解耦。一个GameObject中包含了多个组件,但是目前的组件还依旧存在有功能/行为,并不是纯粹的数据描述。

比如有个 GameObject 存在有 Location 和 Movement 组件,那么这个 GameObject 应该就可以进行移动了,那么移动这个行为/算法是放在那的?如果放在 Movement 里,那么 Movement 就与 Location 产生了关联,打破了组件之间的封闭性,并不是高内聚的,因此 ECS 提出了个组件之间的切片—— System。

ECS 将所有的行为/算法放在了不同的 System 中,而 Component 只存在数据。

因此使用 ECS 架构,并依照正确的 DO 方法论实现的游戏,获得了以下几个好处

  • 对cache友好。由原来的处置管理每个对象的状态,变为相同类型数据横向聚集管理。
  • 通过数据和行为分离,更加专注于正在解决的实际问题,也容易进行横向开发出更多的系统
  • 由于数据被单独隔离,易于做多线程并行,为 Job System 和 Burst 提供了更好的数据结构
  • 代码更容易上手,通过系统掌握行为,根据输入输出了解系统关注的数据源

当然 ECS 也不是真的就完爆原有的OO GameObject,只是 ECS 对于数据密集运算有着良好的支持。ECS 目前也有着大量的问题,这部分将会留在文章后面叙述。

三、How

3.1 ECS 是怎么实现的

架构图

C:Component 组件

与原有的挂在GameObject上的组件不同,ECS的组件就是一堆数据的集合,不存在方法,只是用来存储数据/状态。

public struct CloneCube : IComponentData
{
    public int Index;
    public float3 Postion;
    public float3 Offset;
}

E:Entity 实例

实体只是一个概念上的定义,指的就是游戏世界里的一个物体,是一系列组件的集合。为了区分不同组件的集合,在代码层面只是使用一个 32位 的整数 ID 表示,其实并没有真正的一个 Object,存在的意义在于生命周期管理。

为了方便实例的查询,提出了一个 Archetype(原型) 的概念,能够存储记录组件组合的信息,两个实例如果有相同的组件,那么组件的信息都会被存储在一个相同的 Archetype 底下。

当组件发生增加或者删除的情况,会把当前的块移动到新的原型里,并交换原有原型里最后的实体补齐空缺。

Archetype

S:System 系统

用来处理游戏逻辑的部分,我们可以在 System 中通过 Component 快速筛选出我们需要关系的 Entity 集合。 System 中不存在数据,只有行为,数据的输入和输出都依赖 Component。

System 其实还存在一个问题,就是 System 为了强调解耦,是不能直接相互调用的,因此对于共享代码需要抽离到单独的 Util 中。对于不同的 System 想访问唯一的 Component,可以在 World 创建唯一的Entity。比如一个叫玩家键盘的 Entity,由键盘组件组成。只需要安排一个System 不断更新这个 Component,就能使其他需要获得玩家键盘输入的 System 得到相同的数据。

目前 Unity 的 DOSTSample 项目中就是这么做的,World 中有一个唯一的 LocalPlay 实例,有一个 UserCommand 组件,被 BeforeClientPredictionSystem 不断 Update 更新本地用户输入。

3.2 ECS 工作流

如果真的将目前的游戏都改成 Data-Oriented 并不容易,目前直接放弃所见即所得的编辑器,显然不是当前最佳的方式(可以按照 DOD 重新编写编辑器,只是目前还不能)。Unity 团队提供了一种 ECS Conversion Workflow 。可以让开发者通过常规 GameObject 来实现编辑功能,在需要 ECS 高性能的部分使用 Conversion Workflow,将 GameObject 在 Runtime 的时候转成纯粹的 ECS data。

Conversion

四、Now

通过写相关 DEMO 和粗览 Unity DOTS Sample 源码,可以明显感觉到,为了符合 ECS 的设计理念,需要仔细考虑设计 Component 中的数据,必须其进行合理的设计和拆分。真正使用起来还是相当费劲的。

ECS 对于UI、复杂技能特性支持度也不好。由于没有办法利用起多态,不能将不同数据存放在一起,所以只能靠多增加 Component 来实现。

目前 Unity DOTS 中的包还基本都处于预览版本,还有相当多的地方都不是很完善,仅仅是只能做些小的 Demo。

五、参考

发表评论

0/200
485 点赞
0 评论
收藏