Unity腳本生命週期與執行順序

在Unity中,腳本可以理解爲附加在遊戲對象上的用於定義遊戲對象行爲的指令代碼。必須綁定在遊戲對象上才能開始它的生命週期。遊戲對象可以理解爲能容納各種組件的容器,遊戲對象的所有組件一起決定了這個對象的行爲和遊戲中的表現。

腳本生命週期

Unity腳本中的常見必然事件如下表所示

名稱 觸發時機 用途
Awake 腳本實例被創建時調用 用於遊戲對象的初始化,注意Awake的執行早於所有腳本的Start函數
OnEnable 當對象變爲可用或激活狀態時被調用 用途
Start Update函數第一次運行之前調用 用於遊戲對象的初始化
Update 每幀調用一次 用於更新遊戲場景和狀態
FixedUpdate 每個固定物理時間間隔調用一次 用於物理狀態的更新
LateUpdate 每幀調用一次(在update之後調用) 用於更新遊戲場景和狀態,和相機有關的更新一般放在這裏
OnGUI 渲染和處理OnGUI事件 用途
OnDisable 當前對象不可用或非激活狀態時被調用 用途
OnDestroy 當前對象被銷燬時調用 用途

下面將以代碼來看看這些必然事件的調用時機
新建一個C#腳本,並添加以下代碼,然後將其掛到任意的遊戲對象上

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestTest : MonoBehaviour
{
    private void Awake()
    {
        Debug.Log("Awake");
    }

    private void OnEnable()
    {
        Debug.Log("OnEnable");
    }

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Start");
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("Update");
    }

    private void FixedUpdate()
    {
        Debug.Log("FixedUpdate");
    }

    private void LateUpdate()
    {
        Debug.Log("LateUpdate");
    }

    private void OnGUI()
    {
        Debug.Log("OnGUI");
    }

    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }

    private void OnDestroy()
    {
        Debug.Log("OnDestroy");
    }
}

打印結果如下圖示:

打印結果

可以發現,Awake, Start函數都是在遊戲對象被創建時調用一次。
當遊戲過程中調整腳本的可見狀態時,會分別調用OnEnable, OnDisable函數,而Awake和Start將不會再調用,也就是說一旦腳本被掛載上以後,Awake和Start有且僅會被執行一次。
而Update, FixedUpdate, LateUpdate, OnGUI函數是會在遊戲過程中被多次調用的(日誌窗口右側的數字表示該條日誌信息打印的次數)。
最後在遊戲對象被銷燬時,會依次調用OnDisable, OnDestory函數。

MonoBehavior生命週期圖

下面再放上一張外國友人所畫的生命週期圖

生命週期圖

腳本執行順序

在遊戲開發中,不可避免的會使用許多腳本,那麼如何確定不同腳本之間調用的先後順序呢

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Test1 : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("start 1");
    }

    private void Awake()
    {
        Debug.Log("awake 1");
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("update 1");
    }
}

將上面的代碼依次添加到Test1, Test2和Test3腳本中(適當修改打印的日誌),並掛載到不同的遊戲對象上。
掛載的順序爲先掛載Test3,再掛載Test2,最後掛載Test1
打印結果如下圖所示
打印結果

打印結果是先打印Test1的,再打印Test2,最後打印Test3。
其實腳本的執行順序與掛載到遊戲對象上的先後順序有關。最先被掛載的最後執行,最後被掛載的最先執行(如果讀者有疑惑,可以不斷調整腳本的掛載順序,看日誌打印與上述結論是否相符)。

需要注意的是,無論多個腳本的執行順序如何,但所有腳本的Awake函數一定會比所有的Start函數先執行完,所有的Start函數一定也會比所有的Update函數先執行完,其他有順序的生命週期函數也是類似的(從上圖的日誌信息中也可以看出)。

自定義執行順序

有時可能有這樣的需求,A腳本中的屬性實例化可能需要用到B腳本中的屬性,所以在A腳本屬性實例化時,必須保證B腳本已經被實例化完畢。當然我們可以通過先掛在A腳本再掛載B腳本來實現。但在實際開發中,用到的腳本中多,很難去記住各個腳本掛載的先後順序。所以Unity提供了Script Execution Order配置項,來配置多個腳本的執行順序。
在工程面板中任意點擊選中一個腳本文件,在屬性面板中會出現該腳本的詳細信息,選擇右上角的Execution Order...,打開如下圖所示界面

執行順序界面

點擊“+”可以添加腳本,爲其設置order值,order值越小的越先執行,order值越大的越後執行

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章