Unity3D指令碼中Start()和Awake()的區別對比分析

七大黍發表於2014-10-17



初學者經常把Awake和Start混淆。


簡單說明一下,Awake在MonoBehavior建立後就立刻呼叫,Start將在MonoBehavior建立後在該幀Update之前,在該Monobehavior.enabled == true的情況下執行。




void Awake (){
}     
//初始化函式,在遊戲開始時系統自動呼叫。一般用來建立變數之類的東西。

void Start(){
}
//初始化函式,在所有Awake函式執行完之後(一般是這樣,但不一定),在所有Update函式前系統自動條用。一般用來給變數賦值。
我們通常書寫的指令碼,並不會定義[ExecuteInEditMode]這個Attribute,所以Awake和Start都只有在Runtime中才會執行


例1:


public class Test : MonoBehaviour {
    void Awake () {
        Debug.Log("Awake");
        enabled = false;
    }

    void Start () {
        Debug.Log("Start");
    }
}
以上程式碼,在Awake中我們呼叫了enabled = false; 禁止了這個MonoBehavior的update。由於Start, Update, PostUpdate等屬於runtime行為的一部分,這段程式碼將使Start不會被呼叫到。


在遊戲過程中,若有另外一組程式碼有如下呼叫:




Test test = go.GetComponent<Test>();
test.enabled = true;
這個時候,若該MonoBehavior之前並沒有觸發過Start函式,將會在這段程式碼執行後觸發。




例2:


player.cs


private Transform handAnchor = null;
void Awake () { handAnchor = transform.Find("hand_anchor"); }
// void Start () { handAnchor = transform.Find("hand_anchor"); }
void GetWeapon ( GameObject go ) {
    if ( handAnchor == null ) {
        Debug.LogError("handAnchor is null");
        return;
    }
    go.transform.parent = handAnchor;
}


other.cs


GameObject go = new GameObject("player");
player pl = go.AddComponent<player>(); // Awake invoke right after this!
pl.GetWeapon(weaponGO);
...
以上程式碼中,我們在player Awake的時候去為handAnchor賦值。如果我們將這步操作放在Start裡,那麼在other.cs中,當執行GetWeapon的時候就會出現handAnchor是null reference.


總結:我們儘量將其他Object的reference設定等事情放在Awake處理。然後將這些reference的Object的賦值設定放在Start()中來完成。
當MonoBehavior有定義[ExecuteInEditMode]時


當我們為MonoBehavior定義了[ExecuteInEditMode]後,我們還需要關心Awake和Start在編輯器中的執行狀況。


當該MonoBehavior在編輯器中被賦於給GameObject的時候,Awake, Start 將被執行。
當Play按鈕被按下游戲開始以後,Awake, Start 將被執行。
當Play按鈕停止後,Awake, Start將再次被執行。
當在編輯器中開啟包含有該MonoBehavior的場景的時候,Awake, Start將被執行。


值得注意的是,不要用這種方式來設定一些臨時變數的儲存(private, protected)。因為一旦我們觸發Unity3D的程式碼編譯,這些變數所儲存的內容將被清為預設值。


下面再來看看Unity聖典中的解釋。


Awake()


當一個指令碼例項被載入時Awake被呼叫。


Awake用於在遊戲開始之前初始化變數或遊戲狀態。在指令碼整個生命週期內它僅被呼叫一次.Awake在所有物件被初始化之後呼叫,所以你可以安全的與其他物件對話或用諸如 


GameObject.FindWithTag 這樣的函式搜尋它們。每個遊戲物體上的Awke以隨機的順序被呼叫。因此,你應該用Awake來設定指令碼間的引用,並用Start來傳遞資訊。Awake總是在Start之前被調


用。它不能用來執行協同程式。
Start()


Start僅在Update函式第一次被呼叫前呼叫。Start在behaviour的生命週期中只被呼叫一次。它和Awake的不同是Start只在指令碼例項被啟用時呼叫。
你可以按需調整延遲初始化程式碼。Awake總是在Start之前執行。這允許你協調初始化順序。


相關文章