1. 程式人生 > >Unity3D_02_基類MonoBehaviour/自帶函數以及腳本執行的生命周期

Unity3D_02_基類MonoBehaviour/自帶函數以及腳本執行的生命周期

幀率 標準 between 所有 可用 不同 test 工程 UC

導引:

其中Time,Input,Physics都是Unity中的全局變量。GameObject是遊戲中的基本物件。GameObject是由Component組合而成的,GameObject本身必須有Transform的Component,這也加深了我們對GameObject的理解,即GameObject是遊戲場景中真實存在,而且有位置的一個物件。

但是我們怎麽操縱這個GameObject呢?這就需要引入腳本組件了,也就是所有腳本組件的基類MonoBehaviour。

MonoBehaviour的生命周期:

MonoBehaviour是Unity中所有腳本的基類,如果你使用JS的話,腳本會自動繼承MonoBehaviour。如果使用C#的話,你需要顯式繼承MonoBehaviour。

在我們使用MonoBehaviour的時候,尤其需要註意的是它有哪些可重寫函數,這些可重寫函數會在遊戲中發生某些事件的時候被調用。我們在Unity中最常用到的幾個可重寫函數是這幾個:

Awake:當一個腳本實例被載入時Awake被調用。我們大多在這個類中完成成員變量的初始化
Start:僅在Update函數第一次被調用前調用。因為它是在Awake之後被調用的,我們可以把一些需要依賴Awake的變量放在Start裏面初始化。 同時我們還大多在這個類中執行StartCoroutine進行一些協程的觸發。要註意在用C#寫腳本時,必須使用StartCoroutine開始一個協程,但是如果使用的是JavaScript,則不需要這麽做。
Update:當MonoBehaviour啟用時,其Update在每一幀被調用。
FixedUpdate:當MonoBehaviour啟用時,其 FixedUpdate 在每一固定幀被調用。
OnEnable:當對象變為可用或激活狀態時此函數被調用。
OnDisable:當對象變為不可用或非激活狀態時此函數被調用。
OnDestroy:當MonoBehaviour將被銷毀時,這個函數被調用。

其他情況:

編輯器(Editor)
Reset:Reset函數被調用來初始化腳本屬性當腳本第一次被附到對象上,並且在Reset命令被使用時也會調用。
編者註:Reset是在用戶點擊Inspector面板上Reset按鈕或者首次添加該組件時被調用。Reset最常用於在見識面板中給定一個默認值。
第一次場景加載(First Scene Load)
這些函數會在一個場景開始(場景中每個物體只調用一次)時被調用。

Awake:這個函數總是在任何Start()函數之前一個預設被實例化之後被調用,如果一個GameObject是非激活的(inactive),在啟動期間Awake函數是不會被調用的直到它是活動的(active)。
OnEnable:只有在對象是激活(active)狀態下才會被調用,這個函數只有在object被啟用(enable)後才會調用。這會發生在一個MonoBehaviour實例被創建,例如當一個關卡被加載或者一個帶有腳本組件的GameObject被實例化。
註意:當一個場景被添加到場景中,所有腳本上的Awake()和OnEable()函數將會被調用在Start()、Update()等它們中任何函數被調用之前。自然的,當一個物體在遊戲過程中被實例化時這不能被強制執行。

第一幀更新之前(Before the first frame update)

Start:只要腳本實例被啟用了Start()函數將會在Update()函數第一幀之前被調用。
對於那些被添加到場景中的物體,所有腳本上的Start()函數將會在它們中任何的Update()函數之前被調用,自然的,當一個物體在遊戲過程中被實例化時這不能被強制執行。

在幀之間(In between frames)

OnApplicationPause:這個函數將會被調用在暫停被檢測有效的在正常的幀更新之間的一幀的結束時。在OnApplicationPause被調用後將會有額外的一幀用來允許遊戲顯示顯示圖像表示在暫停狀態下。
更新順序(Update Order)

當你在跟蹤遊戲邏輯和狀態,動畫,相機位置等的時候,有幾個不同的事件函數你可以使用。常見的模式是在Update()函數中執行大多數任務,但是也有其它的函數你可以使用。

FixedUpdate: FixedUpdate函數經常會比Update函數更頻繁的被調用。它一幀會被調用多次,如果幀率低它可能不會在幀之間被調用,就算幀率是高的。所有的圖形計算和更新在FixedUpdate之後會立即執行。當在FixedUpdate裏執行移動計算,你並不需要Time.deltaTime乘以你的值,這是因為FixedUpdate是按真實時間,獨立於幀率被調用的。

Update: Update每一幀都會被調用,對於幀更新它是主要的負荷函數。
LateUpdate:LateUpdate會在Update結束之後每一幀被調用,任何計算在Update裏執行結束當LateUpdate開始時。LateUpdate常用為第三人稱視角相機跟隨。
渲染(Rendering)

OnPreCull: 在相機剔除場景前被調用。剔除是取決於哪些物體對於攝像機是可見的,OnPreCull僅在剔除起作用之前被調用。

OnBecameVisible/OnBecameInvisible:當一個物體對任意攝像機變得可見/不可見時被調用。

OnPreRender:在攝像機開始渲染場景之前調用。

OnRenderObject:在指定場景渲染完成之後調用,你可以使用GL類或者Graphics.DrawMeshNow 來繪制自定義幾何體在這裏。

OnPostRender:在攝像機完成場景渲染之後調用。

OnRenderImage(Pro Only):在場景徐然完成之後允許屏幕圖像後期處理調用。

OnGUI:為了響應GUI事件,每幀會被調用多次(一般最低兩次)。布局Layout和Repaint事件會首先處理,接下來處理的是是通過
Layout和鍵盤/鼠標事件對應的每個輸入事件。

OnDrawGizmos:用於可視化的繪制一些小玩意在場景視圖中。
協同程序(Coroutines)

正常的協同程序更新是在Update函數返回之後運行。一個協同程序是可以暫停執行(yield)直到給出的依從指令(YieldInstruction )完成,寫成的不同運用:

yield:在所有的Update函數都已經被調用的下一幀該協程將持續執行。
yield WaitForSeconds:一段指定的時間延遲之後繼續執行,在所有的Update函數完成調用的那一幀之後。

yield WaitForFixedUpdate:所有腳本上的FixedUpdate函數已經執行調用之後持續。

yield WWW:在WWW下載完成之後持續。

yield StartCoroutine:協同程序鏈,將會等到MuFunc函數協程執行完成首先。
銷毀(When the Object is Destroyed)

OnDestory:這個函數在會在一個對象銷毀前一幀調用,會在所有幀更新一個對象存在的最後一幀之後執行,對象也許會響應Object.Destroy 或一個場景關閉時被銷毀。
退出遊戲(When Quitting)
這些函數會在你場景中所有的激活的物體上調用:

OnApplicationQuit:這個函數在應用退出之前的所有遊戲物體上調用,在編輯器(Editor)模式中會在用戶停止PlayMode時調用,在網頁播放器(web player)中會在網頁視圖關閉時調用。
OnDisable:當行為變為非啟用(disable)或非激活(inactive)時調用。

下面用一張圖來更形象地說明一下這幾個類的在MonoBehaviour的生命周期中是如何被調用的:

官方給出的腳本中事件函數的執行順序如下圖:

技術分享圖片

技術分享圖片

Unity系統自帶函數:

 1 using UnityEngine;  
 2 using System.Collections;  
 3 
 4 public class test : MonoBehaviour  
 5 {  
 6 
 7     void Awake()  
 8     {  
 9         print("Awake");  
10     }  
11 
12     void OnEnable()  
13     {  
14         print("OnEnable");  
15     }  
16 
17     void Start()  
18     {  
19         print("Start");  
20     }  
21 
22     void Update()  
23     {  
24         print("Update");  
25     }  
26 
27     void LateUpdate()  
28     {  
29         print("LateUpdate");  
30     }  
31 
32     void OnGUI()  
33     {  
34         print("OnGUI");  
35     }  
36 
37     void OnDestroy()  
38     {  
39         print("OnDestroy");  
40     }  
41 
42     void OnDisable()  
43     {  
44         print("OnDisable");  
45     }  
46 }  

自帶函數執行順序如下:

運行時:

技術分享圖片

結束時:
技術分享圖片

會發現結束的時候比運行時多兩個方法,OnDisable和OnDestroy,以上就是函數執行的順序!

腳本的編譯順序

關於腳本的編譯順序很是頭疼,官方的說法有點模糊,請看官方的解釋:
技術分享圖片

由於腳本的編譯順序會涉及到特殊文件夾,比如上面提到的Plugins、Editor還有Standard Assets等標準的資源文件夾,所以腳本的放置位置就非常重要了。下面用一個例子來說明不同文件夾中的腳本的編譯順序:
技術分享圖片

如果在你的項目中建立如上圖所示的文件夾層次結構時,編譯項目之後會在項目文件夾中生成一些文件名中包含Editor、firstpass這些字樣的項目文件。比如按照上圖的文件夾結構,我們打開項目文件夾來看一下產生的項目文件是什麽樣的?
技術分享圖片

1、首先從腳本語言類型來看,Unity3d支持3種腳本語言,都會被編譯成CLI的DLL
如果項目中包含有C#腳本,那麽Unity3d會產生以Assembly-CSharp為前綴的工程,名字中包含”vs”的是產生給Vistual Studio使用的,不包含”vs”的是產生給MonoDevelop使用的。
項目中的腳本語言 工程前綴 工程後綴 C# Assembly-CSharp csproj UnityScript Assembly-UnityScript unityproj Boo Assembly-Boo booproj
如果項目中這三種腳本都存在,那麽Unity將會生成3種前綴類型的工程。

2、對於每一種腳本語言,根據腳本放置的位置(其實也部分根據腳本的作用,比如編輯器擴展腳本,就必須放在Editor文件夾下),Unity會生成4中後綴的工程。其中的firstpass表示先編譯,Editor表示放在Editor文件夾下的腳本。
在上面的示例中,我們得到了兩套項目工程文件:分別被Virtual Studio和MonoDevelop使用(後綴包不包含vs),為簡單起見,我們只分析vs項目。得到的文件列表如下:
Assembly-CSharp-filepass-vs.csproj
Assembly-CSharp-Editor-filepass-vs.csproj
Assembly-CSharp-vs.csproj
Assembly-CSharp-Editor-vs.csproj
根據官方的解釋,它們的編譯順序如下:
(1)所有在Standard Assets、Pro Standard Assets或者Plugins文件夾中的腳本會產生一個Assembly-CSharp-filepass-vs.csproj文件,並且先編譯;

(2)所有在Standard Assets/Editor、Pro Standard Assets/Editor或者Plugins/Editor文件夾中的腳本產生Assembly-CSharp-Editor-filepass-vs.csproj工程文件,接著編譯;

(3)所有在Assets/Editor外面的,並且不在(1),(2)中的腳本文件(一般這些腳本就是我們自己寫的非編輯器擴展腳本)會產生Assembly-CSharp-vs.csproj工程文件,被編譯;

(4)所有在Assets/Editor中的腳本產生一個Assembly-CSharp-Editor-vs.csproj工程文件,被編譯。

之所以按照這樣建立工程並按此順序編譯,也是因為DLL間存在的依賴關系所決定的。

Unity3D_02_基類MonoBehaviour/自帶函數以及腳本執行的生命周期