Unity脚本执行顺序

发表于2017-06-08
评论0 3.5k浏览

在Unity中,要控制Unity的脚本执行顺序,Unity引擎本身已经有了一个脚本排序。这个排序在编辑器中可以编辑并设置。它里面带有默认的,根据优先级来排定执行顺序。若没有在排序的均在default time排序的间隙随机执行。也就是说,在在default time 以上列表中的优先级总是高于其他排序。 


在Unity中,继承于MonoBehaviour 的类,有几个Unity3D自带的事件函数按照预定的顺序执行作为脚本执行。

我们可以做一些测试来验证以及学习他们的执行顺序。

测试1.0:

创建一个空物体A,添加脚本ScriptA,代码如下:

[csharp] view plain copy
  1. public class ScriptA : MonoBehaviour {  
  2.   
  3.     void Awake()  
  4.     {  
  5.         Debug.LogWarning("ScriptA.Awake");  
  6.     }  
  7.     void Start () {  
  8.         Debug.LogWarning("ScriptA.Start");  
  9.     }  
  10.       
  11.     void Update () {  
  12.         Debug.LogWarning("ScriptA.Update");  
  13.     }  
  14.     void OnDisable()  
  15.     {  
  16.         Debug.LogWarning("ScriptA.OnDisable");  
  17.     }  
  18.     void OnDestroy()  
  19.     {  
  20.         Debug.LogWarning("ScriptA.OnDestroy");  
  21.     }  
  22.     void OnEnable()  
  23.     {  
  24.         Debug.LogWarning("ScriptA.OnEnable");  
  25.     }  
  26.     void FixedUpdate()  
  27.     {  
  28.         Debug.LogWarning("ScriptA.FixedUpdate");  
  29.     }  
  30.   
  31.     void LateUpdate()  
  32.     {  
  33.         Debug.LogWarning("ScriptA.LateUpdate");  
  34.     }  
  35. }  

运行代码可以得到如下结果:


结束程序可以得到如下结果:


上述试验可以得出简单结论:

Awake -> OnEnable ->  Start -> (FixedUpdate -> Update -> LateUpdate) -> OnDisable -> OnDestroy

其中,括号内的是一直不断执行的。

测试1.1:

我们做点简单的手脚,先将不断循环的内容屏蔽(FixedUpdate 、 Update 、 LateUpdate),然后在运行后,将此物体设为不可见(模拟 SetActive(false)),再将物体设为可见(模拟 SetActive(true)),结果如下:


说明了 SetActive(false) 会执行代码中的 OnDisable,SetActive(true) 会执行代码中的OnEnable。

测试1.2:

依然先将循环的内容屏蔽,然后再运行。运行时先将脚本取消选用(模拟 enabled = false),再将脚本选用(模拟 enabled = true,结果如下:


说明了执行的内容和SetActive 一样。

测试2.0:

新建脚本ScriptB 和 ScriptC。代码和 ScriptA 一样,屏蔽了循环的内容。都挂在物体A上。


结果如下:


结论如下:

最底下的脚本先执行,而Awake和OnEnable的执行顺序交替进行,Start最后,流程如下

C.Awake -> C.OnEnable -> B.Awake -> B.OnEnable -> A.Awake -> A.OnEnable -> C.Start -> B.Start -> A.Start 

测试2.1:

我们先选择单击一脚本,如SriptA。如下图:


再点击上图红色箭头指示的Execution Order。出现如下界面:


点击箭头指示的“加号”,依次添加ScriptA、ScriptB、ScriptC,并Apply。如下所示:


再次运行程序,结果如下:


我们清楚的看到,脚本执行的顺序改变了,按照我们在Execution Order设置的顺序执行了。


测试2.2 :

在物体A上仅挂载脚本ScriptD,在ScriptD中加载ScriptE和销毁ScriptE。代码如下:

ScriptD

[csharp] view plain copy
  1. public class ScriptD : MonoBehaviour  
  2. {  
  3.     private ScriptE scriptE;  
  4.     void Awake()  
  5.     {  
  6.         scriptE = gameObject.AddComponent();  
  7.         Debug.LogWarning("AddComponent ScriptE Success");  
  8.         StartCoroutine(DeleteScriptE());  
  9.     }  
  10.   
  11.     IEnumerator DeleteScriptE()  
  12.     {  
  13.         yield return new WaitForSeconds(1.0f);  
  14.         if (scriptE != null)  
  15.         {  
  16.             GameObject.Destroy(scriptE);  
  17.             Debug.LogWarning("Destroy ScriptE Success");  
  18.         }  
  19.     }  
  20. }  

ScriptE

[csharp] view plain copy
  1. public class ScriptE : MonoBehaviour {  
  2.   
  3.     void Awake()  
  4.     {  
  5.         Debug.LogWarning("ScriptE.Awake");  
  6.     }  
  7.     void Start()  
  8.     {  
  9.         Debug.LogWarning("ScriptE.Start");  
  10.     }  
  11.     void OnEnable()  
  12.     {  
  13.         Debug.LogWarning("ScriptE.OnEnable");  
  14.     }  
  15.     void OnDisable()  
  16.     {  
  17.         Debug.LogWarning("ScriptE.OnDisable");  
  18.     }  
  19.     void OnDestroy()  
  20.     {  
  21.         Debug.LogWarning("ScriptE.OnDestroy");  
  22.     }  
  23. }  

结果如下:

结论如下:

ScriptE.Awake 和 ScriptE.OnEnable 方法应该是在执行scriptE = gameObject.AddComponent(); 这条语句的时候就已经被调用了。而ScriptE.Start在之后再执行。

同理: ScriptE.OnDisable 方法应该是在执行 GameObject.Destroy(scriptE); 时就被调用了。而ScriptE.OnDestroy 则是更后一些。


更多:

脚本更为详细的执行顺序。


各函数描述:

Awake当一个脚本实例被载入时Awake被调用。Awake用于在游戏开始之前初始化变量或游戏状态。在脚本整个生命周期内它仅被调用一次.Awake在所有对象被初始化之后调用,所以你可以安全的与其他对象对话或用诸如 GameObject.FindWithTag 这样的函数搜索它们。每个游戏物体上的Awke以随机的顺序被调用。因此,你应该用Awake来设置脚本间的引用,并用Start来传递信息Awake总是在Start之前被调用。它不能用来执行协同程序。

OnEnable当对象变为可用或激活状态时此函数被调用。

StartStart仅在Update函数第一次被调用前调用。它和Awake的不同是Start只在脚本实例被启用时调用。你可以按需调整延迟初始化代码。Awake总是在Start之前执行。这允许你协调初始化顺序。在所有脚本实例中,Start函数总是在Awake函数之后调用。

FixedUpdate当MonoBehaviour启用时,其 FixedUpdate 在每一帧被调用。处理Rigidbody时,需要用FixedUpdate代替Update。例如:给刚体加一个作用力时,你必须应用作用力在FixedUpdate里的固定帧,而不是Update中的帧。(两者帧长不同)

Update当MonoBehaviour启用时,其Update在每一帧被调用。Update是实现各种游戏行为最常用的函数。为了获取自最后一次调用Update所用的时间,可以用Time.deltaTime。这个函数只有在Behaviour启用时被调用。实现组件功能时重载这个函数。

LateUpdate:当Behaviour启用时,其LateUpdate在每一帧被调用。LateUpdate是在所有Update函数调用后被调用。这可用于调整脚本执行顺序。例如:当物体在Update里移动时,跟随物体的相机可以在LateUpdate里实现。为了获取自最后一次调用LateUpdate所用的时间,可以用Time.deltaTime。这个函数只有在Behaviour启用时被调用。实现组件功能时重载这个函数。

OnGUI:渲染和处理GUI事件时调用。这意味着你的OnGUI程序将会在每一帧被调用。要得到更多的GUI事件的信息查阅Event手册。如果Monobehaviour的enabled属性设为false,OnGUI()将不会被调用。

OnDisable:当对象变为不可用或非激活状态时此函数被调用。当物体被销毁时它将被调用,并且可用于任意清理代码。当脚本编译完成之后被重加载时,OnDisable将被调用,OnEnable在脚本被载入后调用。

OnDestroy:当MonoBehaviour将被销毁时,这个函数被调用。OnDestroy只会在预先已经被激活的游戏物体上被调用。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引

0个评论