正式開始學習Unity了。當然,第一個遇到的問題就是Awake和Start的問題,之前在網上查過一下這兩者的區別,簡單記憶了一下,認為自己知道了兩者的區別。不過實際用起來,發現對於這兩者到底是什么區別,心里還是沒底,而且最關鍵的是木有Unityt的源代碼,所以我們只能是通過文檔或者是別人的blog來了解,當然,還有一個辦法就是自己做一下實驗,實踐是檢驗真理的唯一標准。
先來看看Unity官方對於這兩個函數的解釋:
Awake is called when the script instance is being loaded.
Awake is used to initialize any variables or game state before the game starts. Awake is called only once during the lifetime of the script
instance. Awake is called after all objects are initialized so you can safely speak to other objects or query them using
eg. GameObject.FindWithTag. Each GameObject's Awake is called in a random order between objects. Because of this, you should use
Awake to set up references between scripts, and use Start to pass any information back and forth. Awake is always called before any
Start functions. This allows you to order initialization of scripts. Awake can not act as a coroutine.
Start is called on the frame when a script is enabled just before any of the Update methods is called the first time.
Like the Awake function, Start is called exactly once in the lifetime of the script. However, Awake is called when the script object is
initialised, regardless of whether or not the script is enabled. Start may not be called on the same frame as Awake if the script is not
enabled at initialisation time.
The Awake function is called on all objects in the scene before any object's Start function is called. This fact is useful in cases
where object A's initialisation code needs to rely on object B's already being initialised; B's initialisation should be done in Awake
while A's should be done in Start.Where objects are instantiated during gameplay, their Awake function will naturally be called after
the Start functions of scene objects have already completed.
解釋一下:
Awake在腳本被實例化的時候就會被調用(不管腳本是不是enable的),而且在腳本的生命周期中只會被調用一次。Awake是在所有對象實例化之后,所以我們可以放心大膽地去使用諸如GmeObject.Fine之類的方法來在Awake中給各個組件之間添加引用 關系。Awake會在所有對象的Start之前調用,但是注意不同對象之間的Awake順序是不得而知的。
Start是在對象被第一次enable之后,在Update之前調用的,Start在腳本的生命周期中也只可能被調用一次。Start可能不會被立刻調用,比如我們之前沒有讓其enable,當腳本被enable時,Start才會被調用。
官方文檔的建議是:盡量在Awake函數中進行初始化操作,除非有A依賴B,B必須在A實例化之前完成初始化,那么A在Start,B放在Awake中可以保證A在B之后才被初始化(不過個人感覺還是應該盡量都在Awake中進行對象間的引用,然后手動調用Init函數進行初始化,這樣可以自己控制初始化的順序)。
public class StartAwakeTest : MonoBehaviour {直接運行游戲時,輸出如下:
// Use this for initialization
void Start () {
Debug.Log("Start is called!");
}
void Awake()
{
Debug.Log("Awake is called!");
}
// Update is called once per frame
void Update () {
Debug.Log("Update is called!");
}
}
和官方文檔所說的一致,這個我們早就知道了。不過,如果我們一開始讓腳本對象不被激活,最簡單的方法就是在編輯器的Inspector面板上,找到對應的腳本前面有一個小的勾選框,默認是被勾選的,就是被激活的,如果取消勾選,那么這個腳本組件就不會被激活。
我們試一下,取消腳本的激活,然后運行:
我們看到,Start和Update函數都沒有被執行,而Awake函數仍然被執行了。可見,不管Object被不被激活,Awake函數都會被執行。這時,我們手動勾選一下Start Awake Test前面的勾選框,結果就和第一幅圖一樣啦,Start和Update都開始被執行了。
using UnityEngine;運行之后,對象雖然創建了,但是沒有掛上腳本,我們通過F1按鈕,控制其動態添加腳本,這時,會輸出Awake,但是由於我們設置了對象是非Active的,所以Start函數並沒有調用:
using System.Collections;
public class CreateObj : MonoBehaviour {
//此處通過一個引用來保存對象,因為被取消激活的對象是不能被find函數找到的!!!
private GameObject go = null;
void Awake()
{
go = new GameObject("game object");
}
void Update()
{
//添加腳本組件,默認不激活
if (Input.GetKeyUp(KeyCode.F1))
{
go.AddComponent<StartAwakeTest>();
//只讓StartAwakeTest Component 不激活 = 在編輯器里面取消腳本前面的勾選
//go.GetComponent<StartAwakeTest>().enabled = false;
//直接讓Obj不激活
go.SetActive(false);
}
//將其激活
if (Input.GetKeyUp(KeyCode.F2))
{
if (go == null)
return;
go.SetActive(true);
}
//將其取消激活
if (Input.GetKeyUp(KeyCode.F3))
{
if (go == null)
return;
go.SetActive(false);
}
}
}
當我們按下F2時,該object被激活,這時Start函數和Update函數會開始執行。輸出“Start is called!","Update is called"當我們按下F3之后,對象被取消激活,這時,Update不會再被執行。
當我們再次按下F2,讓對象第二次被激活,Start函數還會調用嗎?(這也是我最關心的)
可見,雖然Object被第二次激活,但是Start函數不會再被調用了!說明Start函數只有在第一次被激活的時候才會被調用!!!
void Awake()結果:
{
GameObject go = new GameObject("game object");
go.SetActive(false);
GameObject go1 = GameObject.Find("game object");
if (go1 == null)
Debug.Log("Can't find!");
else
Debug.Log("Find!");
}
using UnityEngine;
using System.Collections;
public class Component1 : MonoBehaviour {
void Awake()
{
GameObject go = GameObject.Find("Obj2");
if (go != null)
Debug.Log("Obj2 is found!");
go.GetComponent<Component2>().Test();
}
public void Test()
{
Debug.Log("Test in Component1 is called");
}
}
using UnityEngine;運行結果如下:
using System.Collections;
public class Component2 : MonoBehaviour {
void Awake()
{
GameObject go = GameObject.Find("Obj1");
if (go == null)
Debug.Log("Obj1 is not found!");
Debug.Log("Obj1 is found!");
go.GetComponent<Component1>().Test();
}
public void Test()
{
Debug.Log("Test in Component2 is called");
}
}
using UnityEngine;然后准備另一個腳本,供動態生成的對象使用:
using System.Collections;
public class Component1 : MonoBehaviour {
void Awake()
{
Debug.Log("Awake in Original GameObject is called!");
}
void Start()
{
Debug.Log("Start in Original GameObject is called!");
GameObject go = new GameObject("new Obj");
go.AddComponent<Component2>();
go.GetComponent<Component2>().Test();
}
}
using UnityEngine;結果如圖:
using System.Collections;
public class Component2 : MonoBehaviour {
void Awake()
{
Debug.Log("Awake in new GameObject is called!");
}
void Start()
{
Debug.Log("Start in new GameObject is called!");
}
public void Test()
{
Debug.Log("Test in new GameObject is called");
}
}
Look!Awake函數最先被調用了,然后接着是我們自定義的Test函數,最后才是Start函數!!!這里應該是很容易出現問題的地方,比如Test函數中要用到一些值,而這些值應該被初始化,如果我們把初始化放在了Start函數中,那么此處這些值還沒有被初始化,那么就會出現空引用異常等錯誤。我之前也是遇到了很多次,查了半天發現都是把對象的初始化放在了Start函數中,結果浪費了大量的時間,這也是我寫這篇文章的重要原因之一,希望大家少走彎路!
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。