Try   HackMD

[Unity Engine] Coroutine協程

tags: Unity Engine C# Programming

Keynote

  • Coroutine最常做的是『等待』
  • Coroutine並不是多執行緒
  • Coroutine只能使用MonoBehaviour內的StartCoroutine進行呼叫
  • MonoBehaviour的生命週期息息相關

Usage

從上圖可以得知幾種等待方式,並有著呼叫順序,這件事告訴我們,一個MonoBehaviour的循環或幀書內可能進行著N次對Coroutine的回傳與呼叫

最常使用的方式

  • 在固定的循環或時間點中,做一件事
private IEnumerator A() { while (true) { Debug.Log("Do something."); yield null; // 也可以使用其他等待的方式 } }
  • 在一段時間前或後做一件事
private IEnumerator B() { Debug.Log("Do something 1~~."); // Print Do something 1~~. yield return new WaitForSeconds(1.0f); Debug.Log("Do something 2~~."); // Print Do something 2~~. after 1 second }
  • yield break之後的程式不會被執行
private IEnumerator C() { Debug.Log("Do something 1~~."); Debug.Log("Do something 2~~."); Debug.Log("Do something 3~~."); yield break; Debug.Log("Do something 4~~."); // This won't be call }

Nested Coroutine

  • 執行Coroutine A,然後在Coroutine A當中執行並等待Coroutine B完成
void start() { StartCoroutine(A()); } IEnumerator A() { Debug.Log("Start A..."); yield return B(); Debug.Log("Finish A..."); } IEnumerator B() { Debug.Log("Start B..."); yield return new WaitForSeconds(5.0f); Debug.Log("It passed 5 seconds"); Debug.Log("Start B..."); }

Parallel Coroutine

  • 執行Coroutine A,同時處理Coroutine B和Coroutine C,並依序等待執行的Coroutine結束
void Start() { StartCoroutine(A()); } IEnumerator A() { Debug.Log("Start A..."); var b = StartCoroutine(B()); var c = StartCoroutine(C()); yield return b; yield return c; Debug.Log("Finish A..."); } IEnumerator B() { Debug.Log("Start B..."); yield return new WaitForSeconds(2.5f); Debug.Log("It passed 2.5 seconds"); Debug.Log("Finish B..."); } IEnumerator C() { Debug.Log("Start C..."); yield return new WaitForSeconds(2.5f); Debug.Log("It passed 2.5 seconds"); Debug.Log("Finish C..."); }

YieldInstruction

WaitForFixedUpdateWaitForEndFrameWaitForSeconds,這些都是等待固定時間或是幀數,若要進行資源載入或是操作硬體I/O,無法預期要多少時間的話,請使用AsyncOperation衍生類別:

  • SceneManager.LoadSceneAsync():載入場景
  • AssetBundle.LoadAssetAsync():載入 AssetBundle
  • Resources.LoadAsync():從 Resources 目錄載入各種資源

StartCoroutine & StopCoroutine

待補,此段文字來自Reference 2

Start() 執行過程中所啟動的兩個 Coroutine ,實際上都要等到第二幀之後才會真正開始運行。這雖然看起來似乎是個不太重要的現象,但當我們提到 await/async 或是多執行緒時,在哪裡執行就會成為大部分工作都要在主執行緒執行的 Unity 很需要考量的地方。
透過讓 yield 回傳 Coroutine 類別物件,Coroutine 就可以去等待另一個 Coroutine 完成,但是這兩個 Coroutine 之間的關係到底是什麼?是互相隸屬還是個別獨立運作?如果我們將其中一個 Coroutine 用 StopCoroutine() 強制中止時,另外一個會出現什麼反應?是否會發生什麼問題?Unity 在 5.3 之後加入了 CustomYieldInstruction 又會改變些什麼?我們為什麼不建議再讓 yield 用回傳 Coroutine 的方式來等待了?這種種的問題,我們很快回來!

Reference

Unity Coroutine 使用筆記
Coroutine 在等什麼?了解 Unity 的 YieldInstruction 和 Coroutine 類別