淺談Unity3D中的Coroutine及其使用(延時、定時呼叫函式)
一、Coroutine(協程)的概念和本質
在網上的一些資料當中,一直將Coroutine當作一個執行緒來描述,這樣是不準確的。因為Coroutine並不是一個新的執行緒,它仍舊是屬於主執行緒的一部分。Coroutine本質上是一種輕量級的thread,它的開銷會比使用thread少很多。多個Coroutine可以按照次序在一個thread裡面執行,一個Coroutine如果處於block狀態,可以交出執行權,讓其他的Coroutine繼續執行。
而在Unity當中,Coroutine是在所有的Update函式執行完成之後才開始執行的,也就是在LateUpdate()之後執行,主要是用來協助主執行緒進行工作,就像手動添加了一個另外的update()函式在裡面一樣,一般情況下我們用於延時觸發的功能。下面是關於MonoBehavior的執行順序圖,可以看到Coroutine是在LateUpdate之後的。
而我們注意到上圖中,Coroutine左邊標註著幾個yield模組,這個就是我們下面要講的如何使用Coroutine裡的內容了。
二、Coroutine的使用
在Untiy裡面,如果想要將一個函式Fun作為一個Coroutine使用,有兩點是需要注意的 1、Fun的返回型別要為IEnumerator 2、yield的使用 yield ,在Coroutine裡面更像是一個紅綠燈的作用,在滿足緊跟在它後面的條件之前,這個協程會掛起,把執行權交給呼叫它的父函式,滿足條件時就可以執行yield下面的程式碼。大家可以執行一下下面的程式碼看看效果大家應該能得到下面的輸出(本人的Unity版本是5.0.1f1)using UnityEngine; using System.Collections; public class ExampleClass : MonoBehaviour { IEnumerator WaitAndPrint() { // suspend execution for 5 seconds yield return new WaitForSeconds(5); print("WaitAndPrint " + Time.time); } IEnumerator Start() { print("Starting " + Time.time); // Start function WaitAndPrint as a coroutine yield return StartCoroutine("WaitAndPrint"); print("Done " + Time.time); } }
另外大家可以再執行一下下面的程式碼,可以觀察一下看看跟上面的有何異同
using UnityEngine;
using System.Collections;
public class ExampleClass : MonoBehaviour
{
IEnumerator WaitAndPrint()
{
// suspend execution for 5 seconds
yield return new WaitForSeconds(5);
print("WaitAndPrint " + Time.time);
}
void Start()
{
print("Starting " + Time.time);
// Start function WaitAndPrint as a coroutine
StartCoroutine("WaitAndPrint");
print("Done " + Time.time);
}
}
得到的輸出如下:
從上面可以看到,我們是通過StarCoroutine的方法來啟動一個新的Coroutine的,在第一段程式碼當中,把Start方法也宣告成一個IEnumerator型別的時候,當執行到yield 模組,就會進行阻塞,等待yield模組完成了之後再繼續往下執行,而聲名成普通型別的時候,當啟動了新的Coroutine的時候,遇到yield了,並不會把整個Start阻塞,而是會記錄下當前執行的位置,然後返回父函式中執行,每一幀過後會檢測yield是否滿足,當滿足yield條件,則返回到yield後面執行。 在Unity3D中,使用StartCoroutine(string methodName)和StartCoroutine(IEnumerator routine)都可以開啟一個執行緒。區別在於使用字串作為引數可以開啟執行緒並在執行緒結束前終止執行緒,相反使用IEnumerator 作為引數只能等待執行緒的結束而不能隨時終止(除非使用StopAllCoroutines()方法);另外使用字串作為引數時,開啟執行緒時最多隻能傳遞一個引數,並且效能消耗會更大一點,而使用IEnumerator 作為引數則沒有這個限制。
在Unity3D中,使用StopCoroutine(string methodName)來終止一個協同程式,使用StopAllCoroutines()來終止所有可以終止的協同程式,但這兩個方法都只能終止該MonoBehaviour中的協同程式
還有一種終止Coroutine的方法,將gameObject的active屬性設為False,但是設回true的時候並不會啟動Coroutine。
注意:將Coroutine所在的指令碼設為enable = false並不能夠將Coroutine停止,因為它跟Monobehavior是同層級的。