孙广东 2016.5.23
Unity提供了用于创建 UI 的画布。画布上有渲染模式如下 ︰
让我们了解哪种模式是更好的,让我们以一个非常简单的示例测试这三项。
1 屏幕空间相机
让我们创建一个Unity项目︰
脚本 ︰ MoveCamera.cs
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)); } }
正如你从图片中可以看到,有很多的画布的调用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对象。