Unity程序员如何把控项目性能
作为一个主程,应该如何把控控游戏项目的质量,游戏项目的性能。在今天的文章中我将会给大家分享一些我的项目经验,希望能帮助大家顺利带好自己的游戏项目,转型为优秀的技术管理者。
1 从项目管理的角度来把控项目性能
医学里面有句名言, 治病最好的方式是防范于未然, 做项目把控项目的性能问题和稳定性也是一样的,最好从开始做项目的时候我们就要引入项目管理机制来尽早的发现性能问题。很多同学带的项目,项目做完了真机还没有测试过, 项目做完了还没有看过内存占用,项目做完了才知道包体大小,项目做完了才发现某些手机帧率低,项目做完了才发现某个时候游戏卡,这些如果都等项目做完了才去做各个平台的综合性的测试,再暴漏出来的问题查起来就不好查了,因为你对性能参数,稳定性,没有一个长期变化的过程积累, 比如你不知道是什么时候加了哪些功能,导致项目的内存翻倍的,你不知道什么时候加哪些功能导致某个闪退的bug引入的, 你不知道什么时候加了哪些功能导致系统的性能下降,在手机上的帧率降低的。所以这里给大项目技术管理者一个最重要的经验就是要尽快的做好技术验证,尽早的引入全平台测试,尽早把控性能参数,把控项目的稳定性,把控项目的各项指标数据等,这些才是保证项目稳定的强有力的手段。关于这块我给大家介绍几个比较好用的手段:
(1) 项目开始的时候,尽早的放出游戏可能等量的数量级物体,来测试游戏的渲染性等性能问题做技术验证, 尽快优化与调整如定制渲染管线,优化shader, 优化渲染相关的。
(2) 尽快引入测试,同时每个礼拜要做至少1次或2次全平台的功能测试,测试的颗粒度可以由项目开发的功能进度决定,进度越快,测试的颗粒度可以更大一些,方便监控开发哪些功能引入了哪些性能问题,方便管理者更好的把控项目,做到心里有数,比如内存从30M~100M是加了哪些功能导致的,这些功能引入了哪些资源导致的。内存从100M到150M又是开发了哪些功能,技术管理者要很好的把控好这些项目的各项指标,才能在最后能确保项目稳定上线。
(3) 技术管理者每天要review 下属的代码,今早的发现实现不合理的代码编写,会有潜在的系统风险的代码,通过review代码来提升团队的技术能力和培训,同时通过review代码来提升代码质量和把控稳定性。
对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏开发的技术大佬,欢迎你来交流学习。
2: 从框架设计的角度快速定位问题
经过严格的工程管理和项目,大部分的问题基本上都在早期很快就能发现,并能很快解决,并且代码一直每天都做了review,代码质量应该会不错,但是随着项目功能越来越多,越来越复杂,综合功能越来越强的时候,某一个版本会出现一些特别难定位的问题,比如闪退, 看底层闪退的日志,可能挂在了引擎内部的接口函数,不好定位是业务上层的哪个代码引起的报错,或者是哪个数据异常导致底层的函数执行的时候挂了,特别是脚本语言开发的游戏闪退,脚本语言层报错一半都不会闪退,一般都是底层的引擎native代码报错,通常这种错误都是业务逻辑层错误的异常数据,异常调用导致的,而这种错误又不好定位,因为挂在了native层,定位不到是哪里的代码脚本引起的。有时候某个版本在运行的某个时候突然出现游戏帧率下降,这个时候定位问题成为了解决的关键,如何更好定位问题呢?一般我会采用隔离法,比如把游戏怪物的AI隔离,把粒子播放的功能关停等等。通过好的框架和好的代码逻辑的组织,能关闭掉项目的某些功能,来验证问题的可能和定位问题可能发生的方向也是非常重要的,所以在做框架设计的时候,要按照功能很好的组织好代码。
3 游戏性能调优常用的准则与解决方案
前面介绍定位问题的主要手段,问题明确后再针对性的解决。其实游戏常规的性能问题和解决方案可以分成几个类型,下面总结归纳一下,并给出一些处理方案,归纳如下:
(1) 渲染相关的性能问题;
a: 改变问题的需求,比如减少物体渲染数目,取消实时光照,取消实时阴影, 关闭抗锯齿, 减少粒子特效等,也可以根据手机的中高低端来关闭某些功能。
b: 节约渲染时的内存,将纹理采用平台支持的格式,如iOS上使用PVRTC;
c: 通过场景管理来快速物体裁剪,隐藏不在摄像机范围内的物体;
d: 通过LOD来优化远处的物体渲染性能;
e: 通过定制渲染管线来获得很好的性能,比如项目要实时光照,向前渲染管线为了支持任意多的光源,每个光源都要绘制一次物体,这样浪费很多计算性能,所以可以通过定制渲染管线的策略来满足需求获得好性能,比如游戏要支持2~3个实时光源,所以可以定制渲染管线,让多光源的光照计算只经过一次pass,这样就有轻量级渲染管线,延迟渲染管线的策略。
f: 优化渲染效果shader的写法, 很多游戏我们使用的是默认的shader,shader的性能可能不够好,或者可以采用效果接近但是shader算法性能更好的shader来实现;
g: 组织好游戏场景的物体内容,尽量能减少Set Pass Call的调用和Shader切换;
h: 合并渲染批次: 静态合批,动态合批, GPU Instancing合批;
i: 优化动画顶点采样,用空间换时间,将动画每帧顶点位置预先采样到纹理;
j: 优化大量的矢量字渲染,如使用位图字体等;
(2) 物理引擎相关性能问题;
a: 尽量不使用物理引擎,比如有些只是为了防止穿透,就可以通过地图格子的方式来处理,有些只是为了做碰撞检测,可以自己编写代码来实现;
b: 减少刚体的数目;
c: 用性能更好的碰撞器,比如球型碰撞器的性能就要比Box碰撞器的性能好;
d: 更换不同的物理引擎,如使用定点数的物理引擎能在低端机上获得很好的性能;
e: 调整物理引擎迭代的次数和一些运行时候的参数配置;
(3) 运行时内存吃紧导致性能问题;
a: 释放没有使用的内存,没有使用的纹理等;
b: 优化内存分配器,大量创建和释放的对象尽量通过cache缓存起来,如节点池
c: 干预垃圾回收器,来回收对象垃圾;
d: 时间换空间来优化内存;
e: 使用显卡支持的纹理压缩格式,压缩纹理内存;
f: 通过改变加载的时机,避免内存峰值的冲击波,比如场景切换的时候,同时存在两个场景的内容。避免某一瞬间内存飙升;
g: 定制内存分配器;
…
(4) 平台差异导致的性能和稳定性问题;
a: 根据不同的手机来设置目标运行的帧率;
b: 根据不同的手机来设置某些特效,阴影等不显示;
c: 通过修改shader来抹平不同平台的shader的渲染差异;
d: 不同的手机平台采用不同的渲染效果的shader;
…
(5) 不规范的逻辑代码编写导致的性能问题;
a: update里面函数调用次数过多导致的调用开销;
b: update里面大量的查找与比较等算法导致的开销;
c: update里面大量的创建对象;
e: update里面大量的字符串的拼接;
f: update里面复杂的计算;
g: 可以把逻辑迭代降低一些频率,比如逻辑可以用30FPS迭代,渲染用60FPS迭代,减少逻辑运算导致的开销等。
…
(6) IO导致的性能问题;
a: 同步IO读写卡死游戏主线程,导致帧率下降, update同步写游戏数据到文件
b: 同步读写网络IO导致的游戏帧率下降;
c: 同步加载游戏资源导致的游戏帧率下降,操作反应延时;
…
今天的分享就到这里了,更多的游戏主程架构师进阶的内容可以关注我们。