面向数据技术栈DOTS之ECS实体组件系统
本文是面向数据技术栈DOTS的系列文章之一,我们将在本系列文章分享DOTS技术的进展以及未来的发展方向。
在《面向数据技术栈DOTS的C++和C#》文章中,我们介绍了Unity今后的底层基础技术:HPC#和Burst,我们把这种级别的技术栈称为“游戏引擎的底层引擎”。
任何人将可以使用该技术栈来编写游戏引擎,我们也会使用这些技术栈进行编写。同样地,如果你不喜欢我们的底层引擎,你也可以编写自己的底层引擎或按自己的喜好进行修改。
MegaCity
请观看Unite LA 2018大会上,Unity CTO Joachim Ante和Unity全球技术美术总监Martin Kümmel关于《MegaCity》的演讲。
《MegaCity》的灵感来自于知名的香港九龙城寨,是由一个二人团队,短短二个月时间创作出未来风格的城市景观,展示了本文中所描述的Unity新一代引擎技术面向数据技术栈DOTS的卓越性能。
《Megacity》项目现已正式推出,可供下载使用,你可以拆分其中的内容,进行实验与研究,并通过它获得新项目的灵感启发。
《Megacity》项目下载地址:
https://unity.com/megacity
Unity的组件系统
我们要开发的下一个部分是全新的组件系统,Unity一直以组件的概念为中心。例如:我们可以添加Rigidbody组件到游戏对象上,使对象能够向下掉落。我们也可以添加Light组件到游戏对象上,使它可以发射光线。我们添加AudioEmitter组件,可以使游戏对象发出声音。
无论对程序员还是非程序员而言,组件都是非常容易理解的概念,它可以轻松地构建直观的UI。组件的演变非常令人惊讶,因此我们希望保留组件的概念。
我们实现组件系统的方法并没有很好地演变。我们过去使用面向对象的思维编写组件系统。组件和游戏对象都是“大量使用C++代码”的对象,创建或销毁它们需要使用互斥锁修改“id到对象指针”的全局列表。
所有游戏对象都有名称,每一个游戏对象都有指向C++相应对象的C#包装器对象。C#对象可以在内存的任何位置,C++也可以,所以会出现大量缓存丢失情况。我们想尽可能减小这种情况的影响,但可以做的事情并不多。
通过使用面向数据的思维方式,我们可以更好地处理这种情况。我们可以保留用户眼中的优良特性,即只需添加组件就可以实现功能,而同时通过新组件系统取得出色的性能和并行效果。
这个全新的组件系统就是实体组件系统ECS。简单来说,如今我们对游戏对象进行的操作可用于处理新系统的实体,组件仍称作组件。那么区别是什么?区别在于数据布局。
下面是常见的数据处理模式,我们通常在Unity中会这样编写组件:
class Orbit : MonoBehaviour
{
public Transform _objectToOrbitAround;
void Update()
{
var currentPos = GetComponent<Transform>().position;
var targetPos = _objectToOrbitAround.position;
GetComponent<RigidBody>().velocity += SomehowSteerTowards(currentPos,targetPos)
}
}
这种模式会被反复使用:组件必须找到相同游戏对象上的一个或多个组件,然后给它读取或写入数值。
这种方法存在很多问题:
该Update()方法为一个Orbit组件而调用。下次Update()调用会面向完全不同的组件,在下次该代码为另一个Orbit组件运行这一帧时,它很可能造成代码从缓存移出。
Update()必须使用GetComponent()来找到Rigidbody组件。该组件也可以被缓存起来,但必须保证Rigidbody组件不被销毁。
我们要处理的其它组件位于内存中完全不同的位置。
ECS使用的数据布局会把这些情况看作一种非常常见的模式,并优化内存布局,使类似操作更加快捷。
ECS数据布局
ECS会在内存中对带有相同组件集的所有实体进行组合。ECS把这类组件集称为原型(Archetype)。例如:由Position组件、Velocity组件、Rigidbody组件和Collider组件组成的原型。ECS以16k大小的块来分配内存,每个块仅包含单个原型中所有实体的组件数据。
我们不必使用用户的Update方法搜索组件,然后在运行时对每个Orbit实例进行操作,使用ECS时我们只需静态地声明:我想对同时附带Velocity组件、Rigidbody组件和Orbit组件的所有实体进行操作。为了找到所有实体,我们只需找到所有符合特定“组件搜索查询”的原型即可。
每个原型一个Chunks块列表,用来保存原型的实体。我们会循环所有块,并在每个块中,对紧凑打包的内存进行线性循环处理,以读取或写入组件数据。该线性循环会对每个实体运行相同的代码,同时为Burst创造向量化处理的机会。
很多情况下,这个过程会分成多个作业,使处理ECS组件的代码达到几乎100%的核心利用率。ECS会完成所有工作,我们只需要提供对每个实体运行的代码即可。我们也可以手动处理块迭代过程。
当我们从实体添加或移除组件时,ECS会切换原型。我们会把它从当前块移动到新原型的块,然后交换之前块的最后实体来“填补空缺”。
在ECS中,我们还要静态声明要对组件数据进行什么处理,是ReadOnly只读还是ReadWrite读写。通过确定仅对Position组件进行读取,ECS可以更高效地调度作业,其它需要读取Position组件的作业不必进行等待。
这种数据布局也处理了我们长期以来的困扰,即:加载时间和序列化的性能。为大型场景加载或流式处理ECS数据时,主要的操作是从硬盘加载和使用原始字节。
这就是《MegaCity》演示能以几秒时间在手机上完成加载的原因。
实体
实体是什么?实体只是一个32位的整数,所以除了实体的组件数据外,不必为实体保存或分配内存。实体可以实现游戏对象的所有功能,甚至更多功能,因为实体非常轻量。
实体的性能消耗很低,所以我们可以把实体用在不适合游戏对象的情况,例如:为粒子系统内的每个单独粒子使用一个实体。
下一代游戏引擎开发
我们需要开发的下一个部分非常庞大,它是由“渲染器”、“物理”、“联网功能”、“输入”、“动画”等功能组成的“游戏引擎”部分。我们已经开始开发引擎这部分功能,但是它们不会很快完成。
这么说或许有点令人失望,但从另一角度看,却未必会令人失望。因为ECS及建立在其之上的功能都通过C#代码编写,它可以在Unity中运行,所以我们可以编写使用“预ECS”功能的ECS组件。
虽然目前没有纯粹的ECS网格绘制系统,但我们可以编写一个特别的ECS MeshRenderSystem(网格渲染系统),它会使用预ECS的Graphics.DrawMeshIndirect API来实现,同时我们等待着ECS专用版本的推出,这就是《MegaCity》演示所使用的方法:加载、流式处理、剔除、LOD处理和动画制作都使用纯ECS系统完成,而最终绘制处理不使用纯ECS系统。
因此我们可以混合使用ECS的功能,这样做的优点是:我们可以同时让游戏代码利用Burst编译的优点和ECS的性能,不必等待Unity正式推出所有子系统的纯ECS版本。但缺点是:在这种传统阶段中,我们会遇到“结合使用二种不同功能”产生的阻碍。
我们会通过资源包发布ECS HPC#子系统的所有源代码。你可以查看,调试和修改每个子系统,并在更新子系统时有更好的粒度控制,例如:你可以只更新Physics物理子系统资源包,不必更新其它部分。
对游戏对象的影响
游戏对象不会发生任何改变。数十年来,众多开发者成功地使用游戏对象开发出了许多优秀的游戏,这一基础不会发生任何变化。
发生改变的是,你将看到对游戏对象功能的特别改进逐渐转向ECS功能。
API可用性和样板代码
对ECS的常见观点是:ECS需要编写很多代码。因此,实现想要的功能需要处理很多样板代码。现在针对移除多数样板代码需求的大量改进即将推出,这些改进会使开发者更简单地表达自己的目的。
我们暂时没有实现太多这类改进,因为我们现在正专注于处理基础性能,但我们知道:太多样板代码对ECS游戏代码没有好处,我们不能让编写ECS代码比编写MonoBehaviour更麻烦。
Project Tiny已经实现了部分改进,例如:基于lambda函数的迭代API。
Project Tiny的ECS如何适应这些变化
Project Tiny会基于C# ECS开发,它将成为ECS的重要里程碑:
Project Tiny能够运行在完全仅使用ECS的环境,它是没有过去包袱的全新项目。
这意味着Project Tiny纯粹基于ECS,必须使用实际小型游戏需要的所有ECS子系统。
我们将采用Project Tiny的编辑器支持,它会处理所有ECS环境的实体编辑功能,而不仅仅处理小游戏。
小结
面向数据技术栈DOTS之ECS实体组件系统为大家介绍到这里,后续的系列文章中,我们会介绍DOTS更多信息,尽请期待。
更多Unity技术文章分享,尽在Unity Connect平台(Connect.unity.com)。
推荐阅读
官方活动
4月10日晚8点,Unity技术美术专家林翰为大家带来CG角色设计方面的知识分享。了解直播内容,请点击此处。
直播地址:
https://connect.unity.com/events/live_session_cg_character_design
Unite Shanghai 2019
5月10日-12日上海,Unite大会强势回归。技术门票正在热销中,购票即获指定Asset Store资源商店精品21款资源的5折优惠券。
购票请访问:Unite2019.csdn.net
点击“阅读原文”访问Unity Connect