Unity的优化系列(四):Canvas Overlay代替Screen Space Camera
Unity提供了用于创建 UI 的画布。画布上有渲染模式如下 ︰
- 屏幕空间相机 Screen Space Camera
- 屏幕空间覆盖 Screen Space Overlay
- 世界空间 World Space
让我们了解哪种模式是更好的,让我们以一个非常简单的示例测试这三项。
1、屏幕空间相机
让我们创建一个Unity项目︰
脚本 ︰ MoveCamera.cs
- 在你的场景建立一个Canvas。
- 设置渲染模式为: Screen Space Camera
- 将Main Camera,拖到render camera 选项(其他相机也可以)。
- 用sprite或图像中作为一个背景。
- 才Canvas内创建尽可能多的panels 和 UI的Texts 和Images
- 添加到Camera上的脚本。
using UnityEngine;
using System.Collections;
public class MoveCamera : MonoBehaviour
{
private float velocity = 0.0f;
private float smoothTime = 0.3f;
private bool moveCamera = false;
public Vector3 initialPosition;
public Vector3 targetPosition;
public float lerpSpeed;
public float initialZ;
public float targetZ;
public Camera cam;
void Update ()
{
if (Input.GetMouseButtonDown (0)) {
initialPosition = transform.position;
targetPosition = new Vector3 (transform.position.x Random.Range (-5, 5), transform.position.y Random.Range (-5, 5), transform.position.z);
initialZ = transform.eulerAngles.z;
targetZ = initialZ Random.Range (-50, 50);
moveCamera = true;
lerpSpeed = 0;
}
if (moveCamera) {
CameraMovementMethod ();
}
}
private void CameraMovementMethod ()
{
lerpSpeed = Mathf.SmoothDamp (lerpSpeed, 1.0f, ref velocity, smoothTime);
cam.transform.position = Vector3.Lerp (initialPosition, targetPosition, lerpSpeed);
cam.transform.eulerAngles = new Vector3 (0, 0, Mathf.LerpAngle (initialZ, targetZ, lerpSpeed));
}
}
- Build部署到 Android 手机、 创建debugging build ,允许自动连接到profiler
- 在手机上运行该项目的,然后在处理随便点击屏幕
- 在Unity编辑器中profiler上搜索 "canvas",你会看到。:

正如你从图片中可以看到,有很多的画布的调用Calls,特别注意到CanvasRender.OnTransformChanged
每当相机移动,大约每个帧上有 50 次调用。
注意 调用次数是依赖于Canvas上的 使用的UI 元素个数。
我们可以更好地理解这与下面 gif:

你可以看到上面,在游戏中移动Canvas相关的摄像机,因此每个UI 元素在画布上不得不由Unity引擎重新定位。因此越多的UI元素,就需要更多的处理。
那么,解决方案是什么?
2、屏幕空间覆盖
在刚刚的测试项目中我们把 Canvas的渲染模式改为 Screen Space Overlay ,并重复之前的步骤,同样想在观察Profiler。
大约我们优化它约 90%
正如你可以看到以上,UI 画布保持原样在Unity空间,摄像机运动的不影响 UI Canvas根本。画布保持静态。
因此,不需要任何重新定位或所需的处理。
实现了高的优化。
3、世界空间
在世界空间模式,画布渲染手动更改为 World Space,测试结果和第一种一样。
结论 ︰
考虑screen space camera 类型的呈现进行大量的调用,所以非常推荐使用Overlay 绘制画布,使你的游戏在移动设备上更顺畅。(当然了这种只是限于纯UI),但是另外两种模式是有其特殊用处的!
ForEach 循环优化

这是个老生常谈的话题, 主要还是产生GC的问题:
public class ForEachLoopTest : MonoBehaviour
{
#region PUBLIC_DECLARATIONS
public List<GameObject> emptyGameObjects;
#endregion
#region UNITY_CALLBACKS
void Update()
{
if (Input.GetKey(KeyCode.Space))
{
UpdateTextValue();
}
}
#endregion
#region PUBLIC_METHODS
public void UpdateTextValue()
{
foreach (var item in emptyGameObjects)
{
// PROCESS ITEMS IN LIST
}
// for (int i = 0; i < emptyGameObjects .Count; i )
// {
// PROCESS ITEMS IN LIST
// }
}
#endregion
}

按下空格键 ,当是10~30大小的循环时 会看到如上图的 效果: 每帧产生 40B 的GC
foreach (SomeType sin someList)
s.DoSomething();
现在编译, 编译器预处理的代码:
using (SomeType.Enumerator enumerator = this.someList.GetEnumerator())
{
while (enumerator.MoveNext())
{
SomeType s = (SomeType)enumerator.Current;
s.DoSomething();
}
}
在每次迭代时,会创建一个 enumerator对象。
