精華區beta GameDesign 關於我們 聯絡資訊
這是 coroutine 系列的最後一篇。相較於狀態機,coroutine 的優勢除了程式 碼比較容易理解外,還有可重覆利用的特性。 在這篇文章中,我們會以對話框作為範例,探討如何讓這個遊戲中的常見功能變 得容易使用。 我們要製作的對話框看起來非常簡單,就像這樣: http://goo.gl/knN7d 這種對話框在遊戲中相當常見,我們先試著用狀態機來實作看看。 使用狀態機的情況 對話框的各個 state 長這樣: A(確定時的下一個 state) 動畫結束 點擊 ↗ 淡入 ---→ 等待 ---→ 淡出 ↘ B(取消時的下一個 state) 因此我們需要定義三個 state。由於程式碼已經超過 100 行,我貼在 codepad 上,但它還是很重要,請不要因此偷懶不看 XD http://codepad.org/TwqXUMBY 理論上,不管在任何 state,只要讓狀態機轉移到 DIALOG_ENTER 這個狀態,就 相當於呼叫了對話框功能,對話框會開始播放動畫,接著移動到 DIALOG_WAIT 狀態等待玩家選擇。然而在對話框結束後我們卻遇到一個困難:既然狀態機中的 任意 state 都可以「呼叫」對話框功能,我們怎麼知道對話框結束後要轉移到哪 一個 state 呢?總不能讓每個對話框都結束遊戲吧? ? ?(確定) ↘ 動畫結束 點擊 ↗ ?-→淡入 ---→ 等待 ---→ 淡出 (下一個狀態取決於由哪個狀態進入) ↗ ↘ ? ?(取消) 簡單而有效的解法是在進入 DIALOG_ENTER 之前,先在兩個全域變數中設定「確定」 與「取消」所對應到的狀態,這麼一來在 DIALOG_LEAVE 就知道下一個 state 是什 麼了。 -- 確定和取消所對應到的下一個 state -- 需要在進入 DIALOG_ENTER 之前的 state 負責設定 ok_state = "fill next state when player hit OK" cancel_state = "fill next state when player hit CANCEL" state_table["DIALOG_LEAVE"] = { ..., update = function() if system.getTimer() > animation_end then if answer then gotoState(ok_state) else gotoState(cancel_state) end end end } 總結來說,在狀態機內若要呼叫對話框功能,需要以下的操作: 1. 設定全域變數 dialog_text 作為對話框的文字。 2. 設定全域變數 ok_state 及 cancel_state 作為玩家選擇後要轉移的 目標 state。 3. 把目前 state 轉移至 DIALOG_ENTER Coroutine 的情況 回過頭來看看使用 coroutine 的情況。它的實作並不難,就只是個長一點的函式: http://codepad.org/6gdHmH1e (雖然說長一點,但才五十多行!真的) 除了流程更加清楚以外,coroutine 還具備以下的優勢: 1. 不需要用全域變數來傳遞參數或取得回傳值。 2. 它只是個函式呼叫,我們不需要知道函式被誰呼叫--它在 return 時會 自然回到呼叫端。 結語 狀態機並非一無可取。在某些沒有結構化流程中,使用 coroutine 改寫反而較為 困難,比如下面這個例子: A → B ↓ ↗ ↓ D → C 然而,使用狀態機就像只用 goto 而不使用函式或迴圈來寫程式,面對更加複雜的 流程就顯得難以建構與維護。遊戲中需要狀態機的場合,大部份都是具有結構化的 流程,因此使用 coroutine 就相當適合。 * * * * * * 斷斷續續寫了快一個月的 coroutine 系列總算告一段落了,文章中程式碼所占的 比例也達到恐怖的程度。原本我曾想過,在文章中加入這麼多程式碼是否洽當, 但若不把程式碼真的寫出來,實在很難理解為何 coroutine 可以讓流程變得清楚 易懂。 另一方面,使用 coroutine 通常也會搭配大量的 function object 與 closure, 希望各位細細品嘗它們的威力。 -- ※ 發信站: 批踢踢實業坊(ptt.cc) ◆ From: 220.135.3.139