1. 程式人生 > >Unity內建協程詳解

Unity內建協程詳解

本篇教程主要講解了如何使用Unity中的協程。 •        介紹 •        Part 1. 同步等待 •        Part 2. 非同步協程 •        Part 3. 同步協程 •        Part 4. 並行協程 •        總結介紹 每個Unity指令碼都有兩個重要的功能:開始和更新。 前者的作用是當一個物件被建立後,在每一幀對後者進行呼叫。設計規定,下一幀只有更新結束才能開始。這樣會出現一個設計侷限:更新的持續時間不太容易超過一幀。 坦白而言,每個你能想到的自定義行為都能用開始和更新進行實現。然而,基於多幀的事件發生要難實現一些(例如動畫,對白,等待,…)。這是因為其設定無法寫入一個持續的流中,必須分段,分佈在很多幀中。這往往讓程式碼難寫,維護也很困難。 如果能在短暫的單幀中不受任何約束那就非常完美了。如果你是程式設計師,那麼肯定知道執行緒的概念。執行緒是並行執行的程式碼段,使用執行緒需要謹慎。這是因為當多執行緒不加限制地共享一個變數會出現

問題。Unity的設計並不建議使用執行緒。然而,Unity提供了折中的方案:協程。協程持續超過一幀的時間。此外,協程可以在任意情況下中斷和恢復執行。 協程是常規的C#函式,返回一個IEnumerator。為了執行協程(並不同於以往的函式),必須使用StartCoroutine 方法(UnityDoc)。例如:  

[C#] 純文字檢視 複製程式碼

?

1

2

3

4

5

6

7

8

9

void Start ()

{

// Execute A as a coroutine

StartCoroutine( A() );

}

IEnumerator A ()

{

...

}

將A當做協程執行。StartCoroutine 方法立即終止,同時產生新的協程並行執行。同步等待 如果你之前用過協程,那麼應該已經遇到過WaitForSeconds (UnityDoc)。像繼承YieldInstruction的其它類,它允許協程短暫的暫停。當用yield進行連線時, WaitForSeconds提供了一種方式去延遲剩餘程式碼的執行。 下面的程式碼展示瞭如何使用協程:   

[C#] 純文字檢視 複製程式碼

?

1

2

3

4

5

6

7

IEnumerator A()

{

...

yield return new WaitForSeconds(10f);

...

}

UML 的序列圖如上(Wikipedia),驗證了WaitForSeconds的作用。當呼叫協程(即呼叫A),它暫停執行,直到消耗一定的時間。這個等待稱為同步,因為協程等待另一個操作的完成。非同步協程 Unity還允許在現有協程中開啟一個新的協程。最簡單的方法就是使用StartCoroutine。這麼呼叫的話,新生的協程會和以前的協程共存。它們不發生直接互動,最重要的是它們不會相互等待。與之前的同步等待相比,這種情況是非同步的,兩個協程不要試圖保持同步。  

[C#] 純文字檢視 複製程式碼

?

1

2

3

4

5

6

7

8

IEnumerator A()

{

...

// Starts B as a coroutine, and continue the execution

StartCoroutine( B() );

...

}

需要注意的是,在這個例子中B是一個完全獨立的協同程式。終止不會影響B,反之亦然同步協程 可以執行巢狀的協程並等待其實行完畢。最簡單的辦法就是使用yield返回。   

[C#] 純文字檢視 複製程式碼

?

1

2

3

4

5

6

7

8

IEnumerator A()

{

...

// Waits for B to terminate

yield return StartCoroutine( B() );

...

}

值得注意的是,由於執行B期間暫停了A,這種特殊情況下不需要啟動另一個協程。有人可能會像下面這樣試圖優化協程:   

[C#] 純文字檢視 複製程式碼

?

1

2

3

4

5

6

7

8

IEnumerator A()

{

...

// Executes B as part of A

B();

...

}

B的執行和普通函式有一樣的效果,不同的是B是在單幀內執行的。相反,通過使用StartCoroutine,A已經暫定的同時下一幀可以開始進行。 引入這個例子是為了介紹更加複雜的例子,同步協程。並行協程 當協程通過StartCoroutine啟動時,返回了一個特殊的物件。 這可以用來查詢協程的狀態,隨時等待其結束。 下面的例子中,協程B是非同步執行的。父類A可以繼續執行直到B需要的時候。如果有必要,它可以為了同步等待讓步於B的引用。   

[C#] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

IEnumerator A()

{

...

// Starts B as a coroutine and continues the execution

Coroutine b = StartCoroutine( B() );

...

// Waits for B to terminate

yield return b;

...

}

如果你想要開始幾個並行協程的話這會非常有用,所有程式碼在同一刻執行:   

[C#] 純文字檢視 複製程式碼

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

IEnumerator A()

{

...

// Starts B, C, and D as coroutines and continues the execution

Coroutine b = StartCoroutine( B() );

Coroutine c = StartCoroutine( C() );

Coroutine d = StartCoroutine( D() );

...

// Waits for B, C and D to terminate

yield return b;

yield return c;

yield return d;

...

}

這種新模式允許任意數量的平行計算,當這些平行計算終止時恢復執行。總結 這篇文章展示了通過有效利用協程的幾種不同的模式來完成你的遊戲。本系列接下來的文章將集中在如何擴充套件協程來支援自定義的等待和事件。