原文鏈接:https://blog.csdn.net/u011397120/article/details/61236055
本文是個人對Unity協程的一些理解和總結.Unity協程長的有點像線程,但卻不是線程.因爲協程仍然是在主線程中執行,且在使用時不用考慮同步與鎖的問題.協程只是控制代碼等到特定的時機後再執行後續步驟.
啓動協程
Unity 5.x中使用StartCoroutine方法開啓協程,其方式有以下幾種.
//形式一
StartCoroutine(CustomCorutineFn());
StartCoroutine(CustomCorutineFn(7));//向方法中傳遞參數
//形式二
StartCoroutine(“CustomCorutineFn”);
StartCoroutine(“CustomCorutineFn”,7);//向方法中傳遞參數
以上兩種形式都可開起當前MonoBehaviour對象的協程,注意:當你想停止MonoBehaviour對象中的某個協程時,開啓與停止協程需要使用相同的形式,不可混合使用.
協程中有多種等待方式,例如:等到FixedUpdate結束後才執行,代碼如下.
bool canExcute = true;
void FixedUpdate()
{
if (canExcute)
{
Debug.Log("FixedUpdate" );
}
}
IEnumerator Corutine_WaitForFixedUpdate()
{
yield return new WaitForFixedUpdate();
Debug.Log(string .Format("====>{0} time:{1}", 1, Time .time));
yield return new WaitForFixedUpdate();
Debug.Log(string .Format("====>{0} time:{1}", 2, Time .time));
}
輸出結果如下.
官方文檔Monobehaviour的函數執行順序圖,就對協程再次執行的時機做了很好的描述.
以上只是一個示意圖,詳細信息請看官方文檔.
鏈接https://docs.unity3d.com/Manual/ExecutionOrder.html
yield null:協程將在下一幀所有腳本的Update執行之後,再繼續執行.
yield WaitForSeconds:協程在延遲指定時間,且當前幀所有腳本的 Update全都執行結束後才繼續執行.
yield WaitForFixedUpdate:協程在所有腳本的FixedUpdate執行之後,再繼續執行.
yield WWW:協程在WWW下載資源完成後,再繼續執行.
yield StartCoroutine:協程在指定協程執行結束後,再繼續執行.
WaitForSecondsRealtime:與WaitForSeconds類似,但不受時間縮放影響.
WaitWhile:當返回條件爲假時才執行後續步驟.
協程的執行也會受到其他因素的影響,例如:當時間縮放值Time.timeScale被修改後,放大或者縮小.FixedUpdate 方法會受影響,則WaitForFixedUpdate也會跟着受影響;當Update方法中同步加載較大的對象時,WaitForSeconds所指定的時間就可能會與實際的時間不一致.所以在執行協程等待時,要視情況而定.
多個gameObject對象開啓協程,執行順序又是如何呢?
假如場景中存在A,B兩個gameObject對象,均使用WaitForFixedUpdate方式等待,則等待執行的部分,會在A,B兩個對象的FixedUpdate都執行結束後,纔開始執行當前幀後續可執行的部分.源碼如下:
void Awake()
{
StartCoroutine(Corutine_WaitForFixedUpdate());
}
IEnumerator Corutine_WaitForFixedUpdate()
{
Debug.Log(string.Format("{1} : {0}", 0, name));
yield return new WaitForFixedUpdate();
Debug.Log(string.Format("{1} : {0}", 1, name));
}
bool canExcute = false;
void FixedUpdate()
{
if (!canExcute)
{
canExcute = true;
Debug.Log(name + " FixedUpdate");
}
}
執行後,輸出結果入下:
停止協程
在開發中可能會開啓多個協程,如果你想停止其中某個協程,你可使用StopCoroutine.但在使用時,你需要注意一點,停止協程的方式要與開啓協程的方式一致.StopCoroutine(“CustomCorutineFn”)必須與StartCoroutine(“CustomCorutineFn”)成對使用,與StartCoroutine(CustomCorutineFn())一起使用則完全無效.
通過StopCoroutine的重載方法可知道,還有兩種方式可停止協程.在此舉個例子,如下:
IEnumerator cor;
void Awake()
{
//注意保存IEnumerator變量.
cor = Corutine_WaitForFixedUpdate();
StartCoroutine(cor);
StartCoroutine(Corutine_Stop());
}
IEnumerator Corutine_WaitForFixedUpdate()
{
Debug.Log(string .Format("A : {0}", 0));
yield return new WaitForEndOfFrame();
Debug.Log(string .Format("A : {0}", 1));
}
IEnumerator Corutine_Stop()
{
yield return new WaitForFixedUpdate();
//通過cor停止協程
//而不是this.StopCoroutine(Corutine_WaitForFixedUpdate());
this.StopCoroutine(cor);
}
如果想停止多個協程,可使用StopAllCoroutines方法,但這種方法只能停止當前MonoBehaviour類實例中所有協程.其他不受影響.
如果想停止gameObject上所有腳本組件中的協程,禁用腳本組件是無法停止協程的,只需要禁用gameObject即可.
如何用協程的輸出10組計數(每組5次,每次1秒)?
源碼如下:
using System.Collections;
using UnityEngine;
public class mCorutine : MonoBehaviour
{
public float time = 1;
void Awake()
{
StartCoroutine(FirstLayerCorutine());
}
IEnumerator FirstLayerCorutine()
{
for (int i = 0; i < 10; i++)
{
Debug.Log(string .Format("第{0}組", i + 1));
yield return StartCoroutine(SecondLayerCorutine());
}
}
IEnumerator SecondLayerCorutine()
{
for (int i = 0; i < 5; i++)
{
Debug.Log(string .Format("{0}", i + 1));
yield return new WaitForSeconds(time);
}
}
}
協程的使用還有很多要注意的地方,在這裏分享一個關於協程運行時的監控和優化的鏈接.
http://gulu-dev.com/post/perf_assist/2016-12-20-unity-coroutine-optimizing