找回密码
 立即注册
查看: 138|回复: 0

千人同屏战斗:Unity DOTS在《重返帝国》中的应用

[复制链接]
发表于 2024-7-15 18:53 | 显示全部楼层 |阅读模式
导语:在本年的游戏开发者大会(GDC 2023)上,腾讯游戏带来了18场主题演讲,此中展示了AI、衬着、引擎、触觉反馈、音频等自研技术在《王者荣耀》《和平精英》《重返帝国》《暗区突围》《PUBG MOBILE》等头部游戏中的前沿应用和创新打破。本文为“千人同屏战斗:Unity DOTS在《重返帝国》中的应用“分享的图文版干货内容。

演讲者简介

肖健(腾讯天美T2工作室客户端组主法式)
肖健于 2014 年插手腾讯天美工作室群,目前担任《重返帝国》客户端组主法式。肖健在游戏框架、Gameplay和性能优化等方面都有着丰硕的研发经验。
侯仓健(腾讯天美T2工作室引擎负责人)
侯仓健于2014年插手腾讯游戏,曾参与多款手游的研究与开发工作。侯仓健于2019年插手腾讯天美工作室群《重返帝国》团队,目前主要负责游戏引擎和东西链的开发工作。

以下是演讲实录:





[ 《重返帝国》手游画面实录(见视频文件) ]

《重返帝国》是一款高品质全3D SLG手机游戏,游戏场景规模宏大,玩家操作自由多变,画面上经常会呈现超过1000个士兵一起战斗的场景。在有限的移动设备性能上,需要同时兼顾性能与品质,团队在测验考试过C#、C++以及DOTS等多种技术方案的选型与研究后,最终选择了Unity DOTS。
Unity DOTS对于团队可以说是一次敢为人先的选择,当时市面上并没有斗劲知名的使用这项技术的游戏项目,所以这项技术最后呈现出来的效果其实是没有太多参考的。其次,当时DOTS是处于一个斗劲初期的版本,Unity官方还在不竭的改削和完善,这意味着团队享受不到新的features,甚至可能需要措置一些潜在的隐患,这对团队来说是不小的挑战。

实战分享


团队一方面与Unity官方保持密切的合作与交流,另一方面颠末多次的技术迭代与优化,最终在《重返帝国》项目上取得了很好的实践效果,在移动设备上为玩家呈现了极高品质的视觉效果。同时团队也堆集了一套行之有效的方式论,以下总结了几点分享给大师。
1. Job数据依赖分析与优化,提升整个系统的并发性
2. 将部门ECS System的Job与非ECS逻辑并行,充实阐扬多核
3. 逻辑数据显示分手,提升chunk内存操作率,减少资源加载带来的卡顿
4. 针对System进行逻辑降频,保证效果同时也提升性能
当我们完成了整体的框架设计和核心的实现后,在进行性能分析的时候发现Job的并发性并不高,且worker存在大量的idle状态,导致系统的整体耗时偏高。为此,我们专门开发了静态分析东西辅助我们找出System之间的读写冲突与依赖,通过数据拆分、数据备份来解决冲突,让耗时较高的Job能够并行。



[ 数据依赖静态分析东西 ]



[ 数据拆分化决写入冲突 ]



[ 数据备份解决读写冲突 ]

基于东西的分析,我们不竭的细化和调整,解决了数据的冲突依赖,显著提高了Job的并发性,最终达到了我们相对对劲的并行效果。



[ 数据依赖优化后Job的并发执行 ]

之后,我们还将System按照功能进一步的细化拆分,把一部门Job的执行提前到与非ECS代码逻辑并行,进一步从整体上提高了我们的游戏帧率。



[ ECS Job与非ECS逻辑并行 ]

在进行了Job并行性优化之后,我们发此刻大地图上拖动时存在由于Entity资源同步加载导致的一些耗时峰刺,这对玩家来说是体验上的损掉。所以我们针对Entity,使用逻辑与显示分手,一方面让资源可以异步加载减少卡顿,另一方面也提升了单个chunk的内存操作率减少CPU的cache missing。



[ 逻辑显示分手-资源异步加载 ]

最后,我们在不影响效果的前提下,针对部门System进行逻辑降频与错帧(如移动逻辑计算相关的System降到12帧、耗时较高的MoveJob与AnimatorJob错帧执行),让整体的耗时更加平滑,而且有效的降低了游戏的功耗。

性能优化


当时我们使用的是Unity2019版本,Hybrid Render V1版本,为了能更顺利的将DOTS适配到我们的项目中,我们也在原始框架的基础上,在资产与衬着方面也进行了大量的按需开发。
我们在接入DOTS技术栈时,主要面临了以下3个问题:
1. 资源兼容性:因为在接入时已经处于项目中期,很多游戏资产及对应的出产流水线已经成型,所以如何将已有游戏资产改变成可在DOTS技术栈中运行的资产,是我们需要解决的问题。
2. 逻辑阶段的基础开销过大:可能会导致千人同屏场景呈现时呈现卡顿。
3. 衬着阶段无法改削自定义的材质属性:因为我们对于战斗场景的还原重度依赖 GPU Instancing技术,所以需要很多自定义的材质属性可以在运行时被复写。
于是,我们针对以上问题逐个研究核心痛点,找到了适合我们项目的解决方案。
在资源兼容性方面,在综合评估了各种方案之后,我们决定实现一套本身的序列化和反序列化流程。我们的方案分为离线和运行时两个阶段:离线时,我们将游戏中各类资产对应的prefab拆分成二进制文件和引用到的资源文件;运行时,我们创建了一个“deserialize world”,用来把离线时生成的二进制文件和资源文件反序列化,生成entity。当entity生成好后,我们再把它们移入default world进行运行。这样我们既可以在资产制作阶段使用我们熟悉的prefab,也可以减少运行时的转换时间。



[ 资产Entity实例化 ]

对于HybridRenderV1在逻辑阶段的开销过大,我们定位到了核心的瓶颈是主线程阻塞。比如整个生成合批信息的过程都是放在主线程中进行的,这个过程有很大的优化空间。
我们的优化标的目的就是多线程化,充实操作移动端的多核优势。其实在生成合批信息时,分歧的RenderMesh必然对应分歧的batch,任务本身具有可多线程化的特性。所以如下图所示,我们分配了一个较大的缓存数组,数组的大小与线程数量和RenderMesh数量相关。多个线程并行完成对含有RenderMesh的Chunk进行筛选,并填入缓存数组的指定位置。因为在缓存数组中,每个线程都有本身的写入空间,所以多线程并行时,不会发生数据写入冲突。



[ 多线程RenderMesh Batch ]

我们还对游戏中LOD的布局进行了优化。我们游戏中的模型一般有4层LOD,在转换成entity后,将会有6个相关的entities生成。
过多的entity不仅浪费内存,同时也会导致很多冗余计算(比如同步位置信息),而按照LOD的特点,我们可以只记录单个LOD的信息,在衬着时按需替换成该当显示的LOD Mesh即可,这样我们就可以把原本的4个LOD网格当做一个单独的网格来对待。同时,我们也将LOD Group节点和Root节点进行了合并,Entity的数量也从本来的6个下降到2个,性能也有了提升。
这种方式带来的一个额外好处是当我们更高层级的LOD还未加载完成或衬着压力过大时,我们可以只加载低层级的LOD模型来显示。



[ LOD布局优化 ]

为了在C#中更改材质的Instance属性,我们定义一个和Instance属性完全匹配的IComponentData Struct,在数据对齐方面,我们遵循std140内存数据对齐原则。如下图所示





[ Instance属性对齐 ]

在衬着运行时,我们按照entities的数量预先分配一块大的缓存,之后操作多线程把各个可见的entity的InstanceParam数据复制到Buffer中的指定位置。最后将整个缓存直接提交至GPU,我们就可以按照传统的GPU Instance方式来使用缓存中的数据了。
在有了RenderMesh上的材质信息和mesh数据之后,我们的InstanceBuffer也组织好了,这样通过调用Unity的DrawMeshInstanced接口就可以进行衬着了。



[ Instance Data Buffer ]

以上都是团队在实践中不竭迭代总结出来的宝贵经验,但愿能对那些同样想使用Unity DOTS技术的团队能有所启发。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-8 09:57 , Processed in 0.101061 second(s), 28 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表