[註解]Execution Order of Event Functions: Unity生命週期及常見問題
Script Lifecycle Flowchart 指令碼生命週期流程圖
The following diagram summarises the ordering and repetition of event functions during a script’s lifetime.
In Unity scripting, there are a number of event functions that get executed in a predetermined order as a script executes. This execution order is described below:
Unity框架有一個複雜的遊戲迴圈
First Scene Load 第一次場景載入
These functions get called when a __scene __starts (once for each object in the scene).
-
Awake:
- This function is always called before any Start functions and also just after a prefab is instantiated. (If a GameObject is inactive during start up Awake is not called until it is made active.)
- 呼叫時機:
- 在prefab被例項化之後呼叫(todo)
- 在呼叫
Start
之前呼叫
- 注意:
- 如果GameObject在遊戲啟動時處於非活動狀態,則不會呼叫
Awake
, 直到它被啟用才會呼叫Awake
- 不切換場景的情況下,
Awake
只會在首次啟用時呼叫, (只調用一次) - 在所有物件都初始化之後, 才呼叫
Awake
, 所以可以安全地與其他物件互動, 或使用例如GameObject.FindWithTag
來查詢物件 - 在Unity中, 使用
Awake
而不是建構函式來進行初始化,因為元件的序列化狀態在構造時未定義。Awake
僅被呼叫一次,就像建構函式一樣。 Awake
- 任何
Start
呼叫之前, 總是先呼叫Awake
- 如果GameObject在遊戲啟動時處於非活動狀態,則不會呼叫
-
OnEnable:
- (only called if the Object is active): This function is called just after the object is enabled. This happens when a MonoBehaviour instance is created, such as when a level is loaded or a GameObject with the script component is instantiated.
- 呼叫時機
- MonoBehaviour例項被建立之後(例如關卡載入時, 或GameObject的指令碼被例項化)
- 物件啟用後立即呼叫此函式
- __注意: __
- 僅在Object處於啟用狀態時呼叫 (啟用物件的函式有:
GameObject.SetActive(true)
) - 每次啟動時都會呼叫!(啟動物件的函式有
object.enable = true
)
- 僅在Object處於啟用狀態時呼叫 (啟用物件的函式有:
-
OnLevelWasLoaded:
- This function is executed to inform the game that a new level has been loaded.
- 呼叫時機:
- 當一個新的關卡被載入完畢的時候
Note that for objects added to the scene, the Awake and OnEnable functions for all __scripts __will be called before Start, Update, __etc __are called for any of them. Naturally, this cannot be enforced when an object is instantiated during gameplay.
Editor 編輯器
- Reset:
- Reset is called to initialize the script’s properties when it is first attached to the object and also when the Reset command is used.
- 執行時機:
- 當指令碼第一次被附著到GameObject上面時, 該MonoBehavior的屬性會進行初始化
- 或者執行
Reset
命令時, (編輯器指令碼中或者在編輯器介面滑鼠右鍵選擇"Reset")
- 注意:
- 在編輯器環境下起作用
Before the first frame update 第一幀更新之前
- Start:
- Start is called before the first frame update only if the script instance is enabled.
- 呼叫時機:
- 在第一幀更新之前呼叫
- 只有在指令碼被啟用時它才會執
- 注意:
- __第一幀更新之前__呼叫, 說明理論上只會呼叫一次!!
- 如果GameObject被啟用時, 該指令碼物件沒有被啟動, 則該
Start
不會被呼叫 - 如果偏要多呼叫幾次
Start
__ , __可以在程式碼裡面寫Start();
For objects added to the scene, the Start function will be called on all scripts before Update, etc are called for any of them. Naturally, this cannot be enforced when an object is instantiated during gameplay.
In between frames 在幀之間
- OnApplicationPause:
- This is called at the end of the frame where the pause is detected, effectively between the normal frame updates. One extra frame will be issued after OnApplicationPause is called to allow the game to show graphics that indicate the paused state.
- 呼叫時機:
- 當檢測到暫停時, 有效的正常幀更新之間, 呼叫此函式
- 注意:
- 呼叫此函式後, 有額外的一幀被允許來處理影象顯示(比如顯示遊戲處於暫停狀態)
Update Order 更新順序
When you’re keeping track of game logic and interactions, animations, __camera __positions, etc., there are a few different events you can use. The common pattern is to perform most tasks inside the Update function, but there are also other functions you can use.
在處理有關遊戲邏輯, 狀態, 動畫, 相機位置的時候, 有好幾個關於更新的函式可以使用
- FixedUpdate:
- FixedUpdate is often called more frequently than Update. It can be called multiple times per frame, if the frame rate is low and it may not be called between frames at all if the frame rate is high. All physics calculations and updates occur immediately after FixedUpdate. When applying movement calculations inside FixedUpdate, you do not need to multiply your values by Time.deltaTime. This is because FixedUpdate is called on a reliable timer, independent of the frame rate.
- 呼叫時機:
- 基於可靠的定時器呼叫,與幀率無關
- 注意:
- 當幀率低的時候, 可能會一幀呼叫多次
- 當幀率高的時候, 可能不會每一幀都呼叫
- FixedUpdate的時間間隔可以在專案設定中更改,Edit->ProjectSetting->time 找到Fixedtimestep
- 不要濫用
FixedUpdate
方法,因為它是用來處理物理模擬的,更重要的是它並非根據真實時間的間隔執行
- 使用場景:
- 處理物理模擬: 當向剛體新增力時,必須在FixedUpdate內的每個固定幀上應用力,而不是在Update中的每個幀
- Update:
- Update is called once per frame. It is the main workhorse function for frame updates.
- 呼叫時機:
- 每一幀, 這是幀更新最主要的方法
- 注意:
- 這個函式的更新頻率和裝置的效能有關以及被渲染的物體(可以認為是三角形的數量)
- 為了獲得自上次呼叫Update以來經過的時間, 可以使用Time.deltaTime.
- LateUpdate:
- LateUpdate is called once per frame, after Update has finished. Any calculations that are performed in Update will have completed when LateUpdate begins. A common use for LateUpdate would be a following third-person camera. If you make your character move and turn inside Update, you can perform all camera movement and rotation calculations in LateUpdate. This will ensure that the character has moved completely before the camera tracks its position.
- 呼叫時機:
- 每一幀都呼叫
Update
函式執行完畢之後呼叫
Rendering 渲染
- OnPreCull:
- Called before the camera culls the scene. Culling determines which objects are visible to the camera. OnPreCull is called just before culling takes place.
- 在相機場景剔除前呼叫
- OnBecameVisible/OnBecameInvisible:
- Called when an object becomes visible/invisible to any camera.
- 當一個物體對任意攝像機變得可見/不可見時被呼叫
- OnWillRenderObject:
- Called once for each camera if the object is visible.
- 當物體被相機渲染之前呼叫
- OnPreRender:
- Called before the camera starts rendering the scene.
- 在相機開始渲染場景之前呼叫
- OnRenderObject:
- Called after all regular scene rendering is done. You can use GL class or Graphics.DrawMeshNow to draw custom geometry at this point.
- 在所有特定的場景渲染完成之後呼叫
- 可以使用GL類或者Graphics.DrawMeshNow 來繪製自定義幾何體
- OnPostRender:
- Called after a camera finishes rendering the scene.
- 當相機完成場景的渲染工作後呼叫
- OnRenderImage:
- Called after scene rendering is complete to allow post-processing of the image, see Post-processing Effects.
- 當場景的渲染工作完成之後, 可以用來進行影象的後處理
- OnGUI:
- Called multiple times per frame in response to GUI events. The Layout and Repaint events are processed first, followed by a Layout and keyboard/mouse event for each input event.
- 呼叫時機:
- 一幀呼叫多次(為了響應GUI事件)
- __注意: __
- 先處理佈局和重繪事件
- 再處理佈局和每個輸入事件
- 可以用來編輯器工具的繪製
- OnDrawGizmos
- Used for drawing __Gizmos __in the __scene view __for visualisation purposes.
- 可以用來在Scene檢視內繪製小玩意
- 例如射線, 正方體, 球體, 相機的視錐體等(可以用在動態地圖視覺化除錯的輔助工具?)
Coroutines 協同程式
Normal coroutine updates are run after the Update function returns. A coroutine is a function that can suspend its execution (yield) until the given YieldInstruction finishes. Different uses of Coroutines:
正常的協同程式更新是在Update函式返回之後執行
- yield
- The coroutine will continue after all Update functions have been called on the next frame.
- 呼叫時機:
- 下一幀中, 且所有Update函式呼叫完畢之後
- yield WaitForSeconds
- Continue after a specified time delay, after all Update functions have been called for the frame
- 呼叫時機:
- 在指定時間延遲之後的那一幀, 且所有Update函式呼叫完畢之後之後呼叫
- yield WaitForFixedUpdate
- Continue after all FixedUpdate has been called on all scripts
- 等待所有的
FixedUpdate
函式呼叫完畢之後
- yield WWW
- Continue after a WWW download has completed.
- 等待WWW下載完畢之後執行
- yield StartCoroutine
- Chains the coroutine, and will wait for the MyFunc coroutine to complete first.
- 等待
MyFunc
協程執行完畢之後
When the Object is Destroyed 物件被銷燬的時候
- OnDestroy:
- This function is called after all frame updates for the last frame of the object’'s existence (the object might be destroyed in response to Object.Destroy or at the closure of a scene).
- 呼叫時機:
- object生命週期的最後一幀中, 所有幀更新完畢之後呼叫
- 呼叫
Object.Destroy
函式之後 - 切換場景或結束遊戲的時候
When Quitting 退出遊戲
These functions get called on all the active objects in your scene:
- OnApplicationQuit:
- This function is called on all game objects before the application is quit. In the editor it is called when the user stops playmode.
- 呼叫時機:
- 遊戲結束之前
- 編輯器模式下, 停止遊戲執行的時候
- OnDisable:
- This function is called when the behaviour becomes disabled or inactive.
- 當MonoBehavior被禁用(SetActive(false) 或 Object.Enable(false))的時候呼叫(與
OnEnable
相反)
常見問題:
Awake和Start的區別是什麼?
兩者的關係?
- Awake和Start函式在指令碼生命週期中都是隻呼叫一次
- 在指令碼物件初始化完成後, 無論它是否啟動(但是它得處於啟用狀態), 都會呼叫Awake; 而Start僅會在啟動的時候才會在同一幀被呼叫
- Awake在Start之前呼叫
兩者如何配合?
- 如果A物件的初始化工作需要依賴到B物件初始化完畢的資源, 則B的初始化工作需要在Awake內進行, A的初始化工作需要在Start內進行
- 每個GameObject的不同指令碼物件的Awake是隨機順序呼叫的, 應該使用Awake來設定指令碼間的相互引用,並使用Start來回傳遞任何資訊。
物件啟用和物件啟用的區別?
我個人的理解是這樣(暫時找不到資料呀…):
一個GameObject可以有很多個指令碼物件objects, 那麼啟用GameObject物件可以在Inspector面板勾選, 也可以使用GameObject.SetActive(true)
函式, 啟動指令碼物件可以在Inspector面板勾選, 也可以使用object.enable = true
函式