這是 coroutine 系列的最後一篇。相較於狀態機,coroutine 的優勢除了程式碼比較容易理解外,還有可重覆利用的特性。
在這篇文章中,我們會以對話框作為範例,探討如何讓這個遊戲中的常見功能變得容易使用。
我們要製作的對話框看起來非常簡單,就像這樣:
這種對話框在遊戲中相當常見,我們先試著用狀態機來實作看看。
使用狀態機的情況
對話框的各個 state 長這樣:
因此我們需要定義三個 state 如下:
|
|
理論上,不管在任何 state,只要讓狀態機轉移到 DIALOG_ENTER
這個狀態,就相當於呼叫了對話框功能,對話框會開始播放動畫,接著移動到 DIALOG_WAIT
狀態等待玩家選擇。然而在對話框結束後我們卻遇到一個困難:既然狀態機中的任意 state 都可以「呼叫」對話框功能,我們怎麼知道對話框結束後要轉移到哪一個 state 呢?總不能讓每個對話框都結束遊戲吧?
簡單而有效的解法是在進入 DIALOG_ENTER
之前,先在兩個全域變數中設定「確定」與「取消」所對應到的狀態,這麼一來在 DIALOG_LEAVE
就知道下一個 state 是什麼了。
|
|
總結來說,在狀態機內若要呼叫對話框功能,需要以下的操作:
- 設定全域變數
dialog_text
作為對話框的文字。 - 設定全域變數
ok_state
及cancel_state
作為玩家選擇後要轉移的目標 state。 - 把目前 state 轉移至
DIALOG_ENTER
Coroutine 的情況
回過頭來看看使用 coroutine 的情況。它的實作並不難,就只是個長一點的函式:
|
|
除了流程更加清楚以外,coroutine 還具備以下的優勢:
- 不需要用全域變數來傳遞參數或取得回傳值。
- 它只是個函式呼叫,我們不需要知道函式被誰呼叫--它在 return 時會自然回到呼叫端。
結語
狀態機並非一無可取。在某些沒有結構化流程中,使用 coroutine 改寫反而較為困難,比如下面這個例子:
然而,使用狀態機就像只用 goto 而不使用函式或迴圈來寫程式,面對更加複雜的流程就顯得難以建構與維護。遊戲中需要狀態機的場合,大部份都是具有結構化的流程,因此使用 coroutine 就相當適合。
斷斷續續寫了快一個月的 coroutine 系列總算告一段落了,文章中程式碼所占的比例也達到恐怖的程度。原本我曾想過,在文章中加入這麼多程式碼是否洽當,但若不把程式碼真的寫出來,實在很難理解為何 coroutine 可以讓流程變得清楚易懂。
另一方面,使用 coroutine 通常也會搭配大量的 function object 與 closure,希望各位細細品嘗它們的威力。