优化概论
说起游戏的优化,在游戏开发中经常分为这几步:
- 首先要确定游戏中经常会出现哪些问题 – Profile
- 然后确定在哪些方向进行性能优化 – Analyze
- 最后再尽可能将问题逐个解决 – Solve
游戏开发中一定是先做工具,进行Profile,再进行优化,所以,说优化就不得不再扯一下Profile
常见的工具有一些是引擎和IDE自带的,比如Unity自带的Profiler,就包含了CPU,GPU,Memory等等各式各样的性能分析工具,其他的比如GPA,Xcode Instrument和Visual Studio,Intel自带的内存管理工具在必要的时候也使需要去学习和使用的。
另外一些工具,就需要根据游戏的需求去编写了,比如一键关闭所有特效,一键更改分辨率等等,一键设置场上NPC数量,简单的游戏如啪啪三国是做成快捷键开启Profile功能的,更为复杂的游戏如神秘海域则是通过游戏内控制台来进行更为细致的Profie。
接着,我们再来说说游戏优化中主要的四个考虑方向:
CPU
引发的问题:
- 由于短时间内的计算量太大,导致画面流畅性降低,俗称跳帧
- 发热严重,耗电量高
常见的优化手段:
- 将计算分到多个逻辑帧中进行计算,避免短时间内的性能超过负荷,俗称“分帧”(time-slice)。
- 将可以缓存的数据尽可能的缓存起来,避免重复计算和重复分配内存,常见的示例为“内存池”。
- 使用合理的算法和数据结构,比如:冒泡排序和直接插入排序在整体数组比较有序的情况下效率大大好于快速排序。把快排替换成是优化程序排序效率的一个常见的思路。
GPU
引发的问题:
- 发热严重,耗电量高
- FPS降低
常见的优化手段:
- 优化美术资源,比如合理规划图集,约定好模型的最大三角形面数,制定合理的粒子效果规范。这个可以说是游戏优化中最重要的一个,因此,技术美术在游戏开发中作用巨大。
- 简化或者优化着色器(shader),如在游戏开始前就对Shader进行编译和加载。
- 使用Batching,尽量减少DrawCall
- 使用平台推荐的压缩格式,比如安卓平台的ETC1和IOS平台的PVRTC
IO和网络
引发的问题:
- 网络延迟甚至掉线
- 加载资源导致的跳帧
- 加载时间过长
常见的优化手段:
- 使用独立的线程进行加载,有些引擎如Unity中还能利用协程
- 减少网络包里面的冗余数据
- 合并小包,减少请求数据的次数
- 分帧对回包进行处理
- 限制一定时间内的发包频率
内存
引发的问题:
- 闪退和卡死,比如安卓的Low Memory Killer会在低内存情况下杀掉内存占用过大的程序。
常见的优化手段
- 动态加载和卸载资源,比如在游戏内的时候,我们可以把游戏外的一些UI图集卸载掉。
- 降低资源质量或屏幕分辨率,这是有损优化,一般作为最后的手段
对做过项目的一些思考
需要关注非功能性需求
这一点思考是我从为什么很多看起来不是很复杂的网站,比如 Facebook 需要大量顶尖高手来开发?这个答案中想到的,正如这位答主所说:
事实上,从我的经验来看,一般来说,很多软件项目及产品,其在非功能性需求上的成本,难度和工作量,是要超过功能性需求的。在特定的软件领域,例如网站(尤其是淘宝,facebook这样海量用户规模的网站),金融(银行证券),电信领域,其非功能性需求实现的重要性,工作量,技术难度要远远远远大于功能性需求的实现。而且,功能性的需求的实现,其实在大多数情况下,更依赖于业务的高手(或者好的产品经理)而不是技术的高手,而非功能性需求的实现,恰恰是挑战技术高手的重要课题。
在游戏前端这边做了一段时间,大家都在抱怨游戏前端技术含量低,只能写业务逻辑,但是其中的陷阱就是,作为前端,你应当尽量少写业务逻辑,你关注过一下的模块吗?
- 性能:你有没有在自己的游戏中进行Profile,观察在以上各个参数有没有达到指标
- 安全:你的游戏前端代码的Release版本是否还能被别人轻易反编译,你的游戏是否还能轻易被玩家截取网络包或修改内存数据
- 可测试性:你的前端代码能不能进行单元测试,能不能在QA测试之前就把Model层的所有bug解决掉
- 数据驱动:你能不能做出更优秀的工具来给美术和策划使用,解放他们的生产力?
需要善于划定范围,缩小问题区间
我在过去几个月里,参加了全民突击和崩坏学园2两个Unity3D项目的开发,也遇到了一些性能优化相关的问题,在程序出现问题时,很多时候我们会通过所谓的“经验”去解决问题,比如,上面提到的安全问题,有些开发者就会提前意识到安全的重要性,在游戏推出市场前选择加密这种安全有效的方法,崩坏学园就及时采取了爱加密的游戏加密解决方案,保证了安全性!其实这种Quick Link的能力自然是非常重要,但经验并非万能。
但是从另一方面来讲,当我们无法从自己过往的经验中找到答案的时候,我们就要通过划分范围,缩小问题区间来解决问题了,许多人往往只是在自己的经验中死守一个局部的视野去处理问题,最后的结果就一定不能尽如人意。
总结
其实这四个方面的优化总是相互制衡的,你把一个方面的优化做好了,另一个方面的问题又会出现了,比如,我们如果使用动态加载和卸载资源,这就虽然减少了内存占用量,会在IO上造成加载时间延长的问题。
所以,我们在做游戏优化的时候,不能太追求完美,刚刚好就是真的好(Good Enough Is Fine)。最终使得以上这四个方面能达到均衡即可,切忌在某一方面优化过头,又引发其他方面的问题,此消彼长的情况下,有时反而不如不做优化。