HOME ABOUT CONTACT

1. 前提top

動畫控制是兩大主題合併,由於初期先大略地把動畫和控制的系統架構弄起來,目前還不是很完整,且目前專注在開發控制的階段,因此接下來的記錄會先是以控制為主。

這篇主要以「角色移動」來記錄兩個控制設計的問題,以及最後如何解決和架構設計。第一個問題是角色在快速轉向的時候,會有人物卡住的現象,第二個問題是在解決第一個問題之後,角色轉向會有延遲問題。

2. 問題描述top

角色移動我們以橫軸移動為例,按下 D 鍵時,人物會往右邊移動,反之按下 A 鍵時,人物會往左邊移動,而第一個人物在轉向的時候會卡住,其原因在於例如我們按著 D 鍵時,人物會持續往右走,然後快速放開 D 鍵並按下 A 鍵時,人物要往左走,我們預設的輸入指令會像是這樣:


D鍵按著
(人物往右) -> D鍵放開 (人物閒置) ->
A鍵按下 (人物往左) -> A鍵按著 (人物往左)
                    

而人物轉向移動會卡住的原因是,常常我們會意外地輸入成:


D鍵按著 (人物往右)
-> A鍵按下 (人物方向產生衝突) -> D鍵放開 (人物閒置) ->
A鍵按著 (人物往左)
                    

很明顯地,中間出現了衝突與人物閒置的狀況,造成我們看到人物會卡住一小段時間,然後才繼續往指定的方向走。

另外還有一個問題是,SDL 在接收按鍵持續被按著的事件之前,也會有一小段的延遲時間,這就會顯得問題更加嚴重。

而第二個問題是,在解決第一個問題之後,出現轉向移動的延遲,如下動圖: Game Engine Input and Move Problem

3. 解決方法與架構設計top

第一個問題的初步解決方法是,我們不能依賴每次動畫幀迴圈接收到的 Keyboard Event 來作為動作變化的依據,而是記錄每個鍵盤按鍵的狀態來作為判斷依據,我們可以用以下的方式來儲存狀態:


std::unordered_map<key, bool>
                    

每個 Key 用布林值來表示按鍵是否有被按下,然後事件處理者去訪問這個結構來作為動作變化的判斷。

然而,架構設計中還存在一個致命的問題,那就是事件接收者和處理者不應該綁在一塊,也就是說,事件處理者不應該在事件接收到之後,才開始判斷是否執行命令,而是在每次的動畫幀迴圈裡都要判斷是否執行命令,以下面的程式碼來做簡單的描述:


while (true) { // 每次的動畫幀迴圈
    poll_event() // 輪訪事件 -> 判斷事件是否執行命令
    ...
}

while (true) {
    poll_event() // 輪訪事件
    handler->execute() // 不管有無新事件接收,都應該進行判斷是否執行命令
}
                    

總結上述的解決方法,我用以下的 UML 來簡單地描述我的架構設計:

主要分為三個角色,分別是 Handlers、Keyboard Event Handler 和 Event,Handlers 是最上層的事件接收者,它主要負責將接收到的事件做分類,再派遣底下個別的 Handler 去執行。注意這個命名最後有加 s,代表著多個 Handler 的管理者,由於事件不會只有 Keyboard Event,還會有 Window Event 等等,因此未來可能還有其他類型的 Handler。

Handler 與 Event 彼此間為雙向關聯,Handler 以 Command Pattern 的方式,掌管著當特定條件達成時,需要被執行的 Event(或稱 Process)。而每個 Event 也都掌握著執行自己的 Handler 指標來達到雙向關聯。

實作的結果如下動圖: Game Engine Input and Move Solution

Last updated: