Unity3D 關於Coroutine(協程)使用的研究筆記及關於yield的理解

我先亮出我用的有關協程的代碼:

using UnityEngine;
using System.Collections;

public class NewBehaviourScript : MonoBehaviour {
	//本地圖片的紋理
	private Texture tex0;
	//網絡圖片的紋理
	private Texture tex1;
	//加載本地圖片
	IEnumerator loadLocal()
	{
		if (tex0 == null) {
			WWW date = new WWW("file://" + Application.dataPath + "/0.png");
			yield return null;
			tex0 = date.texture;
		}
		GetComponent<Renderer> ().material.mainTexture = tex0;

	}
	//加載網絡圖片
	IEnumerator loadNetWork()
	{
		if (tex1 == null) 
		{
			WWW date = new WWW ("http://pic39.nipic.com/20140321/17561764_000020626150_2.jpg");
			while (!date.isDone) 
			{
				yield return null;
				Debug.Log ("時間:" + Time.time + " " + "進度:" + date.progress);
			}
			tex1 = date.texture;
		}
		GetComponent<Renderer> ().material.mainTexture = tex1;
	}
	void OnGUI()
	{
		if (GUILayout.Button ("load local pic")) {
			StartCoroutine (loadLocal ());
		}
		if (GUILayout.Button ("load net pic")) {
			StartCoroutine (loadNetWork());
		}
	}
	// Use this for initialization
	void Start () {
		
	}
	
	// Update is called once per frame
	void Update () {
	
	}
}

如上所示,我的目的是通過點擊Button本別加載本地圖片與網絡圖片到Panel上。

既然是加載,那麼加載就需要耗費時間,於是這裏使用了協程,以保證圖片加載完成後,再把圖片賦值給mainTexture;

第一步:

在Button的響應中使用

StartCoroutine(loadNetWork())啓動一個協程程序.

關於StartCoroutine

1.這個函數的參數,必須是一個返回值爲IEnumerator類型的函數。

2.StartCoroutine不是啓動了一個新的線程,而是開啓一個協同程序,默認unity所有代碼都在一個線程中

第二部:

執行loadNetWork函數

IEnumerator loadNetWork()
	{
		if (tex1 == null) 
		{
			WWW date = new WWW ("http://pic39.nipic.com/20140321/17561764_000020626150_2.jpg");
			/*while (!date.isDone) 
			{
				yield return null;
				Debug.Log ("時間:" + Time.time + " " + "進度:" + date.progress);
			}*/
			yield return date;
			tex1 = date.texture;
		}
		GetComponent<Renderer> ().material.mainTexture = tex1;
	}
我通過每句加輸出語句觀察到的現象是,程序執行完yield return date,之後停住了!,然後去執行了主程序中的下面的程序,然後接着執行了tex1 = date.texture;然後圖片就顯示出來了。我覺得好神奇,做了如下實驗:

我把網斷了,然後我發現程序在執行完yield return date後,很長時間沒有反應,然後彈出了錯誤,說的是網絡有問題訪問不到之類的。

我把yield return date, 改成了yield return null;結果是,程序執行下一句了,不過下一句告知紋理還沒有獲取到,請使用isDone判斷是否加載完成再執行(you are trying to load data from a www stream which has not completed the download yet.You need to yield the download or wait until isDone returns true)
那他既然讓我使用isDone那我就使用唄,如下:

WWW date = new WWW ("http://pic39.nipic.com/20140321/17561764_000020626150_2.jpg");
			while (!date.isDone) 
			{
				yield return null;
				Debug.Log ("時間:" + Time.time + " " + "進度:" + date.progress);
			}
			tex1 = date.texture;

然後我發現我打印的進度一直在增長,最後就加載OK了!

於是我想當然的認爲下面這種方式也可以,只要s夠成:

IEnumerator loadNetWork()
	{
		if (tex1 == null) 
		{
			WWW date = new WWW ("http://pic39.nipic.com/20140321/17561764_000020626150_2.jpg");
			/*while (!date.isDone) 
			{
				yield return null;
				Debug.Log ("時間:" + Time.time + " " + "進度:" + date.progress);
			}*/
			yield return new WaitForSeconds(10);
			tex1 = date.texture;
		}
		GetComponent<Renderer> ().material.mainTexture = tex1;
	}
果然成功了。

於是我合理推測,如果是yield return date, 其中我返回的這個date,程序絕對做了處理,也就是說他們認識這個www類型,然後會幫忙進行加載,加載完畢後,才返回。而如果是null,或者wait之類的,由於程序沒有得到date,也就沒法特殊處理了,所以只有等得夠久纔有收穫。

現在我對上面這段函數解釋一番:

這個函數關鍵的就是這麼幾句話:

1)創建WWW對象加載資源

這個資源的加載是個異步的過程,也就是說new WWW(...)執行完了之後,不用等待加載結果,直接執行下一句。正因爲是異步的過程,所以纔有了下面一行代碼;

2)循環判斷date.isDone是否爲true,如果爲true,則說明資源加載完畢,可以執行紋理賦值貼圖過程。

3)如果資源未加載完畢,則執行yield return null。

      關鍵就是解釋這一句,我之前兩年C#編程工作並沒有接觸到這個東西,這次看到後,感嘆一句:這是個什麼神馬?!

      一句話說就是:yield,用於創建生成器,執行時中斷返回迭代器值,並記錄現場,下次從現場處繼續執行。

這裏有兩點:   1.Q:這句話的“下次”是啥時候?

    A:這個總的來說,應該是下一幀,應該是Update執行後。有一個圖可以參考一下:


2.Q:返回的值幹什麼用?

    A:返回的值有很多類型,我引用下面這段來解釋:

Unity官方文檔對coroutine的解釋

Normal coroutine updates are run after theUpdate function returns. Acoroutine is a function that can suspend its execution (yield) until the givenYieldInstruction finishes. Different uses of Coroutines:

 

yield; The coroutine will continue after all Update functionshave been calledon the next frame.

yield WaitForSeconds(2); Continueafter a specified time delay, after all Update functions have been called for theframe.

yield WaitForFixedUpdate(); Continue afterall FixedUpdate has been called on all scripts.

yield WWWContinue aftera WWW download has completed

yield StartCoroutine(MyFunc); Chains the coroutine, and will wait for the MyFunc coroutine to completefirst.

 C#要在yield coroutine之間加上return關鍵字。

看吧,上面這段文字完美的解釋了我關於www的疑問,果不其然,yield有這麼幾種返回類型,其中WWWContinue的用法就是當下載完畢纔會返回的,我也找到了另一處對返回值類型的說明,感覺描述不一樣,但是意思一樣,當個補充吧,如下:

WWW - after Updates happen for all game objects; check the isDone flag. If true, call the IEnumerator's MoveNext() function;

WaitForSeconds - after Updates happen for all game objects; check if the time has elapsed, if it has, call MoveNext();

null or some unknown value - after Updates happen for all game objects; Call MoveNext();

WaitForEndOfFrame - after Render happens for all cameras; Call MoveNext().

我單獨翻譯一下第一條:WWW-當該腳本綁定的所有遊戲對象的Update函數執行完畢後發生,檢查WWW這個返回值的isDone屬性,如果這個屬性爲true,則調用枚舉器的MoveNext函數(這也就意味着,一次yield的過程完成了,可以執行下一句了,當然如果沒有下一句了,moveNext就會返回false,這個枚舉過程就全部結束嘍)


我這篇文章也只是記錄了我的一些研究過程中的問題,只是協程整個解釋中的極小一部分,我在研究過程中共參考了這麼三篇文章,我覺得很值得一讀,讀的順序就可以按照我的下面的順序讀

Unity3D中的Coroutine詳解

http://blog.csdn.net/blues1021/article/details/40959915

這個裏邊簡單例子多,更通俗些,關鍵還有個關於WWW類使用協程的封裝,特別好


簡要分析unity3d中剪不斷理還亂的yield

http://blog.csdn.net/huang9012/article/details/29595747

這個主要是有一些更豐滿的例子,比如關於在對話框中逐個顯示文字啦之類的,還有個對協程原理分析的摘要,也很到位


Unity協程(Coroutine)原理深入剖析再續】

http://www.manew.com/blog-53680-2637.html

這個最後讀吧,因爲確實講的比較深,連關於協程的代碼實現也羅列了,讓人看完更清楚了


今天一天也就研究了個協程,不過研究清楚了,心裏也踏實了才。睡覺去~
發佈了61 篇原創文章 · 獲贊 34 · 訪問量 35萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章