zifa2003293 发表于 2022-12-26 19:05

游戏开发47课 性能优化5


3.5 多线程

上面这张搞笑动图相信很多人都看过,它形象生动地表明了当今设备多核常态化但主核承担大部分工作忙到“吐血”而其它核在打酱油的囧态。在游戏开发中,我们可以将一些逻辑通过创建线程的方式独立出去,交给其它CPU核心处理,以缓解上面提到的现象。可独立成线程处理的模块:

[*]文件IO。建立一个IO线程,定时检查文件队列是否有数据,若有便启动IO加载,直至文件队列为空,又回到空闲轮询状态。主线程就不会以为文件IO而处于等待状态。
[*]骨骼动画。如果同屏动画角色多,将占据大量CPU计算。可创建一个或多个线程处理骨骼动画计算,加速渲染流程。但随之而来的是同步问题。
[*]粒子计算。粒子的多线程跟骨骼动画类似,也存在同步问题。
[*]渲染线程。将渲染抽离出一个线程,主要是解决CPU与GPU相互等待的问题。常见的一种做法是建立一个渲染线程,定期去查询渲染队列是否有数据,如果有就提交至GPU进行绘制。
[*]音视频编解码。如果游戏有涉及音频频播放,而它们又占据了较大的消耗,那么开辟独立的线程处理音视频编解码是有必要的。
[*]加密解密。加密解密涉及的算法通常较复杂,占用较多CPU性能,而且常常伴随着文件IO或网络IO,所以此时非常有必要将它们交给独立的线程处理。
[*]网络IO。目前大多数游戏都会开辟一个线程专门收发网络数据,以避免网络处理影响主线程。
值得注意的是,线程切换会带来额外开销,同步和死锁问题也会提高逻辑复杂度和调试难度,是悬在程序员头上的一把大刀。
3.6 引擎模块

引擎内部最耗CPU性能的模块通常有:骨骼动画/粒子计算/物理模拟/导航网格/相机裁剪和渲染等等。
3.6.1 动画


[*]降低动画采用频率。
[*]减少关键帧数据。
[*]缓存动画的插值结果。
[*]用简单曲线插值(线性插值)代替复杂插值(贝塞尔曲线)。
[*]判断动画所附对象的可见性,如不可见,则不更新动画。
[*]控制同屏动画的个数。
[*]不同画质加载不同级别的动画资源。
3.6.2 物理


[*]静态物理只用静态碰撞体。
[*]降低物理模拟频率。
[*]禁用复杂的碰撞体(如模型碰撞体),取而代之的是用若干个简单几何碰撞体组合。
[*]若物体不可见,则关闭物理模拟。
[*]控制同屏物理物体个数。
[*]对物体的重要程度做分级,重要性低的角色采用简单物理效果或者删除物理效果。
[*]Raycast射线检查虽好用,但性能消耗也高,要尽量降低视频频率,防止重复调用。
3.6.3 粒子


[*]粒子资源优化:上一篇
[*]部分粒子特效考虑转成序列帧渲染。
[*]考虑是否可用缓存/预加载/预计算的优化方式。
[*]对特效的重要程度分级,不重要的粒子不计算或者降频。
[*]若GPU的负担比CPU小,可以将粒子更新计算移至GPU端。
[*]粒子物体可见性判断,不可见则不计算和渲染。
3.6.4 导航

简化导航网格,用最少的面数表达复杂地面的导航构造,比如用平面代替地面凹凸不平的路面(详见2.5)。寻路计算复杂,逻辑上须控制调用频率,避免扎堆寻路。
3.7 逻辑优化

逻辑的消耗也是CPU负担高的罪魁祸首,主要体现在AI/算法/脚本等等模块。
3.7.1 AI优化

为了简化玩家操作和让怪物更加拟人化,目前大多数游戏都加入了AI功能,而AI行为树(下图)是实现AI的关键。




但是正常情况下,AI行为树要每帧更新,每次需从根节点搜寻,一直找到合适的节点才更新,最坏的情况会遍历整棵树。常用的AI行为树优化方法:

[*]缓存当前Action节点路径。即将当前正在更新的节点和其父亲节点都缓存起来,下次要更新时优先这个路径搜寻,避免遍历整颗树。如果当前Action节点是持续性的,则这段时间内无需搜索节点,直接更新该节点即可。
[*]降低AI的更新频率。即强制降低AI频率达到减负的目的,另外还可以尝试主次法/摊帧法/动态调帧法优化AI的更新。
页: [1]
查看完整版本: 游戏开发47课 性能优化5