MonoBehavior的调用优化

Posted cartzhang

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MonoBehavior的调用优化相关的知识,希望对你有一定的参考价值。


本文章由cartzhang编写,转载请注明出处。 所有权利保留。
文章链接:http://blog.csdn.net/cartzhang/article/details/70921049
作者:cartzhang

本文同步与游戏蛮牛。

MonoBehavior的调用优化


如果告诉你,Unity由于调用你的MonoBehaviour函数而造成CPU性能大量浪费?真的与你的脚本无关么?若你有成千上百个呢,你应该了解一个新的优化领域。

魔术技巧


MonoBehaviour函数调用缓慢。我们谈论的是像Update(),LateUpdate(),OnRender()等函数。这里所谓的魔术技巧,若你熟悉面向对象语言编程,这个概念就像是使用反射机制(反射让你即使不知道接口也可以调用方法)。反射的调用非常消耗,所以unity尽可能的缓存任何操作,这样所需要的CPU指令集来每帧调用魔术方法就可能很少。但是,依旧很慢,很慢…

为什么这么慢啊?


我不打算讨论具体细节(若真需要了解细节,可以阅读文章后面的链接)(译者注:反射原理),所以只想象Unity试图尽可能的灵活和易用。制造一些容易的东西容易增大CPU功耗,因为引擎不能假设你要做的游戏,它需要做很多检测来保证魔法函数被正确对象以正确顺序调用,且不能崩溃。

可以更快么?


这是我最喜欢的部分了。是的,当然可以!怎么做呢?你需要通过自定义的函数,并负责从一个管理器中调用Update()函数。这样一来,自己负责更新对象。会有多快呢?这个取决有平台。让我们使用Valentin Simonov在Unity官方博客上做的对比:

image

这里你可以看到不同的时间消耗。这个是测试了调用Update 10000次的结果。

写一个管理器


将介绍一个简单例子,一个名为BoxManager的管理器,用来管理被管理的脚本。
管理器有两个职责:

保持管理对象的列表已更新,
当管理器Update()被调用时,调用受管对象类的更新功能。

代码可能如下所示:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class BoxManager : MonoBehaviour
{
    public static BoxManager Instance { get; private set; }

    public List<BoxManaged> _managedBoxes = new List<BoxManaged>();

    void Awake()
    {
        Instance = this;
    }

    void Update()
    {
        // update objects here
    }

    public void Register(BoxManaged box)
    {
        _managedBoxes.Add(box);
    }

    public void Unregister(BoxManaged box)
    {
        _managedBoxes.Remove(box);
    }
}


如你所见,它看起来很简单。在使用Update函数之前,我们看看BoxManaged.cs。

using UnityEngine;

public class BoxManaged : MonoBehaviour {

    private Vector3 _position;

    private Transform _transform;

    void OnEnable()
    {
        BoxManager.Instance.Register(this);
    }

    void OnDisable()
    {
        BoxManager.Instance.Unregister(this);
    }

    public void ManagedUpdate()
    {
        // do what you normally do in Update here
    }
}


当可以使用和被禁用的时候都会自动注册。很公平,ManagedUpdate()函数代替Update()这个魔法函数。我们来实现BoxManager.Update(),这样它就会立刻调用所有的BoxManaged.ManagedUpdate()。

void Update()
{
    for (int i = 0; i < _managedBoxes.Count; ++i)
    {
        _managedBoxes[i].ManagedUpdate();
    }
}


就是这样。真的!现在你可以在ManagedUpdate()中实现你平时在Update()中处理的任何内容。

请注意,我并没有使用foreach来迭代。首先,因为在unity的Mono版本中它会产生少量的GC垃圾。其次,它看起来似乎较慢。(译者注:在Unity5.5 beta后就不是问题了)

我该在意这个么?


这取决于你的游戏类型和目标平台。问下自己——你有很多MonoBehaviour对象调用Update()么?它不一定需要Update(),它可以是每帧调用的任何对象。然后,若针对的是移动平台,绝对值得一试!单机平台?依旧值得考虑,尤其是你有大量的对象。

尽管你的对象数量较少,有时候你可能还是需要一个管理器。在ios上(我不知道是否已经修复)OnRender()函数有问题。在30-40个对象可能会让游戏的性能降低两倍!解决方案?像上面提到的管理器,不是调用Update(),而是OnRender()代码。是的,效果很明显。

请记住,这是您可以使用的许多优化策略之一。 然而,这一个很隐蔽 - 除非你知道,否则你将很难找到这个。 这就是为什么这篇文章产生的原因。

参考

https://blogs.unity3d.com/2015/12/23/1k-update-calls/

原帖地址:http://blog.theknightsofunity.com/monobehavior-calls-optimization/
作者:The Knights of Unity


蛮牛地址:http://manew.com/forum.php?mod=viewthread&tid=103317&page=1&extra=#pid1417941


若有问题,请随时联系!!
感谢浏览,欢迎点赞!

以上是关于MonoBehavior的调用优化的主要内容,如果未能解决你的问题,请参考以下文章

如何优化C ++代码的以下片段 - 卷中的零交叉

Unity编程标准导引-3.2 MonoBehavior 组件父类重构

优化 C# 代码片段、ObservableCollection 和 AddRange

使用 C++ 反转句子中的每个单词需要对我的代码片段进行代码优化

slua中,绑定lua文件到Monobehavior的一种方法

Unity3D脚本中Start和Awake的区别