協(xié)程的原理
協(xié)程的核心就是迭代器,在update中每幀去訪問迭代器,當(dāng)條件滿足的時候可以永遠(yuǎn)迭代下去,當(dāng)條件不滿足時迭代就中止腐晾,具體的細(xì)節(jié)系統(tǒng)已經(jīng)幫我們實現(xiàn)。我們只需要定義返回迭代器對象的方法即可丐一。
利用協(xié)程每幀都會調(diào)用的特性藻糖,可以當(dāng)Update方法一樣來使用,分幀去處理邏輯库车,但比Update使用更簡便巨柒。協(xié)程提供了更多特性,比如會保存現(xiàn)場柠衍,暫停后從上次位置繼續(xù)執(zhí)行洋满;還可以隨時中止;
除了Update方法之外多了一個每幀都執(zhí)行的選擇珍坊。一般一個對象想實現(xiàn)每幀都執(zhí)行的邏輯牺勾,要么繼承了MonoBehaviour,要么被父對象調(diào)用自己的Update方法阵漏。用協(xié)程也可以實現(xiàn)這個需求驻民,當(dāng)然翻具,只有MonoBehaviour才有StartCoroutine這接口。
void Start()
{
StartCoroutine(CoroutineUpdate());
}
IEnumerator CoroutineUpdate()
{
while(true)
{
// do something here
yield return null;
}
}
分析上面一段代碼會發(fā)現(xiàn)回还,函數(shù)返回類型是一個實現(xiàn)了迭代器接口的對象裆泳,yield return null 看起來不像是返回了這種類型啊柠硕?其實這一行代碼工禾,在編譯的時候,會被封裝成一個實現(xiàn)了IEnumerator接口的對象蝗柔。
實現(xiàn)協(xié)程管理器
提供一些運(yùn)行與管理協(xié)程任務(wù)的接口帜篇。這樣就不用局限于想使用協(xié)程的時候,必須繼承MonoBehaviour诫咱。說的再簡單點,TaskManager就是一個MonoBehaviour單例洪灯,當(dāng)然提供了更多功能坎缭。比如暫停,繼續(xù)執(zhí)行签钩,停止掏呼,等。
此外協(xié)程铅檩,還可以用同步的寫法實現(xiàn)異步邏輯
void Start()
{
StartCoroutine(TaskA());
StartCoroutine(TastB());
StartCoroutine(TaskC());
}
IEnumerator TaskA()
{
// 加載資源
}
IEnumerator TaskB()
{
// 使用TaskA中生成的資源憎夷,
// 或要調(diào)用的對象依賴于TaskA中的資源加載完成
}
IEnumerator TaskC()
{
// 依賴于TaskB中生成的對象或資源
}
如果不使用協(xié)程,則實現(xiàn)則會類似于如下昧旨,一層套一層
void Start()
{
TaskA();
}
void TaskA()
{
// 加載資源
TaskB();
}
void TaskB()
{
// 使用資源拾给,或調(diào)用依賴于TaskA加載資源的對象
}
void TaskC()
{
// 使用資源,或調(diào)用依賴于TaskB加載資源的對象
}
顯然兔沃,使用協(xié)程改寫異步邏輯蒋得,使得流程更清晰。
協(xié)程的執(zhí)行時機(jī)
一般來說乒疏,協(xié)程第一幀是在Update之后额衙,LateUpdate之前執(zhí)行的。但有個特例就是怕吴,如果是在Start或都Awake中啟動了協(xié)程窍侧,那第一幀時,協(xié)程先執(zhí)行转绷。
void Start()
{
StartCoroutine(Test());
}
IEnumerator TestCoroutine()
{
while(true)
{
Debug.Log("TestCoroutine " + Time.frameCount);
return yield null;
}
}
void Update()
{
Debug.Log("Update " + Time.frameCount);
}
void LateUpdate()
{
Debug.Log("LateUpdate " + Time.frameCount);
}
// 輸出
TestCoroutine 1
Update 1
LateUpdate 1
Update 2
TestCoroutine 2
LateUpdate 2
...
所以伟件,為了使得協(xié)程總是在Update之后運(yùn)行,可以在第一行暫停一幀暇咆,以避免一些潛在的bug锋爪。如下:
IEnumerator TestCoroutine()
{
return yield null;
while(true)
{
// do something
return yield null;
}
}
還有需要注意的是丙曙,MonoBehaviour.enable = false后,協(xié)程還會繼續(xù)執(zhí)行其骄,這一點與Update方法不同亏镰。
但是gameObject.setActive(false)后,協(xié)程會停止執(zhí)行拯爽,而且即使gameObject.setActive(true)后索抓,協(xié)程也不會繼續(xù)執(zhí)行,而是已經(jīng)被停止了毯炮。