首页 > 代码库 > Unity3D中脚本的执行顺序和编译顺序

Unity3D中脚本的执行顺序和编译顺序

在Unity中可以同时创建很多脚本,并且可以分别绑定到不同的游戏对象上,它们各自都在自己的生命周期中运行。与脚本有关的也就是编译和执行啦,本文就来研究一下Unity中脚本的编译和执行顺序的问题。

先说一下执行顺序吧。
官方给出的脚本执行顺序如下图:

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

三条脚本的代码完全一样,只是做了一点名称上的区分:

 1 using System.Collections; 2  3 public class Scring0 : MonoBehaviour 4 { 5     void Awake() 6     { 7         Debug.Log("Script0 ======= Awake"); 8     } 9 10     bool isUpdate = false;11     void Update()12     {13         if(!isUpdate)14         {15             Debug.Log("Script0 ======= Update");16             isUpdate = true;17         }18     }19 20     bool isLateUpdate = false;21     void LateUpdate()22     {23         if(!isLateUpdate)24         {25             Debug.Log("Script0 ======= LateUpdate");26             isLateUpdate = true;27         }28     }29 }

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

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

 1 using System.Collections; 2  3 public class Scring0 : MonoBehaviour 4 { 5    void Awake() 6    { 7        Debug.Log("Script0 ======= Awake"); 8    } 9 10 //   bool isUpdate = false;11 //   void Update()12 //   {13 //       if(!isUpdate)14 //       {15 //           Debug.Log("Script0 ======= Update");16 //           isUpdate = true;17 //       }18 //   }19 20    bool isLateUpdate = false;21    void LateUpdate()22    {23        if(!isLateUpdate)24        {25            Debug.Log("Script0 ======= LateUpdate");26            isLateUpdate = true;27        }28    }29 }

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

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

1 后台的Awake()2 {3     脚本0中的Awake();4     脚本1中的Awake();5     脚本2中的Awake();6 }

后台的方法Awake、Update、LateUpdate等等,都是按照顺序,等所有游戏对象上脚本中的Awake执行完毕之后,再去执行Start、Update、LateUpdate等方法的。

1 后台的Update()2 {3     脚本0中的Update();4     脚本1中的Update();5     脚本2中的Update();6 }

然后我们来看看这样一种情况:在脚本0的Awake方法中创建一个立方体对象,然后在脚本2的Awake方法中去获取这个立方体对象。代码如下:

 1 脚本0的代码: 2 using UnityEngine; 3 using System.Collections; 4  5 public class Script2 : MonoBehaviour 6 { 7     void Awake() 8     { 9         GameObject.CreatePrimitive(PrimitiveType.Cube);10     }11 }12 13 脚本2的代码:14 using UnityEngine;15 using System.Collections;16 17 public class Script0 : MonoBehaviour18 {19     void Awake()20     {21         GameObject go = GameObject.Find("Cube");22         Debug.Log(go.name);23     }24 }

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

那么实际项目中的脚本会非常多,它们的先后执行顺序我们谁也不知道(又说是按照栈结构来执行的,即后绑定到游戏对象上的脚本先执行。这一点值得研究)。但一般的,建议在Awake方法中创建游戏对象或Resources.Load(Prefab)对象,然后在Start方法中去获取游戏对象或者组件,这样就可以确保万无一失了。
另外,Unity也提供了一个方法来设置脚本的执行顺序,在Edit -> Project Settings -> Script Execution Order菜单项中,可以在Inspector面板中看到如下图所示:

点击右下角的"+"将弹出下拉窗口,包括游戏中的所有脚本。脚本添加完毕后,可以用鼠标拖动脚本来为脚本排序,脚本名后面的数字也越小,脚本越靠上,也就越先执行。其中的Default Time表示没有设置脚本的执行顺序的那些脚本的执行顺序。

Unity3D中脚本的执行顺序和编译顺序