為什么我們需要狀態機
實行較多狀態的角色,把動作全寫在一個部分中會導致維護成本高,拓展性低
例如:走路,跳躍,射擊,躲避的相互轉換,有些可以轉換,有些不能,實現邏輯復雜
(滿屏幕都是if - else)
狀態模式switch實現
//包含着所有的狀態
enum class State{StateA, StateB, StateC, ...} activeState;
...
//通過switch語句切換狀態,根據具體情況實現細節
switch (activeState)
{
case State.StateA:
...
break;
case State.StateB:
...
break;
.......
}
...
狀態機的原形,用一個枚舉表示當前的狀態,通過填充完善switch語句實現狀態之間的切換,但是依然有維護成本高拓展低的缺點(雖然確實是比用if - else堆好)
Finite State Machine(FSN)有限狀態機
最基本的狀態機,一般來說其他狀態機都是這種狀態機的變體
對於狀態機的理解,最好就是畫個圖(如圖結構,方框是狀態,箭頭是狀態之間的聯系)
有限狀態機強調的是狀態之間的切換,以及對不同狀態的封裝,所以實現方法一般可以根據需求調整
以下參考了《游戲人工智能》的實現
首先需要個基類State和基類Translation
//狀態基類,所有狀態都繼承這個類
class State
{
public:
virtual ~State(){}
virtual OnStateEnter(){} //進入此狀態執行一次
virtual OnUpdate(){} //每一幀執行一次
virtual OnStateExit(){} //跳出狀態時調用一次
list<Translation> translations; //狀態遷移列表
};
//狀態遷移
class Translation
{
public:
virtual ~Translation(){}
virtual bool isValid() = 0; //用於判定遷移,可切換返回true
virtual State* getNextState() = 0; //進入下一個狀態
virtual void onTransition(){} //遷移時調用
};
State是每一個狀態都會繼承的基類,translations中存着他指向其他狀態的Translation,通過每一幀遍歷所有Translation的isValid()來判定是否可以跳轉,若可以跳轉則執行自身的OnStateExit(),並將當前的狀態設置為getNextState()獲得的狀態
然后還有狀態機類,用來管理所有狀態:
//狀態機類
class FiniteStateMachine
{
public:
void Update(); //每一幀運行一次
State* initialState; //初始狀態
State* activeState; //正在運行的狀態
protected:
list<State> states; //所有狀態的實例
};
void FiniteStateMachine::Update()
{
//遍歷活動狀態的所有遷移,若有可用的,則切換狀態
list<Translation>::iterator itr = activeState->translations.begin();
for (int i = 0; i < activeState->translations.size(); ++i, ++itr)
{
if (itr->isValid())
{
activeState->OnStateExit(); //退出調用
activeState = itr->getNextState(); //切換活動狀態
itr->onTransition(); //切換調用
activeState->OnStateEnter(); //進入調用
return; //直接返回
}
}
//如果沒有狀態切換,運行一次update
activeState->OnUpdate();
}
接下來具體的狀態細節就要具體繼承,具體實現
Hierarchical Finite State Machine (HFSM) 分層狀態機
分層狀態機相當於對有限狀態機的進一步封裝,將復數個狀態封裝成一個大的狀態,再用一個“歷史狀態”記錄切出此狀態時運行的子狀態,就可以在大狀態間切換(結構如圖)
圖中將StateA和StateB加入到一個更高層的狀態StateD中,只需退出時記錄狀態,就可以在CD狀態間切換,增加了狀態C的復用性(省略了AC,BC間的聯系)
主要思想一是包裝更高的層次,二是高層的切換放到更高層來覺得以提高復用
分層狀態機的實現主要分兩種:
一、父狀態就是一個狀態機,可以往里面添加狀態
二、添加一個狀態棧儲存父子狀態,每一個狀態都是棧下一個狀態的子狀態,進入狀態時入棧,結束狀態時出棧並發送一個消息,新棧頂狀態不能處理就再出棧,直到棧頂狀態能處理這個消息為止。
並發狀態機
並發狀態機可以理解為一個對象擁有兩個同時運作的狀態機,兩個狀態機相互獨立(但可以通過修改對象的狀態進行通信),例如我們可以將腿部動作(站立,下蹲,奔跑)和手部動作(持槍,空手,瞄准)分離。
下推狀態機
下推狀態機的核心是他有一個狀態棧(但和層次狀態機實現的棧完全不同,層次狀態機的棧是為了記錄父狀態,這個棧是為了紀錄上一個狀態)。
狀態棧主要解決的是狀態機無法得到上一輪執行的狀態。
實現時,在狀態機中加一個棧,在進入狀態時將狀態入棧,退出狀態時將狀態出棧,運行時則運行棧頂的狀態。
例如,我在做作業的時候餓了,想去吃東西。餓了去吃東西是多數狀態都能進入的狀態,並且結束“吃東西”的狀態后我要回復原來的狀態。
在運行時,狀態機已經將“做作業“入棧
然后“我餓了”,將“吃東西”入棧
“吃東西”結束,將“吃東西”出棧
繼續運行棧頂的“做作業”(事實上不可能)
若有錯誤,歡迎指出