乐趣区

Unity-性能优化经验整理

优化思路

个人优化原则:
1. 减少总量
2. 空间、时间互换
3. 由浅入深

1. 减少总量:
尽量减少内存和 CPU 占用的总量

2. 空间、时间互换:
内存和 CPU 可以互换、CPU 和 GPU 可以互换

3. 由浅入深:
先优化小的细节再优化大的结构

大致手段:
1. 降 CPU
2. 降内存
3. 内存换 CPU
4.CPU 换内存
5.GPU 换 CPU
6. 磁盘换内存

优化思路说明:
尽量减少占用的内存 (资源体积) 和 CPU(计算量),是一切的前提,首先着重减少总量才能更好的进行后续细节的优化。
由于内存比 CPU 便宜多了,所以一般都是内存换 CPU 的,内存不够时再用 CPU 换内存。
比如利用对象池缓存对象,可以省略加载资源、实例化、销毁实例、卸载资源的步骤,可以明显降低 CPU 的消耗。
利用 Loading 进度条按需加载资源,可以减少内存峰值,大量节约内存。

通常来说先优化细节,如果细节优化已经做的很好,没什么太多潜力可以挖了,性能还是消耗太大,那么就需要考虑重构结构了。

性能分析:
使用 Profiler 性能分析器首先确定性能瓶颈出现在哪里,定位性能问题出现的根本原因,按照具体原因去做优化。
通常来说,性能问题大致出现在两个方面:
1. 细节不够好(资源问题、插件问题、代码写法问题)
2. 结构不够好(框架问题、底层 API 问题)

细节问题解决成本低,可以独立调整,对其他部分影响小,可以批量解决。
结构问题解决成本高,牵一发动全身,对其他部分影响大,需要大修、大测。
由于游戏整体是由各个细节组成的,所以当细节做的不够好时,整体也会显现出问题。
反之当结构不够好时,细节即使做的很好,游戏整体表现出的性能也不会太好,两者是相辅相成的。
我的建议是:用严格认真的态度控制细节,用丰富的经验积累出可靠的结构。
前者靠态度,后者靠经验。当我们经验不多时,应该依靠态度严控细节,当我们经验足够时,两方面都要兼顾。

多读官方 API:
多读官方 API, 多读官方 API 多, 读官方 API。重要的事情说三遍,不要重复造轮子。Unity 中有很多性能细节问题都出现在功能不熟悉上,没玩明白导致的,熟悉官方 API 可以让我们事半功倍。C#代码也一样,也要熟悉 C# 官方 API。

优化内存

Unity 中 资源占用的内存量比代码高的多,只要代码不往死掉写,一般不会占用太高内存,我们要特别注意资源的内存占用。
Unity 资源内存占用排行榜
1. 贴图 Textures
2. 动画 AnimationClips
3. 网格 Meshes
4. 音频 AudioClips
5. 材质 Materials

资源内存占用说明:

名称 单个体积 同时使用数量 总体内存占用 内存占用说明
贴图 很多 很大 占用超级大户 远超其他资源(超级土豪 VIP 熟客 需重点关照 利润大)
动画 占用大户 时间越长 关键点越多体积越大(普通顾客 认真对待 利润中)
网格 占用中等 和精细度有关 一般内存问题不会出现在它身上(普通游客 利润小)
音频 占用大 压缩比高 ogg 加进内存后 体积增大 10 倍(土豪游客 出现一次狠宰)
材质 很小 很小 占用很小 数量也少 (邻居 别指望从它身上赚钱了)

1. 减少贴图占用内存
内存占用超级大户,史前怪兽级别的,要优化内存先从优化贴图格式开始。
按照下面 3 步设置,可以极大降低贴图占用内存。
1. 降低分辨率
2. 拆分透明通道
3. 调整压缩格式
4. 禁用 Mipmap
5. 启用 Use Crunch Compression

1. 降低分辨率(Max Size):

根据 Game 摄像机距离物体最近时,物体所占的像素大小 (QQ 截图),来确定最大分辨率。
一般美工或 Asset Store 上下载的资源很可能是高清资源,1024×1024、2048×2048 或更大,我们需要根据实际使用的尺寸确定。
2048×2048 降低为 1024×1024 后 内存会降低为原来的 1 /4 极大降低内存占用 这里是是大头 要控制好。
(图片大小和像素有关 像素点数 = 宽 x 高 = 面积 宽高各变为 1 /2 面积变为 1 /4)

2. 拆分透明通道(Alpha):

不需要 Alpha 通道的一定要去除 Alpha 通道 因为带 Alpha 通道的贴图 Unity 会默认选择 RGBA 格式。
如果不能剔除 Alpha 要把 format 由 RGBA 格式选为 RGB 格式 以减小内存占用。
非渐变的透明贴图 可以调成 RGB + 1bit alpha 的格式 拆分 alpha 通道。
(RGBA 一般各通道是平均分 每份 1 /4 剔除一个通道 体积减少 1 /4)

3. 调整压缩格式(Compression):

尽量选用当前平台支持的最高压缩格式,不要轻易使用 RGBA32 格式,更不要使用不压缩格式,内存天差地别。
只要启用 Compression 选项,Unity 会自动帮我们选用合适的压缩格式 要注意的是压缩格式的支持都是有条件限制的。
当不能使用更好的压缩格式时,Unity 会出现提示,告诉我们哪里有问题。对于压缩格式:一般要注意以下两个问题。
(1)不需要 Alpha 透明通道的贴图 请在 PS 里剔除。
(2)高压缩比格式要求图片宽高是 2 的倍数 (4 的倍数更好) 宽高不能被 2 整除,会导致不能用高压缩比的格式。
宽高禁止出现奇数,必须都是偶数,打成图集的图片是 2 的倍数即可,单独使用的图片宽高要是 4 的倍数。

以一张 512×512 分辨率的图片为基准 测试不同平台 不同压缩格式占用的内存:

PC 常用图片格式:DXT

图片格式 512×512(启用 Mipmap) 1024×1024(启用 Mipmap) 图片质量 内存(压缩比) 说明
RGBA 32 bit 1M(1.3M) 4 M(5.3M) 最高 最大(1) 透明高清无压缩 最靠后选择
ARGB 16 bit 0.5M(0.7M) 2 M(2.7M) 中(1/2) RGBA32 阉割版 靠后选择
RGB(A) BC7 256KB(341.4KB) 1M(1.3M) 中高 小(1/4) 透明高清高压缩 次优先选择
RGBA DXT5 256KB(341.4KB) 1M(1.3M) 小(1/4) 透明中清高压缩 最优先选择
RGB 24 bit 0.8M(1M) 3M(4M) 很大(3/4) 不透明高清无压缩 最靠后选择
RGB 16 bit 0.5M(0.7M) 2M(2.7M) 中(1/2) RGB24 阉割版 靠后选择
RGB DXT1 128KB(170.7KB) 0.5M(0.7M) 很小(1/8) 不透明中清高压缩 最优先选择

RGB(A) BC7:高质量高压缩格式 但是 mac 上不兼容

Android 常用图片格式:ETC

图片格式 512×512(启用 Mipmap) 1024×1024(启用 Mipmap) 图片质量 内存(压缩比) 说明
RGBA 32 bit 1M(1.3M) 4 MB(5.3M) 最高 最大(1) 透明高清无压缩 最靠后选择
ARGB 16 bit 0.5M(0.7M) 2 MB(2.7M) 中(1/2) RGBA32 阉割版 靠后选择
RGBA ETC2 8 bits 256KB(341.4KB) 1 MB(1.3M) 小(1/4) 透明中清高压缩 最优先选择
RGB 24 bit 0.8M(1M) 3M(4M) 很大(3/4) 不透明高清无压缩 最靠后选择
RGB 16 bit 0.5M(0.7M) 2M(2.7M) 中(1/2) RGB24 阉割版 靠后选择
RGB ETC2 4 bits 128KB(170.7KB) 0.5M(0.7M) 很小(1/8) 不透明中清高压缩 最优先选择
RGB ETC 4 bits 128KB(170.7KB) 0.5M(0.7M) 很小(1/8) 不透明中清高压缩 次优选择

IOS 平台常用图片格式:PVRTC

图片格式 512×512(启用 Mipmap) 1024×1024(启用 Mipmap) 图片质量 内存(压缩比) 说明
RGBA 32 bit 1M(1.3M) 4 MB(5.3M) 最高 最大(1) 透明高清无压缩 最靠后选择
ARGB 16 bit 0.5M(0.7M) 2 MB(2.7M) 中(1/2) RGBA32 阉割版 靠后选择
RGBA PVRTC 4 bits 128KB(170.8KB) 0.5M(0.7M) 很小(1/8) 透明低清高压缩 最优先选择
RGBA ASTC 6×6 block 115.6KB(154.7KB) 456.9KB(0.6M) 很小(~1/8) 透明中清高压缩 iPhone6 之后首选
RGB 24 bit 0.8M(1M) 3M(4M) 很大(3/4) 不透明高清无压缩 最靠后选择
RGB 16 bit 0.5M(0.7M) 2M(2.7M) 中(1/2) RGB24 阉割版 靠后选择
RGB PVRTC 4 bits 128KB(170.8KB) 0.5M(0.7M) 很小(1/8) 不透明低清高压缩 最优先选择
RGB ASTC 6×6 block 115.6KB(154.7KB) 456.9KB(0.6M) 很小(~1/8) 透明中清高压缩 iPhone6 之后首选

PVRTC:IOS 原生支持的一种压缩格式 优点是:高压缩比 兼容性好 缺点是:有损压缩 图片质量较差,对于透明像素的边缘和渐变的透明度有特别明显的失真。
ASTC:IOS 支持的一种新的压缩格式 优点是:比 PVRTC 更高的压缩比和质量,透明贴图质量更高 缺点是:兼容性差 iPhone6 才开始支持该格式,iPhone5 和之前不支持该格式。

参考资料:

《干货:Unity 游戏开发图片纹理压缩方案》https://www.jianshu.com/p/f7c…
《Unity3D 的图片压缩格式详解》https://www.jianshu.com/p/bec…
《Unity3D~ 纹理格式》https://www.cnblogs.com/rayya…
《Unity 中一个简单的图形优化指导》http://gad.qq.com/program/tra…

4. 禁用 Mipmap

Mipmap 相当于 Texture 的 LOD 启用后会生成多级纹理 会占用更多的内存 好处是会让贴图看起来更平滑。
启用该选项会生成多级小贴图 内存会增加 1 /3。

5. 启用 Use Crunch Compression

Crunch 是 Unity 支持的最新压缩格式,压缩比非常高,如果你用其他格式,图片依然很大的话,这个格式将会成为你的神兵利器。
Crunch 支持 Android 和 IOS 平台,能把图片压的很小,但图片质量有损失,可以通过调节质量百分比来解决。
Crunch 在 Editor 下,计算压缩的时间很长,77 张 2048 的图片要压缩 5~8 分钟左右。
对于大量贴图的更新来说,整个团队的开发人员都要消耗相当长的时间来导入贴图,十分痛苦 很容易拉仇恨(说的就是我自己)。

参考资料:

《Unity 性能优化之内存 - 贴图格式优化》https://segmentfault.com/a/11…
《Unity 优化(一):图形优化》https://gameinstitute.qq.com/…
《[Unity 优化] unity 图片 mipmap》https://www.jianshu.com/p/651…
《Unity 2017.3 中的 Crunch 纹理压缩库》https://www.sohu.com/a/204935…
《Unity 官方文档 Texture》https://docs.unity3d.com/Manu…

2. 减少动画占用内存
总结一下优化动画的手段:
1. 减小动画长度
2. 减少骨骼数量
3. 减少关键帧密度
4. 减少动画精度

1. 减小动画长度
有些动画实际使用的的长度,远小于动画时长,如果有被加快使用的动画,应该考虑改短动画,降低大小,Animator 里加快速度不会减少动画大小。

2. 减少骨骼动画
动画导入后,查看有无无效的骨骼点,删除这些节点可以降低动画大小。
(1)Animation 里 骨骼点变成黄色说明缺少该骨骼点,需要确定是骨骼点丢失还是不需要这些骨骼点,如果动画正常,则说明这些骨骼点无效可以删除。

(2)注意 IK 骨骼点 如果不需要使用反向动力学功能,而动画里又包含了 IK 骨骼点, 则删除这些骨骼点(通常是美工做完动画忘删 IK 了)。

3. 减少关键帧密度
启用 FBX > Animation 选项 > Anim.Compression 选项 > Keyframe Reduction
Keyframe Reduction 官方文档翻译:减少导入的冗余关键帧。如果选择,将显示动画压缩错误选项。这将影响文件大小(运行时内存)和如何评估曲线。

4. 减少动画精度
Unity 存储 Animation 的数值精度比较高,可以通过减少数值精度的手段,降低动画大小。

参考资料:
《Unity 骨骼动画资源解析与优化》https://www.cnblogs.com/damow…
《unity 性能优化之降低动画文件的大小》https://blog.csdn.net/rhett_y…
《Unity 优化翻译官方文档(三) Animation Clips》https://blog.csdn.net/dengshu…

3. 减少网格占用内存
1. 减少顶点
2. 开启 Optimize Mesh 选项
3. 关闭 Mesh Compression

1. 减少顶点 Vertex
减少顶点会显著的提升网格的性能,减少内存的占用,通常来说,在可以实现美术效果的前提下,顶点越少越好。

2. 开启 Optimize Mesh 选项
官方文档翻译:为了更好的 GPU 性能,顶点和索引将被重新排序。需要严格顶点排序的技术,如网格变形或特殊的粒子网格发射器效果,应该禁用此选项。

3. 关闭 Mesh Compression
开始网格压缩可能会导致 Optimize Mesh 失败,占用更大的内存。

参考资料:
《Unity 的 Mesh 压缩:为什么我的内存没有变化?》https://www.cnblogs.com/muron…

4. 减少音频占用内存
音频同时使用的数量较少,但是压缩比率大,默认音频格式加进内存后,体积会增大 10 倍。
对于较大的音频文件 (超过 200KB),要特别注意。
Load Type 改为 Streaming,可以极大降低内存占用。
建议较长的音频使用.mp3 或.ogg 格式,较短的音频使用.wav 或.aiff 格式。

参考资料:

《Unity 优化翻译官方文档(四) —— Audio Clip》https://blog.csdn.net/dengshu…

5. 减少材质占用内存

材质性能排行(由高到低):
1.Unlit 仅为纹理,光线不产生效果
2.VertexLit 顶点光照
3.Diffuse 漫反射
4.Normal Mapped 法线贴图
5.Specular 高光
6.Normal Mapped Specular
7.Parallax Normal Mapped
8.Parallax Normal Mapped Specular

尽量用性能消耗更小的 Shader 来实现效果,复杂的 Shader 需要专人定制、管理和维护,写好 Shader 需要时间和经验的积累。

参考资料:
《Unity3D Shader 性能排行》https://www.cnblogs.com/tim-u…
《UnityShader 学习资料推荐》https://blog.csdn.net/wwlcsdn…
《Amplify Shader Editor 手册(中文版)》https://blog.csdn.net/Debugge…

优化 CPU

1. 降低 DrawCall
2. 注意代码规范
3. 内存换 CPU
4. 使用异步代替同步

1. 降低 DrawCall:
消耗 CPU 过大的情况很容易出现在图形渲染上,合并批处理,降低 DrawCall 可以极大的提升性能。比如使用动、静态批处理,GPU Instance 技术。

2. 注意代码规范:
良好的代码规范会让你获得更好的性能,并且避免很多陷阱,减少入坑的几率,节约时间。多读官方 API,避免重复造轮子。

3. 内存换 CPU:
CPU 不够时,不但可以想方法降低 CPU 的消耗,也可以考虑用内存换 CPU。使用对象池就是一种常见的内存换 CPU 的手段。

4. 使用异步代替同步:
使用异步 Async(非阻塞式)代替同步 Sync(阻塞式),可以优化用户体验。比如加载时用异步加载,显示进度条就属于这种方式。
但是通常异步比同步消耗的时间更多,遇到的问题更多,所以要合理使用异步和同步。

参考资料:
《[Unity 优化]减少 DrawCall:批处理》https://blog.csdn.net/lyh916/…
《Unity 官方文档 -DrawCallBatching》https://docs.unity3d.com/Manu…
《Unity GPU Instance(大量相同网格物体合批)》https://segmentfault.com/a/11…
《Unity 对象池》https://blog.csdn.net/wangjia…
《Unity3D 研究院之异步加载游戏场景与异步加载游戏资源进度条》https://www.xuanyusong.com/ar…

退出移动版