# 小游戏运行性能优化建议
# 一、背景
游戏运行性能直接影响玩家体验。玩家如频繁遇到卡顿,发烫,闪退,很快就会流失。
游戏性能直接影响平台游戏推荐策略,优秀运行性能的游戏将获得平台更多流量曝光。
- 游戏画面品质和运行性能是一个权衡。
- 游戏流畅度,响应速度和设备发热需要权衡。
- 要考虑不同设备,不同环境的运行兼容性。
# 二、平台推荐值和合格值
分位数 | 游戏启动后增加的内存(MB) | fps | anr | crash | 首帧-可交互耗时 |
---|---|---|---|---|---|
建议值 | 280 | 50 | 0.08% | 0.01% | 8s |
合格值 | 420 | 40 | 0.2% | 0.08% | 13s |
# 三、优化建议
# 内存优化
# 内存调优
在开发小游戏过程中,往往会有很多内存问题,比如内存溢出、内存泄露等。使用内存超过 1G,就有内存崩溃的风险。虽然 Android 平台可用内存较多,但内存十分紧张时,可能会显著变卡。综上,当前机型分布条件下,使用内存控制在 1G 以下,较为妥当。如果游戏复杂,建议多关注线上的内存使用情况以及内存崩溃率。
# 资源纹理压缩
在游戏内存的使用中,图形内存往往是占用的大头。所以对贴图资源的优化是必要的。
最佳实践:选择 ASTC 格式进行纹理压缩。
原理:传统的图片文件格式有 PNG、JPEG 等,这种类型的图片格式无法直接被 GPU 读取,需要先经过 CPU 解码后再上传到 GPU 使用,解码后的数据以 RGB(A) 形式存储,无压缩。
而纹理压缩顾名思义是一种压缩的纹理格式,它通常会将纹理划分为固定大小的块 (block) 或者 tile,每个块单独进行压缩,整体显存占用更低,并且能直接被 GPU 读取和渲染(无需 CPU 解码)。
举例来说,一张 1024x1024 的 JPEG 图片,使用 RGBA 格式,显存占用在 4M~5.3M 左右,而如果采用 ASTC_4x4 纹理压缩格式后,理论内存占用约在 1.3M 左右,相比普通纹理,可以减少 70%+ 内存。
各个游戏引擎,COCOS,LAYA 都提供了对纹理格式的设置。
# Cocos Creator
压缩纹理 | Cocos Creator
# 运行时
# FPS
FPS(Frames Per Second) 越高,游戏感觉越流畅。如果小于30,那玩家会感觉到卡顿。所以应当争取,在绝大多数机型上,FPS保持在30帧以上。
受到手机的限制,平台的限帧是60。并且提供了限帧接口,如果游戏对画面的流畅度要求并不高,建议限制在30,以保证长时间运行下手机不发热。
# CPU 性能优化
绘制调用(draw call) 次数过多,游戏循环中计算量过大,都会造成 CPU 性能下降,尽量减少游戏中的总绘制调用次数,应该尽可能的使用批量绘制。
一般有以下几种优化方案:
- 合并网格:将多个相同材质的网格合并成一个大的网格,减少渲染调用次数。
- 使用图集:将多个小的纹理合并成一个大的纹理图集,减少纹理切换次数。
- 使用批处理:将多个相同材质的物体放在一个批处理中一起渲染,减少渲染调用次数。
- 使用GPU实例化:使用GPU实例化技术,将多个相同模型的物体实例化渲染,减少渲染调用次数。
- 减少透明物体:透明物体的渲染需要进行混合操作,会增加DrawCall的次数,可以尽量减少透明物体的数量。
- 使用静态批处理:将不会发生变化的物体进行静态批处理,减少渲染调用次数。
# IO操作
当玩家玩游戏时,要尽量避免 IO 操作,尽可能预加载图集、音频、TTF字体等
# 运算优化
- 不要在游戏循环中进行繁重的计算操作,因为这可能造成每帧 60 次的大量计算,性能消耗大。
- 考虑使用worker,利用多线程能力,进行异步计算。
# GPU 性能优化
一般来说, 2D 游戏,没有复杂的着色器,基本不会遇到 GPU 性能问题。但是过度绘制的问题仍然存在,如果过度绘制较多,将会消耗大量带宽,进而降低 GPU 性能。
# 内存优化
- 使用合适的的压缩纹理,参考各引擎的纹理压缩的支持情况
- 及时释放使用不到的资源,内存紧张时,限制资源的预加载 在场景切换的时候主动调用ks.triggerGC 主动清理内存
# 其他性能优化建议
- 始终使用批量绘图,将同一图层中的精灵图像打包成一个大的图集
- 为避免加载资源带来的卡顿,考虑资源预加载
- 为避免GC带来的卡顿,对频繁使用的对象,使用对象池
- 使用烘焙光照,而不是动态光照
- 避免使用复杂的像素着色器
# 发热优化
# 优化的重要性和目标
发热问题是小游戏性能优化中的一个关键方面。
- 发热会导致手机降频运行,进一步导致游戏帧率下降、卡顿,甚至出现闪退等现象。
- 发热会极大地影响玩家的游戏体验,降低玩家游戏时长和留存。 优化发热问题的目标在于降低设备在运行游戏时的功耗,提高游戏的流畅性和稳定性。具体而言,要通过优化游戏代码、资源管理、图形渲染等方面,实现高效的性能表现,让玩家能够在长时间的游戏过程中享受流畅、稳定的体验。
# 发热原因
手机发热,都来源于硬件资源的使用,主要的有CPU,GPU,网络。对应要优化的游戏计算部分有
- 每帧要运行的代码的效率
- 每帧渲染的图形的处理
- 持续的网络请求或IO请求
# 分析和优化策略
# 降低 CPU 使用率
- 检查骨骼动画的数量及做法,是否有较多的Spine的骨骼动画更新计算;
- 如果有较多的物理计算,考虑修改物理引擎迭代计算的参数,降低刚体迭代的数目等
- 针对Profiler的Top10函数进行优化,根据性能堆栈定位性能根源并进行优化;
- 使用对象池来管理对象的创建和销毁,减少频繁的内存分配和释放,避免频繁的垃圾回收。
# 降低GPU使用率
- 看看纹理的一些设置是否合理,比如是否压缩、是否开启Mipmap(Mipmap会造成额外的内存问题,根据实际情况开启)
- 通过LOD,减少模型细节,降低渲染质量,减少光照。
- 合并drawcall,提前烘培,使用更简单的shader,关闭一些特效,等。根据游戏情况做优化。
# 降低IO
控制网络流量。游戏中如果有边玩边下的功能,可以控制当游戏处于战斗等本身消耗比较大的场景时,停止下载。游戏比较闲置(如只在进行一些简单的UI操作)时再重新开始下载。
# 使用分包功能
对于基于webasmmbly的游戏,编译时会占用较高的CPU,通过分包功能,减少首包,能优化发热情况。
# 首帧到可交互耗时
# 尽快渲染
所谓的尽快渲染,指的是缩短业务代码注入完成到首屏渲染指令的时间。因此除了前面提到的优化手段外,还可以:
- 精简初始化代码
- 避免在游戏启动时加载过多不必要的模块和数据。例如,如果游戏中有一些非关键的辅助功能,如成就系统、排行榜等,可以将其初始化代码延迟加载。只在玩家真正需要访问这些功能时再进行初始化,这样可以减少游戏启动时的资源占用和运算量。
- 检查并优化游戏的主逻辑初始化部分。例如,减少循环嵌套的层数。如果在启动时有一个复杂的循环用于初始化游戏对象,尽量简化循环条件和操作,以加快代码执行速度。
- 异步加载资源
- 对于游戏中的大型资源,如高清图片、音频文件等,采用异步加载的方式。例如,当游戏启动时,可以先显示一个简单的加载界面,同时在后台加载主要的游戏资源。
- 合理划分资源的加载优先级。例如,对于游戏场景中的背景图片、主角模型等关键资源优先加载,而一些装饰性的道具模型等可以稍后加载。这样可以确保游戏在最短的时间内呈现出主要的视觉元素。
- 图片资源优化 对游戏中的图片进行压缩。根据游戏的实际显示需求,选择合适的图片格式和压缩比例。