纹理优化
- Sprite Packer
通过细分导入到Unity的纹理,利用自带的Sprite Packer将时间段内需要同时绘制在屏幕上的纹理合并到一个图集中,可以减少Drawcall的开销
UGUI优化
- Canvas
- 分离Canvas
画布(Canvas)是Unity UI的基本组件。它会生成网格来呈现放置在画布上的UI元素,当UI元素变化时,它会重新生成网格并向GPU发起绘图调用,从而显示出UI。
生成这些网格会消耗大量性能,需要将UI元素收集到批处理中,从而尽可能减少绘图调用。因为批处理的生成过程性能消耗较大,通常只在必要时候才重新生成。问题在于,当画布上有一个或多个元素变化时,必须重新分析整块画布,才能得到绘制元素的最优方法。
每块画布上的元素都与其它画布的元素相隔离,所以我们可以使用工具来切分画布,从而解决Unity UI的批处理问题。
我们也可以通过嵌套画布来解决,这样允许设计师创建大型分层UI,而且不必担心不同内容出现在多个画布上。子画布的内容与父画布和同级画布相互隔离,它们会保持自带几何体,执行自己的批处理。
当使用子画布分离画布时,尝试根据画布更新时间来分组。例如:分离动态元素和静态元素。
- 隐藏Canvas
有时需要隐藏UI元素和画布,要怎样高效完成该任务呢?
禁用Canvas组件会阻止画布向GPU发起绘图调用,所以该画布不再可见。然而,此时该画布不会丢弃它的顶点缓冲区,它会保留所有网格和顶点,当重新启用时不会触发重构过程,它只会重新绘制画布内容。
此外,禁用Canvas组件不会触发Canvas层级上性能消耗较大的OnDisable/OnEnable回调。禁用子组件时要小心,注意它是否运行性能消耗较大的每帧代码。
- Raycast Target
该组件会获取特定画布上输入信息相关的UI元素集,然后执行交点测试,它会针对Graphic Raycaster的画布上每个交互式UI元素的RectTransform,检查输入事件发生的位置。
关闭静态或非交互式元素的Raycast Target会直接减少Graphic Raycaster每帧进行的交点测试次数。
- Camera
如果将世界空间画布的Event Camera字段留空,这不意味着该画布不会接收事件。它会使用游戏的主摄像机。为了确定哪个摄像机是主摄像机,该画布会访问Camera.main属性。
根据Unity所使用的代码路径,每帧中每有一个Graphic Raycaster和世界空间画布,该画布会访问7到10次Camera.main。每次访问Camera.main都会调用Object.FindObjectWithTag。这个做法在运行时并不合适。
缓存摄像机的引用,然后创建系统来跟踪主摄像机。如果使用世界空间画布,要指定Event Camera,不要将该属性留空。如果需要修改Event Camera,编写代码来更新Event Camera属性。
- Layout System
布局系统是一组连续的布局分组(Layout Group),它们在布局元素(Layout Element)之上。布局元素不只是名为Layout Element的组件,它们还包括UI图像、文字和Scroll Rect组件,而且Scroll Rect同时也是布局分组。
避免使用布局分组。使用锚点进行比例布局。在拥有动态元素数量的活跃UI上,考虑编写代码来计算布局,仅在需要时运行该代码,而不是每次发生改变的时候。
- Animator
Animator每帧都会改变元素,即使动画中的数值没有变化。Animator没有空指令检查。
只在频繁变化的动态元素上加入Animator。对于很少变化的元素,或是仅响应事件时才变化的元素,请自行编写代码或补间系统,推荐DoTween。
Drawcall优化
Drawcall优化合并,即Drawcall Batching. 通过减少Drawcall数 和对显卡性能的消耗来提高性能。
在Unity中,Batches值就表示了Drawcall的次数
1.Mesh Renderer
1.1 Dynamic Batching
不需要任何操作,只要共享材质(即使是不同的Mesh模型也可以),就会自动被合并。可以自由移动旋转。但有以下使用要求:
- 模型文件共计点数不超过900。(重复使用同一个Mesh不计)
- 单个物体可以不超过300点,Shader可以有法线UV。但如果Shader使用了 UV0 UV1两套UV,或者Tangent切线的话,单个物体只能不超过180点
- 游戏对象使用相同模型和材质时,只有相同缩放(即xyz等比缩放,浮点尾数可以有细微差)的会被合并。
- 场景烘焙:烘焙后同材质将不会被烘焙。lightmap 有隐藏的材质参数:offset/scale, 所以使用lightmap的物体不会被合并
- Shader不能使用多Pass:多Pass的Shader会破坏Dynamic Batching
1.2 Static Batching
运行游戏后将一组游戏对象的多个模型会被动态合并为1个。这组游戏对象所有使用同一材质的在一个Drawcall来完成。这些游戏对象运行后无法移动缩放旋转。但是Drawcall一定是最大化合并的,并且不受动态合并的诸多限制
注意:
即使物体都使用了同样的模型,在batch后每一个物体都会创建一份模型对应的geometry,在新的Combined Mesh里。所以过多的batch会增加内存占用。例如场景里的树群就不适合Static Batch,而适合动态合并。
实现方法:
- MeshRenderer勾选Batching Static: 勾了即可
- 代码中使用UnityEngine.StaticBatchingUtility实现(可以在任何平台调用):
a. 将所有要合并的静态物体(不须勾Batching Static)放入统一一个root
b. StaticBatchingUtility.Combine(root); 之后就合并好了!
区别:
. 勾选Batching Static: 完全自动合并,在MeshFilter里显示的是 Combined Mesh(root:scene)。合并后不能移动
. StaticBatchingUtility: 合并到一个游戏对象下。合并后可以移动父节点游戏对象
2.Skinned Mesh Renderer
没有任何batching, 故不是人物的话尽量少用。
注意:
(1)修改Renderer.material将会创建一份新的material,应使用Renderer.sharedMaterial来保持材质公用。
(2)可以使用Mesh.Optimize() 优化模型。导入模型不需要调用此接口,在导入模型至Unity3d时已自动处理。
代码创建的模型有可能需要调用此接口来优化。
3.合并要求对比
要求 | 动态 | 静态 |
---|---|---|
只能是MeshRenderer不能是SkinnedMeshRenderer | 要求 | 要求 |
Shader必须是单Pass | 要求 | 不要求 |
不能被场景烘焙(不能标注为Light map static) | 要求 | 不要求 |
网格模型文件总点数要求 | 要求 | 不要求 |
网格模型单个文件点数要求 | 要求 | 不要求 |
限制缩放 | 要求 | 不要求 |
限制位移旋转 | 不要求 | 要求 |
4.总结
合并方式 | 优点 | 缺点 |
---|---|---|
动态合并 | 内存消耗小 可以自由位移旋转 |
要求众多 |
标记Light map static合并 | 最方便快捷,要求最少 | 完全不能移动 内存有消耗 只能处理在Scene中默认放置的物体 |
代码StaticBatchingUtility合并 | 方便快捷,要求少 可整体移动 可处理代码动态加载的场景或物体 |
内存有消耗 |
代码合并是要求最少的,并可以整体移动。而动态合并可以移动旋转,并做Animation动画。故推荐StaticBatchingUtility + 动态合并。
5.场景制作建议
美术人员在制作某场景时,尽量:
-
静态物体:使用静态合并
条件
1 用Mesh Renderer,也就是不绑定骨骼
2 不是Wrap循环UV的贴图,尽量合并到一张大图集
方法
标注Batching Static即可 -
动态物体:使用动态合并,动画可以用Unity的动画去做
条件
1 无骨骼
2 不参与场景烘焙
3 相同物体相同缩放
4 模型点数小于300或180
5 模型文件总点数小于900
6 Shader单Pass
方法
满足条件Unity自动优化
(不满足动态合并要求的。场景动画可以用骨骼动画去做。但应尽量避免或者少的使用这种方式。因为没有Drawcall优化效率较低)