本節書摘來異步社區《游戲編程模式》一書中的第7章,第7.8節,作者: 【美】Robert Nystrom (尼斯卓姆) 譯者: 趙衛兵 , 許新星 , 姜召陽 , 陳侃 , 屈光輝 , 鄭炯彬 責編: 陳冀康,更多章節內容可以訪問云棲社區“異步社區”公眾號查看。
7.8 并發狀態機
我們決定給我們的主角添加持槍功能。當她持槍的時候,她仍然可以:跑、跳和躲避等。但是,她也需要能夠在這些狀態過程中開火。
如果你執著于傳統的有限狀態機,那我們可能需要把之前的狀態加倍。對于每一個已經存在的狀態,我們需要定義另一個狀態,它做的事情也差不多,不過就是多了持槍的操作。比如站立狀態和站立開火狀態,跳躍狀態和跳躍開火狀態等。
如果我們添加更多的武器種類,那么這個狀態數量將會急劇增加。而且不僅僅是增加了大量的狀態類實例,它還會增加大量的冗余,實際上帶不帶槍的狀態僅有是否包含開火代碼的區別而已。
這里的問題是,我們把兩種狀態雜合在一起了。我們把兩種不同的狀態硬塞到一個狀態機里面去了。為所有可能出現的組合建模,我們可能需要為每一種狀態準備一組狀態。解決方法比較直觀,就是分開成兩個狀態機。
如果我們需要為主角定義n種狀態和m種它能夠攜帶的武器狀態,如果使用一個狀態機來表示,那么我們需要n×m個狀態。而如果使用兩個狀態機,那么狀態組合僅是n+m。
首先我們可以保留原有的狀態機的代碼和功能不管它。接下來,我們定義一個單獨的狀態機,用來處理主角攜帶的武器。現在,我們的主角會有兩個狀態索引,其中一個看起來如下所示:
為了便于示例說明,我們這里使用了完整的狀態模式來處理女主角的裝備變化。事實上,由于裝備目前只有兩個狀態,我們完全可以只使用一個布爾值變量來替代。
class Heroine
{// Other code...private:HeroineState* state_;HeroineState* equipment_;
};
當主角派發輸入事件給狀態類時,需要給兩種狀態都派發一下。
void Heroine::handleInput(Input input)
{state_->handleInput(*this, input);equipment_->handleInput(*this, input);
}
這樣每一個狀態機都可以響應輸入事件并以此切換狀態而不用考慮其他狀態機的實現細節。當兩個狀態沒什么關系的時候,這種方法工作得很好。
功能更加完備的系統可能會讓一個狀態機來處理輸入,以便另外一個狀態機不會接收到輸入。這樣將能防止兩個狀態機對同一輸入進行錯誤的響應。
在實際中,你可能會發現你需要對某些狀態處理進行干預。比如,如果主角不能夠在跳躍的過程中開火,或者她在裝備武器的時候不能俯沖。為了處理這種情況,在代碼里面,對于每一個狀態,你可能需要做一些簡單的if判斷并做出特殊處理。雖然這可能不是最好的解決方案,但是至少它可以完成任務。