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官方博客上做的对比:

这里你可以看到不同的时间消耗。这个是测试了调用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



若有问题,请随时联系!!

感谢浏览,欢迎点赞!

时间: 2024-10-23 11:12:40

MonoBehavior的调用优化的相关文章

函数的调用优化

函数的调用优化 在类中有四大成员函数,构造函数,拷贝构造函数,赋值函数和析构函数,在类外进行实例化时,若调用函数的方法不当则会产生时间和空间的浪费. 以下,将用几个小栗子来说明函数的调用优化的常见办法 类: class Test { public: Test(int d = 0) : _data(d) { cout << "Create Test Object:"<<this << endl; } Test(const Test&x) :_d

尾调用优化和尾递归改写

1 尾调用 尾调用就是指某个函数的最后一步是调用另一个函数. # 是尾调用 def f(x): return g(x) # 不是尾调用,因为调用函数后还要执行加法,加法才是最后一步操作 def f(x): return 1+g(x) 2 尾调用优化 函数调用有一个调用栈,栈内保存了这个函数内部的变量信息.函数掉用就是切换不同的调用帧,从而保证每个函数有独立的运行环境.因为尾调用是函数的最后一步操作,所以在进入被尾调用函数之前并不需要保留外层函数的运行时环境,因为调用位置.内部变量等信息都不会再用

ListView 关于减少耗时方法调用优化

private class GirdTemp { TextView nameTV; ImageView mGPUImageView; Bitmap image; } =========================================================================================== final GirdTemp temp; if (convertView == null) { convertView = mInflater.inf

尾调用优化

参考:http://www.ruanyifeng.com/blog/2015/04/tail-call.html 感谢阮老师. 尾调用的概念非常简单,一句话就能说清楚,就是指某个函数的最后一步是调用另一个函数. //正确的尾调用 function f(x) { if (x > 0) { return m(x) } return n(x); } function f(x){ return g(x); } //非尾调用 // 情况一 function f(x){ let y = g(x); retu

C#.NET 大型企业信息化系统集成快速开发平台 4.2 版本 - 外部服务调用、内部服务调用优化,面向服务化的

现在的信息系统越来越复杂,越来越庞大,不仅需要内部是一个整体,而且还需要提供很多对外的服务调用. 1:别人如何调用最方便?用不同的开发语言调用.2:服务的返回状态是什么样子的?有利于排查问题.3:服务的安全性.可过渡升级性.性能效率要有保障.4:服务也需要有完整的调用日志记录等. 下面是一个服务调用有效性判断的函数代码,供大家参考. 1 //----------------------------------------------------------------- 2 // All Rig

前段性能优化之js,css调用优化

规则1:减少HTTP请求     把多个JS请求合并为一个JS请求,把多个CSS请求合并为一个CSS请求.从而减少从客户端向服务器端的请求数.     规则3:添加Expires头     用http请求的查看工具,我这里用的是firebug查看http请求,可以看到响应头中有Expires头,unicorn的设置的默认过期时间是30分钟.即是如果当前网页未过期,浏览器不会发请求,直接查找本地页面缓存.加速前端响应速度,减少服务器端压力.     规则4:压缩组件     查看http请求头参数

C++基本函数的调用优化(构造、拷贝构造、赋值)

合理的函数可提升时间和空间的利用率 //Test1.h #include<iostream> using namespace std; struct ST { private: int a; short b; public: ST(int a=0, short b=0):a(a),b(b) { this->a = a; this->b = b; cout<<"Object was Built. "<<this<<endl; }

CSDN日报20170428 ——《你的开发为何如此低效?》

[程序人生]你的开发为何如此低效? 作者:Stay 今天的主题是撇开技术和大家聊聊高效开发的一些套路与实践. 点击阅读全文 [深度学习]用 TensorFlow 创建自己的 Speech Recognizer 作者:Alice熹爱学习 语音识别无处不在,siri,google,讯飞输入法,讯飞语记,智能家居,车,etc. 每天都在用的,很好奇它是怎么实现的,今天来看看这么便利的东东到底是什么样子呢. 点击阅读全文 [Web 前端]2017 年前端面试题最新汇总 作者:郭小北 由于过了五一就是面试

栈溢出 缓冲区溢出的一种 缓冲区以外的存储单元被改写 优化方法之一:尾调用

个人:尾调用时函数式编程的一个重要概念, 栈溢出: 函数调用会在内存形成一个"调用记录",又称"调用帧"(call frame),保存调用位置和内部变量等信息.如果在函数A的内部调用函数B,那么在A的调用帧上方,还会形成一个B的调用帧.等到B运行结束,将结果返回到A,B的调用帧才会消失.如果函数B内部还调用函数C,那就还有一个C的调用帧,以此类推.所有的调用帧,就形成一个"调用栈"(call stack).递归调用非常耗内存. 尾调用: 就是指某