Unity性能优化整理

C#

浏览数:133

2019-9-1

AD:资源代下载服务

纹理优化

  • Sprite Packer

通过细分导入到Unity的纹理,利用自带的Sprite Packer将时间段内需要同时绘制在屏幕上的纹理合并到一个图集中,可以减少Drawcall的开销

UGUI优化

  • Canvas
  1. 分离Canvas

画布(Canvas)是Unity UI的基本组件。它会生成网格来呈现放置在画布上的UI元素,当UI元素变化时,它会重新生成网格并向GPU发起绘图调用,从而显示出UI。
生成这些网格会消耗大量性能,需要将UI元素收集到批处理中,从而尽可能减少绘图调用。因为批处理的生成过程性能消耗较大,通常只在必要时候才重新生成。问题在于,当画布上有一个或多个元素变化时,必须重新分析整块画布,才能得到绘制元素的最优方法。

每块画布上的元素都与其它画布的元素相隔离,所以我们可以使用工具来切分画布,从而解决Unity UI的批处理问题。

我们也可以通过嵌套画布来解决,这样允许设计师创建大型分层UI,而且不必担心不同内容出现在多个画布上。子画布的内容与父画布和同级画布相互隔离,它们会保持自带几何体,执行自己的批处理。

当使用子画布分离画布时,尝试根据画布更新时间来分组。例如:分离动态元素和静态元素。

  1. 隐藏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模型也可以),就会自动被合并。可以自由移动旋转。但有以下使用要求:

  1. 模型文件共计点数不超过900。(重复使用同一个Mesh不计)
  2. 单个物体可以不超过300点,Shader可以有法线UV。但如果Shader使用了 UV0 UV1两套UV,或者Tangent切线的话,单个物体只能不超过180点
  3. 游戏对象使用相同模型和材质时,只有相同缩放(即xyz等比缩放,浮点尾数可以有细微差)的会被合并。
  4. 场景烘焙:烘焙后同材质将不会被烘焙。lightmap 有隐藏的材质参数:offset/scale, 所以使用lightmap的物体不会被合并
  5. Shader不能使用多Pass:多Pass的Shader会破坏Dynamic Batching

1.2 Static Batching
运行游戏后将一组游戏对象的多个模型会被动态合并为1个。这组游戏对象所有使用同一材质的在一个Drawcall来完成。这些游戏对象运行后无法移动缩放旋转。但是Drawcall一定是最大化合并的,并且不受动态合并的诸多限制

注意
即使物体都使用了同样的模型,在batch后每一个物体都会创建一份模型对应的geometry,在新的Combined Mesh里。所以过多的batch会增加内存占用。例如场景里的树群就不适合Static Batch,而适合动态合并。

实现方法:

  1. MeshRenderer勾选Batching Static: 勾了即可
  2. 代码中使用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优化效率较低)

作者:Pieces_Jing