Unity3d中默认函数调用顺序(MonoBehaviour)

首先要明确的是MonoBehaviour是每个脚本的基类.每个Javascript脚本自动继承MonoBehaviour.使用C#或Boo时,需要显式继承MonoBehaviour.
 
 
 
 

Unity是不支持多线程的,也就是说我们必须要在主线程中操作它,可是Unity可以同时创建很多脚本,并且可以分别绑定在不同的游戏对象身上,他们各自都在执行自己的生命周期感觉像是多线程,并行执行脚本的,它是如何执行的呢?

我们做一个小小的实验来验证它。如下图所示,在Hierarchy视图中创建三个游戏对象,在Project视图中创建三条脚本,然后按照顺序将脚本绑定在对应的游戏对象身上。

Unity3D研究院之深入理解Unity脚本的执行顺序(六十二) - 雨松MOMO程序研究院 - 1

三条脚本的代码完全一样,只是做了一点名称上的区分,代码写的比较丑我们只是作为测试!!

 播放游戏,看看他们的执行顺序。如下图所示,Awake、Update、LateUpdate、无论播放游戏多少次,他们执行的顺序是完全一样的。

Unity3D研究院之深入理解Unity脚本的执行顺序(六十二) - 雨松MOMO程序研究院 - 2

 

 

接着我们在做一个测试,把Script0的Update方法注释掉!!

 播放游戏,在看看它的结果。脚本的执行顺序和以前完全一样,Script0即使删除掉了Update方法,但是它也不会直接执行LateUpdate方法,而是等待Script1和Script2的Update方法都执行完毕以后,在去执行所有的LateUpdate方法。

 

Unity3D研究院之深入理解Unity脚本的执行顺序(六十二) - 雨松MOMO程序研究院 - 3

 

通过这两个例子我们就可以清楚的断定Unity后台是如何执行脚本的了。每个脚本的Awake、Update、LateUpdate、FixedUpdate等等,方法在后台都有一个总汇。

后台的Awake()

{

       脚本0中的Awake();

       脚本1中的Awake();

       脚本2中的Awake();

}

       后台的方法 Awake、Update、LateUpdate、FixedUpdate等等都是按照顺序,等所有子脚本中的Awake执行完毕后在去执行 Start 、Update、LateUpdate等等。所以这里也就解释了Unity没有多线程的概念。

后台的Update()

{

       脚本0中的Update();

       脚本1中的Update();

       脚本2中的Update();

}

Unity还提供的一组协同任务的方法,其实它的原理和上面的完全一样,它们都是假的多线程。说了一圈我们又回到了Unity脚本的执行顺序上来?我们在看两条脚本!

在脚本2的Awake方法中创建一个立方体对象。

在脚本0的Awake方法中去获取这个立方体对象 

 

          如果脚本的执行顺序是 先执行Script2 然后在执行Script0那么Script0中的Awake就可以获取到该立方体对象,可是如果脚本的执行顺序是先Script0然后在Script2,那么Script0肯定会报空指针错误的。

          那么实际项目中的脚本会非常非常多,他们的先后顺序我们谁也不知道。所以我的建议一般在Awake方法中创建游戏对象或在Resources.Load(Prefab) 对象。在Start方法中去获取游戏对象,或者游戏组件,这样就可以确保万无一失了。

     如果说你非要控制脚本的执行先后顺序,也不是完全不行!Unity可以设置脚本执行的顺序。如下图所示,选择任意脚本在Inspector视图中点击Execution Order..按钮。

Unity3D研究院之深入理解Unity脚本的执行顺序(六十二) - 雨松MOMO程序研究院 - 4

如下图所示,点击右下角的“+”将弹出下拉窗口,包括游戏中的所有脚本。添加脚本完毕后,Default Time下方数值越小的排在越前面脚本将率先执行,如果没有设置的脚本将按默认的顺序执行。

Unity3D研究院之深入理解Unity脚本的执行顺序(六十二) - 雨松MOMO程序研究院 - 5

 

按照我的这个设置,程序将先执行Script0然后Script1最后Script2,欢迎一起讨论!!哇咔咔

 

 

46

It's rather complicated.

A simple test with 3.5.2 revealed, most concurrent functions (well, at least the ones I tested: Awake, Start, OnEnable, FixedUpdate/Update/LateUpdate) abide by the execution order defined for the scripts. The execution order of OnLevelWasLoaded is not affected by that, and therefore cannot be influenced by the user. This could be considered a bug.

The order of the four methods of a script related to initialization is always:

  • Awake()

  • OnEnable()

  • OnLevelWasLoaded() // (only on scene changes)

  • Start()

However, if your script was disabled in the first place(via Script.enabled=false), this order changes to:

  • OnLevelWasLoaded() // is now called first, before Awake()! (only on scene changes)

  • Awake()

  • [OnEnable()/Start() are not executed until the script is actually enabled]

In addition, note that Awake() and OnEnable() calls are connected/interleaved. Meaning, assuming a particular, user-defined execution order A and B with A*<*B,

  • each individual script of type A will execute its Awake(), immediately! followed by its OnEnabled()

  • then all scripts of type B will do the same

  • then all OnLevelWasLoaded() will be executed, in a (presumably) fixed but unpredictable order (assuming this scene was freshly loaded - otherwise this step is skipped completely)

  • then all Start() will be executed, in the order A,B

In particular, this means that OnEnable() of type A will be executed before Awake() of type B, while OnEnable() of type B will be executed after Awake() of type A. This overview explains it more clearly:

  • Awake() of Type A, instance 1

  • OnEnable() of Type A, instance 1

  • Awake() of Type A, instance 2 // order of instances cannot be influenced

  • OnEnable() of Type A, instance 2 // order of instances cannot be influenced

  • Awake() of Type B

  • OnEnable() of Type B

  • OnLevelWasLoaded() of Type ? // order cannot be influenced

  • OnLevelWasLoaded() of Type ? // order cannot be influenced

  • Start() of Type A

  • Start() of Type B

EDIT: Hm, this is a total mess. If DontDestroyOnLoad() is activated for such a script, this will get even more complicated, and the order changes yet again to:

  • [Awake() is never called again, only the very first time]

  • OnEnable()

  • OnLevelWasLoaded() // as opposed to being called before OnEnable(), when DontDestroyOnLoad() is notactivated

  • [Start() is never called again, only the very first time]

EDIT2: In addition, when DontDestroyOnLoad() is active, the user-defined execution order is no longer abided by!, neither by OnEnable(), nor by OnDestroyOnLoad().

EDIT3: WAH! I'm gonna stop testing now, this is a neverending story... As @Noisecrime noticed, there is actually another bug, where user-defined execution order is overriden if the script has an OnEnable() function!

Script A has OnEnable, Script B has not:

  • Awake() of Type A

  • OnEnable() of Type A

  • Awake() of Type B

Script B has OnEnable, Script A has not:

  • Awake() of Type B

  • OnEnable() of Type B

  • Awake() of Type A // after Type B!

posted @ 2015-03-14 07:40  zhepama  阅读(884)  评论(0编辑  收藏  举报