必讀好文推薦:
上面的文章說得太透徹,所以這里就記一下自己的學習筆記了。
首先要說明的是,協程並不是線程,協程是運行在主線程中的,是和主線程同步執行的代碼,不同的地方是運行的方法可以被yield return在當前幀進行打斷,到下一幀后可以繼續從被打斷的地方繼續運行。
下面我們看一個示例,場景中有一個空的GameObject對象,其綁定了下面的腳本:
1 using UnityEngine;
2 using System.Collections;
3
4 public class Test : MonoBehaviour
5 {
6 int frame = 0;
7
8 void Start ()
9 {
10 this.StartCoroutine(CountDown());
11 }
12
13 void Update ()
14 {
15 Debug.Log("Now is frame: " + (++frame));
16 }
17
18 IEnumerator CountDown()
19 {
20 Debug.Log("step - 1");
21 yield return null;
22 Debug.Log("step - 2");
23 yield return null;
24 Debug.Log("step - 3");
25 yield return null;
26 Debug.Log("step - 4");
27 }
28 }
下面是執行的結果:

下面我們看看運行的邏輯是如何的:
當進入Start方法時開始啟動協程,這時候協程開始運行,輸出“step1”后遇到第一個yield return后暫停本幀的運行,接下來進入Update方法輸出“frame1”,由於協程調用是在Update之后,所以第二幀開始后,先執行了第二個Update輸出“frame2”,然后從協程的上次暫停處繼續執行,輸出“step2”后遇到第二個yield return后暫停本幀的運行,如此反復,當輸出“step4”后發現方法已經執行完畢,協程結束。
下面看看yield break的效果,這個語句會立即中斷協程的運行,代碼如下:
1 using UnityEngine;
2 using System.Collections;
3
4 public class Test : MonoBehaviour
5 {
6 int frame = 0;
7
8 void Start ()
9 {
10 this.StartCoroutine(CountDown());
11 }
12
13 void Update ()
14 {
15 Debug.Log("Now is frame: " + (++frame));
16 }
17
18 IEnumerator CountDown()
19 {
20 Debug.Log("step - 1");
21 yield return null;
22 Debug.Log("step - 2");
23 yield return null;
24 Debug.Log("step - 3");
25 yield break;
26 Debug.Log("step - 4");
27 }
28 }
下面是運行的結果:

我們可以發現“step4”已經運行不到了。
yield的返回值,我們可以返回null或者數字0,效果是一致的,同時還可以返回3個對象,分別如下:
yield return new WaitForFixedUpdate();
·等待直到下一個固定幀速率更新函數。
yield return new WaitForEndOfFrame();
·等待直到所有的攝像機和GUI被渲染完成后,在該幀顯示在屏幕之前。
yield return new WaitForSeconds(1);
·在給定的秒數內,暫停協同程序的執行。
下面我們來看一個例子,修改第一個例子的Test.cs:
1 using UnityEngine;
2 using System.Collections;
3
4 public class Test : MonoBehaviour
5 {
6 int frame1 = 0;
7 int frame2 = 0;
8 int frame3 = 0;
9
10 void Start ()
11 {
12 this.StartCoroutine(CountDown());
13 this.StartCoroutine(CountDown_WaitForFixedUpdate());
14 this.StartCoroutine(CountDown_WaitForEndOfFrame());
15 this.StartCoroutine(CountDown_WaitForSeconds());
16 }
17
18 void Update ()
19 {
20 Debug.Log("Update is frame: " + (++frame1));
21 }
22
23 void FixedUpdate ()
24 {
25 Debug.Log("FixedUpdate is frame: " + (++frame2));
26 }
27
28 void LateUpdate ()
29 {
30 Debug.Log("LateUpdate is frame: " + (++frame3));
31 }
32
33 IEnumerator CountDown()
34 {
35 Debug.Log("yield - step - 1");
36 yield return null;
37 Debug.Log("yield - step - 2");
38 yield return null;
39 Debug.Log("yield - step - 3");
40 }
41
42 IEnumerator CountDown_WaitForFixedUpdate()
43 {
44 Debug.Log("yield WaitForFixedUpdate - step - 1");
45 yield return new WaitForFixedUpdate();
46 Debug.Log("yield WaitForFixedUpdate - step - 2");
47 yield return new WaitForFixedUpdate();
48 Debug.Log("yield WaitForFixedUpdate - step - 3");
49 }
50
51 IEnumerator CountDown_WaitForEndOfFrame()
52 {
53 Debug.Log("yield WaitForEndOfFrame - step - 1");
54 yield return new WaitForEndOfFrame();
55 Debug.Log("yield WaitForEndOfFrame - step - 2");
56 yield return new WaitForEndOfFrame();
57 Debug.Log("yield WaitForEndOfFrame - step - 3");
58 }
59
60 IEnumerator CountDown_WaitForSeconds()
61 {
62 Debug.Log("yield WaitForSeconds - step - 1");
63 yield return new WaitForSeconds(1 / 60 * 3);//大概是三幀的時間
64 Debug.Log("yield WaitForSeconds - step - 2");
65 yield return new WaitForSeconds(1 / 60 * 3);
66 Debug.Log("yield WaitForSeconds - step - 3");
67 }
68 }
運行的結果如下,有點長,我就弄成兩張圖了:


通過輸出我們可以得出下面的結果:
- 當幀數波動時,FixedUpdate會進行多次補幀處理,我們可以發現兩張圖之間FixedUpdate從3一直補幀到15;
- WaitForFixedUpdate表示協程是跟在FixedUpdate之后執行的;
- WaitForEndOfFrame表示協程是跟在LateUpdate之后執行的;
- WaitForSeconds額。。。不用多說了,你指定多久后執行就多久后執行,當然由於是基於幀運算的,所以可能會不准確;
最后補一張開頭博客的運行順序圖:


