LoginSignup
1
0

More than 3 years have passed since last update.

【Unity学习笔记】脚本优化小技巧分享

Posted at

前言

Unity游戏程序的优化一直以来都是令人棘手的问题。与排查Bug的过程不同,造成游戏帧率低下卡顿的问题可能分散在程序的各个角落,而每一个小问题单独看却又不够明显难以察觉。因此比起在开发后期再进行优化而困难重重,应该从一开始就注重效率来开发。
Unity优化在于不少方面,本文介绍几个在写脚本时容易被忽视的优化小技巧。

1. 使用transform应该先缓存

一个继承自MonoBehavior的脚本可以直接通过transform属性访问到自身物体的Transform组件,然而这个行为的效率其实不高。如果在一帧内需要大量对Transform进行操作的话,则应该先建立一个Transform的缓存,对缓存进行操作而不是每次都访问transform属性。
我们进行直接访问和通过缓存访问的对比试验。

Test.cs
private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("ChangePosition");
    // 为使实验结果明显,反复执行1000000次操作
    for (int i = 0; i < 1000000; i++)
    {
        transform.position = new Vector3(i, i, i);            
    }
    Profiler.EndSample();
}

コメント 2020-01-03 192333.png
通过直接访问transform来改变position1000000次,耗时91.52ms。

Test.cs
private Transform myTransform;

private void Start()
{
    myTransform = transform;
}

private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("ChangePosition");
    // 为使实验结果明显,反复执行1000000次操作
    for (int i = 0; i < 1000000; i++)
    {
        myTransform.position = new Vector3(i, i, i);            
    }
    Profiler.EndSample();
}

コメント 2020-01-03 193055.png
首先建立一个名为myTransform的私有变量,在start中访问transform一次赋值给myTransform。通过访问myTransform来改变position1000000次,耗时55.12ms。
结论是,通过缓存transform的方式可以比直接访问transform的运行效率提高了约40%。这个优化技巧在场景中有大量Transform操作时的效果十分明显。

2. 不要频繁访问Camera.main

使用Camera.main虽然可以很方便的访问到主相机,但其内部本质是执行了FindGameObjectWithTag("MainCamera"),效率是非常低下的。

Test.cs
private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("Camera Operation");
    // 为使实验结果明显,反复执行100000次操作
    for (int i = 0; i < 100000; i++)
    {
        Camera.main.fieldOfView = 60f;            
    }
    Profiler.EndSample();
}

コメント 2020-01-03 202413.png

通过执行Camera.main获取相机,设置FOV100000次,耗时33.99ms。

Test.cs
private Camera myCamera;

private void Start()
{
    myCamera = Camera.main;
}

private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("Camera Operation");
    // 为使实验结果明显,反复执行100000次操作
    for (int i = 0; i < 100000; i++)
    {
        myCamera.fieldOfView = 60f;
    }
    Profiler.EndSample();
}

コメント 2020-01-03 202632.png
创建一个myCamera的变量来缓存Camera.main,通过访问myCamera来设置FOV100000次,耗时仅5.61ms。
结论是,创建一个变量把Camera.main缓存下来,通过访问缓存来相机,效率会提高约83%。

3. 创建向量时,尽量使用new Vector(),而不要使用Vector.zero等属性

Unity的向量类中提供的方便创建向量的属性,例如Vector3.zero,Vector.up等属性的效率其实并不高。

Test.cs
private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("New Vector");
    // 为使实验结果明显,反复执行1000000次操作
    for (int i = 0; i < 1000000; i++)
    {
        var vector = Vector3.zero;   
    }
    Profiler.EndSample();
}

コメント 2020-01-03 205855.png
使用Vector3.zero来创建向量1000000次,耗时11.70ms

Test.cs
private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("New Vector");
    // 为使实验结果明显,反复执行1000000次操作
    for (int i = 0; i < 1000000; i++)
    {
        var vector = new Vector3();   
    }
    Profiler.EndSample();
}

コメント 2020-01-03 210148.png
直接使用new Vector3()来创建向量1000000次,耗时仅3.55ms。
结论是使用new Vector()而不是Vector.zero等属性来创建向量,效率提升约70%。

4. position和localPosition等同时,尽可能使用localPosition(rotation同理)

为了方便管理Hierarchy,我们经常会创建position和rotation都等于0的父节点,将需要管理的物体放进去作为子物体。这些子物体的localPosition和position在值上是等同的,但在访问position时实际上是执行了将localPosition转换到世界坐标系的操作,效率上比直接访问localPosition低。
コメント 2020-01-03 220436.png
先创建一个5层结构,在每一层的物体上执行测试脚本。

Test.cs
private Transform myTransform;
private void Start()
{
    myTransform = transform;
}
private void Update()
{
    // 让Profiler监视这一段操作
    Profiler.BeginSample("SetPosition");
    // 为使实验结果明显,反复执行1000000次操作
    for (int i = 0; i < 100000; i++)
    {
        myTransform.position = new Vector3(i, i, i);
    }
    Profiler.EndSample();

    // 让Profiler监视这一段操作
    Profiler.BeginSample("SetLocalPosition");
    // 为使实验结果明显,反复执行1000000次操作
    for (int i = 0; i < 100000; i++)
    {
        myTransform.localPosition = new Vector3(i, i, i);
    }
    Profiler.EndSample();
}

根节点的执行情况
コメント 2020-01-03 221248.png
第一层子节点的执行情况
コメント 2020-01-03 221322.png
第二层子节点的执行情况
コメント 2020-01-03 221402.png
第三层子节点的执行情况
コメント 2020-01-03 221435.png
第四层子节点的执行情况
コメント 2020-01-03 222740.png
我们可以看到,层级越深的物体访问position时坐标系转换的耗时就越长。

总结

  1. transform先缓存再使用。
  2. Camera.main先缓存再使用。
  3. 创建向量使用new Vector(),而不是Vector.zero等属性。
  4. 尽可能使用localPosition而不是position,rotation同理。
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0