注:本部分内容来自于我自己在《Unity3D 高级编程》阅读时做的读书总结,供学习参考;
优化技术 1:动静 UI 元素重构分离
为什么需要 UI 动静分离 Unity 中的 UGUI 和 NGUI 系统都是使用了基于网格构建的方式构建 UI 画面;在网格构建之后,都进行了网格合并的操作;
- 不合并 UI 网格,会导致更多的 draw call,合并操作可以减少 drawcall,提升性能;
- 问题
- 无论当前 UI 元素是否会移动,只要当前有动态 UI 移动,就需要重构网格;
- 一些静态的 UI 元素因为需要被合并的原因,也会被要求重构;
如何动静分离 核心点:将会移动的 UI 元素和不会动的 UI 元素分离开,减小部分 UI 元素的合并范围;
- 重绘制合并节点
- UGUI:Canvas
- NGUI:UIPanel
- 把动态的 UI 重新放置到一个新的重绘制合并节点上,静止的 UI 放在原本的节点上,实现动静网格重构分离;
优化技术 2:拆分冗余 UI
UI 元素过重 随着项目开发,一个 UI 当中被装入的东西可能会越来越多,在对 UI 进行实例化以及初始化时消耗的 CPU 也会很大,因此可以考虑将部分冗余或者过重的 UI 拆分出来;
拆分策略 核心点:把当前不需要使用的 UI 拆分成独立运作的界面,在需要展示使用的时候再调用其实例化;
- 甚至还可以考虑把拆分出来的 UI 再进行拆分,把部分点击之后的效果作为预制体进行存储,在需要使用的时候再加载;
优化技术 3:预加载 UI
UI 元素过于琐碎 对于 UI 过于厚重的问题,可以通过拆分的方式解决;但对于大量细碎、小的 UI 元素的加载,则很难通过拆分的方式进行后加载。此时可以考虑把这部分 UI 元素的加载时间提前,使其在场景加载时就完成 UI 元素的实例化;
预加载技术 把 UI 元素的实例化和初始化提前到游戏开始之前,然后再把其隐藏;当需要使用时再显示出来;
- 注意:预加载技术并没有减少任何 CPU 消耗,只是将 CPU 消耗的时间集中提前到了场景加载时;
优化技术 4:UI 图集的 Alpha 分离
为什么需要 Alpha 分离 UI 图集经常需要被压缩,压缩完之后可以减少 UI 的碎片化,减少 IO 读取,提升性能;但会导致画面效果变差,因为压缩时会把透明通道(Alpha)一并进行压缩。此时可以考虑单独把 Alpha 分离出来单独压缩,提升图像效果;
Alpha 分离技术
- UGUI 的 Alpha 分离已经由 Unity 自己完成;
- NGUI 的 Alpha 分离的原理,是将原本以 ETC 方式压缩的一张图,改为压缩一张没有 Alpha 的图和一张有 Alpha 的图;
优化技术 5:字体拆分
字体问题 项目中字体经常需要占用很大的空间,但并不是所有的字都会被经常使用;因此可以把部分 UI 所用到的常用字体拆分出来,提升 UI 性能;
如何拆分 根据场景、界面需要,从字体文件当中提取出当前场景需要的几个数字和字母,使其成为单独的字体文件;
优化技术 6:网格重构优化
Unity 网格重构机制 UGUI 只有将相同材质球的网格合并在一起,才能得到最好的效果;一个材质球对应一个图集,只有相同图集内的图片才需要合并在一起;
- UI 元素颜色更改
- UGUI 中,当元素需改变颜色时,需要改变当前元素的顶点颜色,然后将其重新合并到整块网格里去;
- 因此:颜色更改需要一次性合并重构网格;
- UI Alpha 更改
- 和 UI 颜色一样;需要改变顶点的属性;
- 改变顶点的属性需要一次性合并重构网格;
解决方法:建立独立的特殊材质球 当不希望在 UI 颜色改变时进行网格重构,可以单独建立一个特殊的材质球;
- 此材质球用于控制 UGUI,对这些改变颜色的 UI,使用自己的特殊材质球进行渲染;
- 当颜色动画对颜色和 Alpha 进行更改时,只改变自定义的材质球,把渲染工作交给了新的材质球,不需要重构网格;
- 新的材质球的颜色和 Alpha 上的变化都是通过改变材质球属性来实现的,并不是通过 UGUI 设置顶点颜色来达到效果;
- 减少了 UGUI 重构网格的消耗;
优化技术 7:UI 展示与关闭
一般的 UI 展示与关闭 不进行优化时的 UI 元素,展示经常对应成实例化,关闭经常对应成销毁;这两个动作都很消耗性能;
- 可以选择的策略:
- 在关闭时选择隐藏节点而不是销毁;
- 可以选择提前加载。打开时重新激活节点,而不进行实例化;
如何隐藏节点
- 移出屏幕比隐藏(关闭)的效果更好
- 移出:使 UI 元素在相机渲染之外,但仍在当前 Canvas 节点下;
- 需要注意的是:移出后还需要关闭和此 UI 元素相关的脚本更新内容;
优化技术 8:UI 元素使用对象池
对象池运作规则 对象池就是寄存了一些废弃对象的池子。当程序需要某对象时可以向对象池申请,可以对对象池中的废弃对象再利用,在结束使用后不进行销毁,而是继续保持在对象池当中,减少实例化、销毁的动作,进行内存的重复利用;
- UI 元素的很大一部分消耗,就是 UI 元素的重复实例化、销毁;
运用对象池的经验
- 当程序中有重复实例化并不断销毁的对象时,可以使用对象池对其进行优化;
- 每个需要使用对象池的对象都需要继承对象池的基类对象,这样在初始化时可以针对不同对象重载;
- 销毁对象时使用对象池提供的回收接口,不要重复回收对象,也不要错误的放弃回收;
- 场景结束时要及时销毁整个对象池,避免无意义的内存驻留,此时对象池已不适合新的场景了;
优化技术 9:UI 贴图设置优化
Unity 中的图片 无论导入的图片类型是 PNG 还是 JPG 类型,在 Unity 中导入并需要使用时,Unity 都会自动读取图片内容并生成一个自己格式的图片。对于这些重新生成的图片有很多内容可以进行设置并优化。
UI 贴图设置内容
- 是否需要 Alpha 通道;
- 是否需要进行 2 次方大小的修正;
- 是否去除读写权限;
- 勾选拥有读写权限的图片会使得图片所占内存大一倍;
- 是否去除 Mipmap;
- 选择图片压缩格式;
- 最高的色彩度是无压缩的
- 其次是RGBA16,色彩少了部分,且有Alpha通道
- 再次是RBG24,没有Alpha的全彩色
- 之后是RGB16,色彩少了一半,且无Alpha通道
- 再后是RGBA ETC2 8位和RGBA PVRTC 4位的带Alpha通道的压缩算法
- 最差的是 RGBA ETC 2 4 和 RGB PVRTC 4 位的不带 Alpha 通道的压缩算法
优化技术 10:内存泄漏
两种常见内存泄漏
- 程序泄漏
- GC 未能完整的识别需回收的垃圾而导致;
- 资源泄漏
- 资源使用后没有被卸载;
Mono 内存管理机制
- Mono 介绍
- Unity 中 CSharp 使用 Mono 作为虚拟机,因此 C Sharp 在编写一份后,可以生成中间语言后被各个平台的 Mono 单独解析,实现跨平台运行 CSharp 代码。因此 Mono 也承担了 CSharp 代码的内存管理的任务。
- Mono 内存管理
- Mono 有其自己堆内存,其堆内存在运行时只会增加而不会减少;
- 每次 CSharp 向 Mono 申请内存后都会在堆内存构成的内存池当中为其进行分配,在使用结束后再归还到池当中去;
- 当池内存不足时,会自动进行扩容;扩容时会自动进行一次垃圾回收;
- Mono 的垃圾回收
- Mono 中的垃圾回收会暂停哪些需要使用 Mono 进行内存分配的线程;
- 因此主游戏线程可能会因此卡顿;
IL2CPP 内存管理机制
- IL2CPP 介绍
- Unity 中的 CSharp 在通过 CLR 翻译成 IL 代码后,会继续翻译成 CPP 代码;
- 翻译生成 CPP 代码的过程由各个平台自己拥有的 CPP 编译器实现;
- 因此 IL 2 CPP 的内存管理机制由各个平台的 VM 来进行实现;
- Unity 中的 CSharp 在通过 CLR 翻译成 IL 代码后,会继续翻译成 CPP 代码;
资源泄露工具 1:MemoryProfiler 资源内存的泄漏大部分时由于加载后没有及时释放导致的;
- MemoryProfiler 可以快照内存的信息,并将其以文件的形式进行保存和加载;
性能分析工具:Unity 自带 MemoryProfiler 可以记录各个部分的资源使用情况;
- 检测当前场景当中不需要使用到的资源;这些资源就是泄漏的点;
- 注意:在 Editor 下编辑场景时,Unity 编辑器本身也会加载一些资源,来使其可以在 Scene 视图中被看到,此时这些资源已经可以被 MemoryProfiler 观测,需要判断这些资源是否真的是被泄漏
寻找泄漏资源技巧:通过资源名辨识泄漏物 再给美术资源命名的时候,额可以将其所属的游戏状态放在文件名当中;
- 比如贴图
stone.png
可以叫成Room_stone.png
优化技术 11:针对机型性能优化
如何处理高低端机型的优化 面对不同的机型时,可以根据其性能为其评分,并按照评分得出的表现将机型分为高中低端机型;
- UI 贴图质量:可以对高低机型使用两套 UI 贴图,一套无压缩,无缩小,还有一套则压缩了 UI 贴图;
- 模型与特效区别对待:可以将模型分为多个等级,不同设备使用不同的模型等级。特效也类似,对于低端机型甚至可以删去部分非关键部位特效;
- 阴影:可以使用
QualitySettings
来设置不同质量的阴影,甚至不使用阴影; - 贴图渲染质量:可以使用 Unity 中的
QualitySettings.masterTextureimit
的 API 来设置贴图的渲染质量;
优化技术 12:UI 图集拼接优化
优化策略 对 UI 图集优化可以减少很多浪费的空间,增加 CPU 工作效率; 几种 UI 图集优化方法:
- 充分利用图集空间。将图片拼在一起时,尽量减少碎片空间,比如把大图分开来拼接,大图穿插小图。
- 图集大小控制。如果不控制大小,容易出现 2048_2048 甚至 4096_4096 的像素的图,导致加载 UI 时异常卡顿。我们需要规范图集大小;
- 图片的拼接归类。在没有归类的情况下,加载 UI 会加载一些不必要的图集,导致加载速度变慢,消耗过多内存。例如可以分为常用图集(各个界面都可能用到的)、功能图集(只在某个特定界面才用到的)等。
优化技术 13:GC 优化
- 缓存变量;
- 减少逻辑调用;
- 清除链表;
- 使用对象池;
- 减少创建不必要的字符串、减少不必要的字符串操作;
- 一处游戏中的 Log 日志函数代码;