(转)unity3d中脚本生命周期(MonoBehaviour lifecycle)
自:http://blog.csdn.net/qitian67/article/details/18516503
最近在做一个小示例,发现类继承于MonoBehaviour的类,有很多个方法,于是乎必然要问出一个问题:这么多个方法,执行先后顺序是如何的呢?内部是如何进行管理的呢?于是在网上找了许多资料,发现了Richard Fine在2012年就已经发布了一篇文章,而且讲得算是相当深入,并且很有道理的,这里加上我的一些尝试与思考,分享给大家。
先贴上图,大家有个直观认识:
接下来,做出一下讲解:最先执行的方法是Awake,这是生命周期的开始,用于进行激活时的初始化代码,一般可以在这个地方将当前脚本禁用:this.enable=false,如果这样做了,则会直接跳转到OnDisable方法执行一次,然后其它的任何方法,都将不再被执行。
如果当前脚本处于可用状态,则正常的执行顺序是继续向下执行OnEnable,当然我们可以在另外一个脚本中实现这个脚本组件的启动:this.enab=true;
再向下执行,会进行一个判断,如果Start方法还没有被执行,则会被执行一次,如果已经被执行了,则不会再被执行。这是个什么意思呢?我们可以在某个脚本中将组件禁用this.enable=false,再启用时会转到OnEnable处执行,这时继续向下走,发现Start执行过了,将不再被执行。比如说:第一次启用时,将怪物的初始位置定在了(0,0,0)点,然后怪物可能会发生了位置的变换,后来被禁用了,再次启用时,不会让怪物又回到初始的(0,0,0)位置。
继续向后执行,就是Update了,然后是FixUpdate,再然后是LateUpdate,如果后面写了Reset,则会又回到Update,在这4个事件间可以进行循环流动。
再向后执行,就进入了渲染模块(Rendering),非常重要的一个方法就是OnGUI,用于绘制图形界面。当然,如果你使用了NGUI,这个生命周期的事情你就不用考虑了。
再向后,就是卸载模块(TearDown),这里主要有两个方法OnDisable与OnDestroy。当被禁用(enable=false)时,会执行OnDisable方法,但是这个时候,脚本并不会被销毁,在这个状态下,可以重新回到OnEnable状态(enable=true)。当手动销毁或附属的游戏对象被销毁时,OnDestroy才会被执行,当前脚本的生命周期结束。
特别要强调的是:这里虽然可以使用C#来写代码,但是这个类构造对象的生命周期,与MonoBehaviour的生命周期,是完全不同的。
可以通过如下示例:对脚本进行验证(两个脚本添加到同一个游戏对象上):
脚本1Monster1:
void OnBecameInvisible()
    {
        Debug.Log("invisible");
        MonsterController mc1 = this.gameObject.GetComponent<MonsterController>();
        mc1.enabled = false;
    }
void OnBecameVisible()
    {
        Debug.Log("visible");
        MonsterController mc1 = this.gameObject.GetComponent<MonsterController>();
        mc1.enabled = true;
    }
脚本2MonsterController:
void Awake()
    {
        Debug.Log("awake");
        this.enabled = false;
    }
void OnEnable()
    {
        Debug.Log("enable");
        this.enabled = true;
    }
void Start()
    {
        Debug.Log("start");
        //this.gameObject.SetActive(false);
    }
void Update()
    {
        Debug.Log("update");
    }
void OnGUI()
    {
        Debug.Log("gui");
    }
    void OnDisable()
    {
        Debug.Log("disable");
    }
void OnDestroy()
    {
        Debug.Log("destroy");
    }
如果您觉得有不对之处,欢迎指正,为了技术进步,我们一起努力。
Richard Fine的文章地址:点击阅读
补充官网的信息:
事件函数的执行顺序
Unity 脚本中有许多按预设顺序以脚本身份运行的事件函数。其执行顺序如下:
加载第一个场景
启动场景时调用这些函数(为场景中的每个对象调用一次)。
- Awake: 始终在调用任何 Start 函数之前和实例化预设之后调用此函数。(如果游戏对象 (GameObject) 在启动期间处于非活动状态,则直到其处于活动状态时或调用添加至其本身的任何脚本中的函数时,再调用 Awake 函数。)红字部分是图中没有提到的,特别需要注意!!!绿色的部分通关测试是不正确的
 - OnEnable: (仅当对象 (Object) 处于活动状态时调用此函数):程序会在启用该对象后立即调用此函数。上述现象会在创建了实例化的 MonoBehaviour 后发生,例如加载了级别或对含脚本组件的游戏对象 (GameObject) 进行实例化后。
 
第一帧更新之前
- Start: 只要启用脚本实例,即可在更新第一帧之前调用 Start 函数。
 
插值帧
- OnApplicationPause: 程序检测到暂停时,会在帧的结尾处调用此函数,这在常规帧更新期间很有效。调用 OnApplicationPause后,程序将运行另一帧来显示提示暂停状态的图形。
 
更新顺序
有几个不同的事件有助于追踪游戏逻辑与交互、动画、相机位置等内容。常用的方法是运行Update() 函数中的大部分任务,但也可使用其他函数。.
- FixedUpdate: 通常,FixedUpdate() 的调用频率高于 Update()。如果帧速率较低,则可在一帧中多次调用此函数,如果帧速率较高,则可能完全无法在帧间调用此函数。程序调用 FixedUpdate() 后将立即执行所有物理计算和更新。在 FixedUpdate() 中应用移动计算时,无需将您的值与 Time.deltaTime 相乘。这是因为,程序是在可靠的计时器上调用FixedUpdate(),与帧速率无关。
 - Update: 在每帧上调用一次 Update() 函数。它是用于帧更新的主要 workhorse 函数。
 - LateUpdate: 完成 Update() 调用后,在每帧上调用 LateUpdate()。Update() 中执行的所有计算都将在 LateUpdate() 开始之前结束。LateUpdate() 的常规使用记录由第三人称相机跟踪。如果在 Update() 中移动和旋转角色,则可在 LateUpdate() 中计算所有相机移动和旋转。这将确保在相机跟踪其位置之前完整移动该角色。
 
渲染
- OnPreCull: 在相机剔除场景之前调用此函数。相机可见的对象取决于剔除。OnPreCull 函数调用发生在剔除之前。
 - OnBecameVisible/OnBecameInvisible: 在对象对于相机可见/不可见时调用此函数。
 - OnWillRenderObject: 如果对象可见,则为每个相机调用一次此函数。
 - OnPreRender: 在相机开始渲染场景之前调用此函数。
 - OnRenderObject: 在完成所有常规场景渲染后调用此函数。此时,可使用 GL 类或 Graphics.DrawMeshNow 绘制自定义几何图形。
 - OnPostRender: 在相机完成场景渲染后调用此函数。
 - OnRenderImage(仅限专业版): 在完成场景渲染后调用此函数,以便对屏幕图像进行后处理。
 - OnGUI: 在每帧上多次调用此函数,以响应 GUI 事件。程序首先将处理 Layout 和 Repaint 事件,然后再处理每个输入事件的 Layout 和 keyboard/鼠标事件。
 - OnDrawGizmos 用于在场景视图中绘制小图示 (Gizmos),以实现可视化目的。
 
协同程序
正常的协同程序更新是在返回 Update 函数之后运行。协同程序是可自行停止运行 (yield),直到给定的 YieldInstruction 结束再继续运行的函数。 协同程序 (Coroutines) 的不同用途:
- yield; 在下一帧上调用所有 Update 函数后,协同程序将继续运行。
 - yield WaitForSeconds(2); 在指定的时间延迟之后,为此帧调用所有 Update 函数之后继续运行
 - yield WaitForFixedUpdate(); 在所有脚本上调用所有 FixedUpdate 后继续运行
 - yield WWW 完成 WWW 下载后继续运行。
 - yield StartCoroutine(MyFunc); 连接协同程序,并等待 MyFunc coroutine 首先结束。
 
对象 (Object) 被销毁时
- OnDestroy: 完成所有帧更新后,在当前对象的最后一帧上调用此函数(可能为响应 Object.Destroy 或在关闭场景时销毁此对象)。
 
退出时
程序将在场景的所有活动对象上调用这些函数:
- OnApplicationQuit: 在退出应用程序之前,程序会在所有游戏对象上调用此函数。在编辑器中,用户停止播放模式时,程序将调用此函数。在网页播放器中,此函数会在网页视图关闭时调用。
 - OnDisable: 此函数会在行为被禁用或不活动时调用。
 
综上所述,任何给定脚本的执行顺序为:
- 调用所有 Awake
 - 调用所有 Start
 - 同时进行(朝向可变增量时间)
- 所有 FixedUpdate 函数
 - 物理模拟
 - OnEnter/Exit/Stay 触发函数
 - OnEnter/Exit/Stay 碰撞函数
 
 - 刚体插值应用 transform.position 和旋转
 - OnMouseDown/OnMouseUp 等事件
 - 所有 Update 函数
 - 将动画优化为高级、混合并应用动画,以进行变换
 - 所有 LateUpdate 函数
 - 渲染
 
提示
- 协同程序在所有 Update 函数结束后运行。
 
(转)unity3d中脚本生命周期(MonoBehaviour lifecycle)的更多相关文章
- unity3d中脚本生命周期(MonoBehaviour lifecycle)
		
最近在做一个小示例,发现类继承于MonoBehaviour的类,有很多个方法,于是乎必然要问出一个问题:这么多个方法,执行先后顺序是如何的呢?内部是如何进行管理的呢?于是在网上找了许多资料,发现了Ri ...
 - Unity3D热更新之LuaFramework篇[06]--Lua中是怎么实现脚本生命周期的
		
前言 用c#开发的时候,新建的脚本都默认继承自Monobehaviour, 因此脚本才有了自己的生命周期函数,如Awake,Start, Update, OnDestroy等. 在相应的方法中实现游戏 ...
 - Unity脚本生命周期
		
前言 说到生命周期,影响最深刻的是,在接触Java的JSF组件时,JSF组件的五大生命周期,全要默写出来,嘿嘿…… 总结这两天在写小怪和掉落的糖葫芦时,老是遇到GameObject未销毁,一直存在场景 ...
 - 【转】Unity3D中脚本的执行顺序和编译顺序
		
支持原文,原文请戳: Unity3D中脚本的执行顺序和编译顺序 在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行.与脚本有关的也就是编译和执行啦 ...
 - Unity学习疑问记录之脚本生命周期
		
总的来说unity的脚本生命周期分几个部分:编辑→初始化→游戏逻辑→渲染→GUI→Teardown首先是Reset,顾名思义:重置.在什么情况下调用呢?1.用户第一次添加组件时.2用户点击见组件面板上 ...
 - Unity脚本生命周期与执行顺序
		
文章目录 脚本生命周期 MonoBehavior生命周期图 脚本执行顺序 自定义执行顺序 在Unity中,脚本可以理解为附加在游戏对象上的用于定义游戏对象行为的指令代码.必须绑定在游戏对象上才能开始它 ...
 - Unity基础-脚本生命周期
		
理解Unity脚本的生命周期对游戏开发很重要,这篇文章对生命周期做一个记录和总结.Unity的脚本生命周期(消息),也就是在脚本运行时,自动并且按顺序执行的一系列函数.在unity官网中有对生命周期详 ...
 - Android 进程生命周期 Process Lifecycle
		
Android 进程生命周期 Process Lifecycle 进程的生命周期 Android系统会尽力保持应用的进程,但是有时为了给新的进程和更重要的进程回收一些内存空间,它会移除一些旧的进程. ...
 - 《React Native 精解与实战》书籍连载「React Native 中的生命周期」
		
此文是我的出版书籍<React Native 精解与实战>连载分享,此书由机械工业出版社出版,书中详解了 React Native 框架底层原理.React Native 组件布局.组件与 ...
 
随机推荐
- django中日志配置
			
# ======日志配置====== # 错误优先级:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL # Djang ...
 - luoguP2303 [SDOI2012]Longge的问题 化式子
			
求\(\sum \limits_{i = 1}^n gcd(i, n)\) \(\sum \limits_{i = 1}^n gcd(i, n)\) \(=\sum \limits_{i = 1}^n ...
 - OpenGL ES 3.0 帧缓冲区对象基础知识
			
最近在帧缓冲区对象这里卡了一下,不过前面已经了解了相关的OpenGL ES的知识,现在再去了解就感觉轻松多了.现在就进行总结. 基础知识 我们知道,在应用程序调用任何的OpenGL ES命令之前,需要 ...
 - wxwidget wxpython 可视化开发工具
			
wxwidget官方建议的工具集合:http://wiki.wxwidgets.org/Tools 支持wxpython可视化开发工具 wxFormBuilder wxGlade wxDesigner ...
 - Shell脚本里的双冒号是什么意思
			
这个是代码开发风格,其实也就是一个函数名,相当于下划线分割,但改读成包名之后就意义不一样.这个是根据Google的Shell开发规范进行定义的. 参考: https://google.github.i ...
 - 通过Roslyn构建自己的C#脚本(更新版)
			
之前写过文章介绍过如何通过Roslyn构建自己的C#脚本,但那篇文章是参考自Roslyn CTP版的,记得本来想等到Roslyn正式版出来重新更新一下文档的,不过记得后来Roslyn是跳票了的,Scr ...
 - 电感式DC/DC变换器工作原理
			
http://www.amobbs.com/thread-3293203-1-1.html 首先必须要了解电感的一些特性:电磁转换与磁储能.其它所有参数都是由这两个特性引出来的. 电感回路通电瞬间 断 ...
 - c++中两个类相互包含引用的相关问题
			
在构造自己的类时,可能会遇到两个类相互引用的问题. 例如: class A { int i; B b; }; class B { int i; A a; }; 在这种情况下,这样就会出现一个死循环a. ...
 - 禁用IE缓存
			
HTTP消息报头包括普通报头.请求报头.响应报头.实体报头. 普通报头中的Cache-Control用于指定缓存指令,缓存指令是单向的(响应中出现的缓存指令在请求中未必会出现),且是独立的(一个消息的 ...
 - 《Windows核心编程》第八章——用户模式下的线程同步
			
下面起了两个线程,每个对一个全局变量加500次,不假思索进行回答,会认为最后这个全局变量的值会是1000,然而事实并不是这样: #include<iostream> #include &l ...