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
這是 coroutine 系列的最後一篇。相較於狀態機,coroutine 的優勢除了程式
碼比較容易理解外,還有可重覆利用的特性。
在這篇文章中,我們會以對話框作為範例,探討如何讓這個遊戲中的常見功能變
得容易使用。
我們要製作的對話框看起來非常簡單,就像這樣: