簡述游戲開發中的狀態機


為什么我們需要狀態機

實行較多狀態的角色,把動作全寫在一個部分中會導致維護成本高,拓展性低
例如:走路,跳躍,射擊,躲避的相互轉換,有些可以轉換,有些不能,實現邏輯復雜
(滿屏幕都是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間的聯系)
主要思想一是包裝更高的層次,二是高層的切換放到更高層來覺得以提高復用
分層狀態機的實現主要分兩種:
一、父狀態就是一個狀態機,可以往里面添加狀態
二、添加一個狀態棧儲存父子狀態,每一個狀態都是棧下一個狀態的子狀態,進入狀態時入棧,結束狀態時出棧並發送一個消息,新棧頂狀態不能處理就再出棧,直到棧頂狀態能處理這個消息為止。

並發狀態機

並發狀態機可以理解為一個對象擁有兩個同時運作的狀態機,兩個狀態機相互獨立(但可以通過修改對象的狀態進行通信),例如我們可以將腿部動作(站立,下蹲,奔跑)和手部動作(持槍,空手,瞄准)分離。

下推狀態機

下推狀態機的核心是他有一個狀態棧(但和層次狀態機實現的棧完全不同,層次狀態機的棧是為了記錄父狀態,這個棧是為了紀錄上一個狀態)。
狀態棧主要解決的是狀態機無法得到上一輪執行的狀態。
實現時,在狀態機中加一個棧,在進入狀態時將狀態入棧,退出狀態時將狀態出棧,運行時則運行棧頂的狀態。
例如,我在做作業的時候餓了,想去吃東西。餓了去吃東西是多數狀態都能進入的狀態,並且結束“吃東西”的狀態后我要回復原來的狀態。

在運行時,狀態機已經將“做作業“入棧
然后“我餓了”,將“吃東西”入棧
“吃東西”結束,將“吃東西”出棧
繼續運行棧頂的“做作業”(事實上不可能)

若有錯誤,歡迎指出


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM