图都挂了??那就 https://indienova.com/indie-game ... on-sphere-devlog-4
引言

戴森球与行星级生产基地
目前这个存档的游戏时间是 118 小时。
游戏玩到后期卡吗?这也许是大家最关心的问题之一。
数千艘运输机在忙碌,数千座设施在运作,数万个太阳帆在环绕,数十万货物在运送,斗转星移,地面上所有太阳能板都面朝着太阳… 这计算量可不是闹着玩的!而以上还只是这一个星球。
本篇我将主要介绍为了保证游戏的流畅性,我们是如何实现游戏性能优化的。由于该话题涉及的内容与技术细节实在太多,所以我打算将优化系列分为三篇开发日志,粗略的谈一下我们所用的方法与技术。
游戏的帧率机制游戏中的帧率分为渲染帧和物理帧:
渲染帧主要负责渲染游戏画面;
物理帧主要负责运行游戏逻辑。

我们在游戏中制作了帧率计数器以监控实时性能,其中左边的数字是渲染帧,右边的数字是物理帧。也可以在设置面板中设定渲染帧的帧速率。当开启垂直同步时,渲染帧速率会和显示器刷新率一致。

帧速率越高,GPU 的使用率也就越高,当使用率接近 100% 时,就会自动降低画面帧率。
在游戏的生产系统中,我们需要一套能精确量化,服从“决定论”的逻辑,即同样的操作只能导致同样的结果。要抛开帧率带来的影响,首先是游戏中所有的时间必须按物理帧计数,例如制造时间为 3 秒的“电弧熔炉”,其实在内部逻辑里就是 180 个物理帧。再例如 360kW 的工作功率,在内部逻辑里其实是每个物理帧消耗 6kJ 的能量,颇有一种“量子化”的感觉。

根据以上的逻辑,负责游戏逻辑的物理帧必须相对稳定,不能被渲染帧率所影响,否则玩起来就会感觉时间一会快,一会慢,所以我们将物理帧率锁定为 60 帧,只有当渲染帧率太低时,才会折减物理帧率,以免塞帧。
使用 DOP 来代替 OOPDOP 和 OOP 分别指“面向数据编程”和“面向对象编程”。具体的比较可以写好长一篇文章,而且前人已充分讨论,所以这里就不赘述了。简单来讲,在面对游戏中大量物件的情况下,面向对象会造成许多不必要的开销,造成性能低下,而面向数据编程则将对象中的属性逐一拆分出来,形成紧密排列的数组,使得相关逻辑能够更快速的进行遍历,甚至一些数据还能直接传给 GPU 来处理。
那么为什么大家不都用 DOP 来代替 OOP 呢?这是因为“面向对象”更符合人们的认知和代码书写习惯,也便于项目管理,而“面向数据”的代码写起来有一些反人类,有时甚至相似的结构写好几遍,不方便管理,但实际运行效率却极高。所以为了游戏的优化,我们还是选择了“面向数据”来作为该项目的编程核心思想与框架。
读起来太晦涩了吗?那实在不行的话我们就这样吧!

不开玩笑了,回到正题
物理帧的大部分工作是由 CPU 来完成的,要维持 60 的帧率,一个物理帧允许的 CPU 处理时间不能超过 16ms(毫秒),除开提交 DrawCall 渲染的时间和其他必须的开销,只有大约 11ms 可用于一帧的游戏核心逻辑。
单核游戏?多核游戏?都不是,这是一个 GPU 游戏!目前 CPU 的性能发展遇到了瓶颈,已经没办法再大规模提升单核性能,只能靠提升核心数量。而在很多实际应用的情况下,对核心数量的提升,远不如增加一点点单核频率提升的性能多。
虽然我们在星系的随机生成及模拟、星球地形动态生成、无缝加载等逻辑中使用了多线程来缓解主线程的压力,但是对于游戏逻辑中如此庞大的计算量,就算有 100 个核火力全开多线程完美配合也未必驾驭得了!
从《戴森球计划》的想法诞生之初,就决定了这是一个 GPU 游戏。看那数万颗太阳帆,每一颗的运动都遵循着万有引力定律,每一颗都在计算发电量,每一颗都能被近距离观看。像大规模并行计算这样的工作,使用 GPU 是不二的选择。

我们将凡是可以并行计算的那部分计算工作,全部交给了 GPU,剩下的那部分计算量,只要 GPU 能在 16ms 之内按时渲染出来,CPU 就绝不会掉链子!
这同时也意味着,好的 GPU 的确能为《戴森球计划》带来更流畅的体验,而 CPU 的影响则相对较小。
使用 GPU 来渲染大批量动画在现今主流游戏引擎中,动画大多是靠移动部件或骨骼的位置(Position)、旋转(Rotation)、缩放(Scale)来完成的,如下图:

该建筑的动画是靠旋转移动部件来完成
就是这样一个建筑,它的动画部件已超过 50 个,其中还包括父子 Transform 层级的嵌套,假设我们有 1000 个这样的建筑,那 CPU 需要同时处理的动画子部件就有超过 50000 个,这显然是 CPU 驾驭不了的,而这还仅仅只是建筑动画,并不是核心逻辑。

用 CPU 去逐一计算这 50000 个部件的 Transform 的确是相当不划算的,这些动画其实可以并行计算,只要在画面帧结束时,所有建筑的动画均就绪即可,这对于具有强大并行计算能力的 GPU 来说简直就是小菜一碟。
我们在编辑器中将这个建筑的所有动画帧中所有的顶点位置、法线等信息按照一定的顺序,事先录制在一个 VERTA 文件中。

当游戏加载时,只需要载入这些文件,通过 ComputeBuffer 将所有信息传递给 GPU,这样 GPU 就拿到了所有建筑动画的预烘焙信息,接下来再将每个建筑的动画状态组成一个数组,传给 GPU。

每个建筑的动画状态数据
最后,还需要在 vertex shader 中分析这些数据,逐一还原每个建筑在当前帧的建模。

在 vertex shader 中还原当前帧建模部分代码

按照这个思路,我们成功使用 GPU Instancing 还原了所有建筑的动画,还顺便使用了建筑状态数据来控制建筑贴图上指示灯的开关。在动画和建筑状态更新方面,CPU 被完全解放。而在 GPU 中,仅仅只是多了 150MB 左右的显存数据用于存储所有建筑的动画数据(仅相当于几张 4096 贴图)与若干在 vertex shader 中的寻址与插值计算。可以说是一顿免费的午餐了!
粒子特效为了将特效渲染纳入上面的框架中,所有的建筑特效均无法使用引擎自带的粒子特效,必须将原本是作为粒子来渲染的特效包含在模型网格中,再使用 shader 来逐一实现各个建筑不同的特效需求。

特效片元在制作时就包含在模型中
在游戏中,几乎每一个不同的建筑都定制了不同的 shader,对于同一个建筑特效的不同片元,我们按照类别涂上不同的顶点色,哪些是辉光,哪些是要拉长的,哪些应该从下至上亮度递增,通过不同的片元顶点色,就能做不同的处理。

整个建筑的动作与特效在一个 shader 中实现
需要时刻朝向太阳方向的建筑,逻辑不能让 CPU 来处理。我们将建筑模型按照“底座”、“横向转动部分”、“俯仰转动部分”涂上不同的顶点色加以区分,再在 shader 里面计算这些顶点应该如何旋转,从而朝向目标点。

朝向太阳的 shader 代码节选
有了这套机制,我们就只需要在主线程中计算好每个建筑当前的状态,形成 ComputeBuffer,传递给 GPU 来进行统一渲染,在这个过程中,动画、IK 朝向、特效、自发光变化等等,就统统都有了。
Unity Profiler 性能测试说了这么多,直接上目前的性能测试图吧!
下面是 100 小时规模的存档在 Unity Profiler 里面的性能测试图


开发机配置
从图中可以看到,游戏主逻辑物理帧 CPU 耗时 5.11ms,也就是说理论上每秒可以跑到 195 个物理帧,所以 CPU 这边肯定是没问题,而最终画面能跑多少帧,就看 GPU 的能力了,我的卡是 660Ti,在畅玩了 100 小时后,还能跑上 40 帧,不过我们还将继续不懈的优化,争取将我这张卡的帧率提到 60!
除了物理帧的优化以外,我们还非常看重 C# 的垃圾回收机制的优化,因为过多的内存垃圾会导致游戏时常卡顿,严重影响游戏体验。
一般来讲,每次触发垃圾回收机制都会出现不同程度的卡顿,卡顿出现的频率取决于 GC Alloc 的大小,而每次卡顿的时间取决于数据结构的复杂度。
为了尽可能消除游戏卡顿现象,从立项开始,程序在数据结构上就严格把控,能用数组的地方用数组,尽量少的使用 Dictionary 或 List,凡是物理帧的逻辑除数组扩容等操作以外,均不能产生 GC Alloc,在 UI 逻辑中严格控制字符串的操作,避免不必要的开销。
据测试,目前游戏的 GC.Collect 卡顿时间已控制在 30ms 以内,出现频率为几乎没有。
下图为 100 小时规模下的 GC 性能统计:

游戏主逻辑在物理帧上的 GC Alloc 为 0,只有 UI 上有 132 字节的开销,加上一些引擎必要的开销,每帧总的 GC Alloc 为 5.0KB,目前这个数值处于非常低的水平。
本篇完
在下一篇优化日志中我会介绍物流运输机、戴森云的优化,看看 GPU 是如何轻松达到“数十万”这个数量级!
评论
前排mark
评论
希望p社能好好学学
评论
我在肝 幸福工厂,希望他们能学学,优化太渣了
评论
原帖地址
https://indienova.com/indie-game ... on-sphere-devlog-4/
评论
第四天灾表示P社就是渣渣
评论
也希望P社能进步,不过这俩都是策略游戏,但他们的AI算法并不是一回事吧。
评论
祖宗之法躺着也中枪,瑞典的程序员本来就只有这个水平,一周还只上四天班,不服自己去请硅谷的程序员996来搞啊
评论
你也看不见图?
俺重新编辑了一蛤
评论
CK3都出了,为啥总还有人要P社学
评论
戴森球记得是国产游戏?支持吧。但是大段谈复杂性让读者联想到吃机能的内容是干毛的……骗骗小白?这种评测一看就反感了,如果是宣传文案的话恐怕要起反效果了
评论
这个是开发者的文章吧。主要介绍开发机制的。
评论
五人团队、无敌主程,搞这么个项目还有空公开开发经验,真的太强了
评论
目前 CPU 的性能发展遇到了瓶颈,已经没办法再大规模提升单核性能,只能靠提升核心数量。而在很多实际应用的情况下,对核心数量的提升,远不如增加一点点单核频率提升的性能多。
评论
大致意思就是把一些逻辑交互由CPU运算交给GPU运算,挺聪明的做法,这文章写了大概的思路,具体实现不太清楚怎么做到的,这作者还比较有想法的
也可能是游戏界的一般用法,不做这方面恐怕还真不知道
评论
我的意思是复杂度高的游戏非常多,例如无人深空,光是玩家探索到的已知宇宙可能就有几百个星系,里面天体运行和科技发展之类的如果算起来也是天文数字。但其实看不见的东西游戏都可以用简单的办法处理,并不会太消耗性能。开局说不止一个复杂的星球,其实玩家都已经久历沧海了。
同屏画面和物理运算方面,举个例子年货召唤,连续爆炸物抛射轨迹算起来也是极复杂的,相对而言戴森球里谈复杂时提到的运输机很多,数万太阳帆始终指向太阳这点是个小工程了。
当然,你说5个人的团队那就要肃然起敬了。我一开始以为是米哈游那种集团作案。5人团队,不说程序处理,光是建模也会旷日持久。而且文章如果不是第三方评测,只是开发者谈自己摸索机制中解除各组困难的文章那就更佩服了,其他有心无力的开发组看到这个会深受鼓舞的。
打开steam,上次纯支持购买的太吾绘券跟古剑系列还没下载呢,这次的游戏也考虑购买支持一下。等阵……古剑为了不让steam抽成,好像是官网购买的,序列号都忘了放在哪里了
评论
他用了面向数据编程而不是面向对象编程.
这种方式可以完全支持并行运算.但是缺点是不符合编程直觉反人类.
也就是说完全支持多核并行运算.
这个框架是unity近来主推的,用于复杂场面处理.
图像方面,动画和特效全都打包到了shader里.CPU负责只是状态切换.
还有另一个知乎上也有开发人员分享
那个也可以看看.
这种一般的独立游戏开发者是做不到的.
这是知乎部分作者分享的:
https://www.zhihu.com/question/423211989/answer/1700841044
评论
五人团队,好像程序就一个人,其他原画什么的,根据这篇文章
https://indienova.com/indie-game-news/birth-of-dyson-sphere/
主程序14年曾经在贴吧发过一个虫洞的演示,很有意思
https://tieba.baidu.com/p/3454014631
评论
天朝还是人才济济啊,五个人的团队,NB
评论
对比起来,transport fever 2那个优化简直就是灾难
评论
怎么个骗小白,你这个帽子扣的
编程啊
评论
年轻真好 我司的产品就是纯面向array运行 然而至今无法跑在gpu上 我们底层runtime的做法是一个个纯c的struct叫pocket 然后用地址偏移区分出不同的属性 比如有个section包含所有的rank和dimension信息 pocket最后的section就包含所有的一维化的数据 甚至function operator都可以包含在这样的array里 相当于可以做 (1 2 3 4)(+ - * /)(4 5 6 7) 电路 电子 维修 我现在把定影部分拆出来了。想换下滚,因为卡纸。但是我发现灯管挡住了。拆不了。不会拆。论坛里的高手拆解过吗? 评论 认真看,认真瞧。果然有收 电路 电子 维修 求创维42c08RD电路图 评论 电视的图纸很少见 评论 电视的图纸很少见 评论 创维的图纸你要说 版号,不然无能为力 评论 板号5800-p42ALM-0050 168P-P42CLM-01
·日本中文新闻 唐田绘里香为新剧《极恶女王》剃光头 展现演员决心
·日本中文新闻 真子小室夫妇新居引发隐私担忧
·日本中文新闻 前AKB48成员柏木由纪与搞笑艺人交往曝光
·日本学校 {日本国际学校}梅田インターナショナルスクール
·日本学校 LINE:sm287 陳雨菲、20歳、台湾からの留学生、東京に来たばかり
·日本留学生活 出售平成22年走行48000km 代步小车
·日本华人网络交流 円相場 一時1ドル=140円台まで上昇?
·日本华人网络交流 问日本华人一个问题
·日本旅游代购 富山接机
·生活百科 英国转澳大利亚转换插头
·汽车 【求助】修车遇到困难怎么办?