//待完善
有限狀態機是一個很常用的技術,在流程控制和游戲AI中都比較實用,因為狀態機編程簡單又很符合直覺。與有限狀態機類似的是設計模式中的狀態模式。本文是參考《Programming Game AI by Example》
一、
記得最開始工作時候也接觸過有限狀態機,當時是一個長長的用switch寫成的狀態機,理解它的時候真的很困難。
所以現在使用一套內置規則到狀態內部去,來控制狀態的轉換。
現在就來制作一個有限狀態機。
作為一個關于使用狀態機創建一個智能體的實際案例,我們先模擬這樣一個場景。是作為一個簡單的基于文本的控制臺應用實現的,所以你將不得不想象遍地的風滾草,嘰嘰嘎嘎的礦井支柱,時有荒漠的灰塵吹進你的眼睛。任何狀態的改變或者狀態動作的輸出將作為文本傳送到控制臺窗口。我使用這種只有普通的文本的方法是因為它能將有限狀態機的機制演示清楚而不會由于更復雜的環境而增加編碼混亂。
而這些位置每一個都代表了一個狀態,因為我們是使用內置規則來控制狀態機的轉換,“我”在到達一個位置之后要干什么,都會由當前所處的狀態和一些屬性值來決定。
BaseGameEntity類,用來作為所有游戲對象的基類,主要為游戲對象提供了一個ID,以及每一幀更新時調用的純虛函數Update。
class BaseGameEntity
{
private:int m_ID; // 每個實體具有一個唯一的識別數字static int m_iNextValidID; //這是下一個有效的ID。每次BaseGameEntity被實例化這個值就被更新void SetID(int val);//在構造函數中調用這個來確認ID被正確設置。在設置ID和增量前,它校驗傳遞給方法的值是//大于還是等于下一個有效的ID。public:BaseGameEntity(int id) { SetID(id); }virtual ~BaseGameEntity() {}virtual void Update() = 0;//所有的實體必須執行一個更新函數int ID() const { return m_ID; }
};
使用一個枚舉類型管理所有可能到達的地點,sweetHome、school、company、beerLady 分別代表家、學校、實習公司、啤酒阿姨這四個地點。在 Me 類中,對“我”所特有的屬性進行了定義,如心情值( m_iMoodForDoingStuffs ),金錢數( m_iMoneyInCard ),能力( m_iAbilityLevel ),疲勞( m_iFatigue ),以及這些屬性的閾值,用來在狀態轉移中起作用。隨后定義的方法表明了這些屬性如何變化,并為其他類查看這些屬性暴露了接口。
enum location_type
{sweetHome,school,company,beerLady,
};class Miner : public BaseGameEntity
{
private:State* m_pCurrentState;location_type m_Location;//指向一個狀態實例的指針State* m_pCurrentState;//礦工當前所處的位置location_type m_Location;//礦工的包中裝了多少天然金塊int m_iGoldcarried;//礦工在銀行存了多少錢int m_iMoneyInBank;//價值越高,礦工越口渴int m_iThirst;//價值越高,礦工越累int m_iFatigue;public:Miner(int ID); void Update(); void ChangeState(State *pNewState);location_type Location()const { return m_Location; }void ChangeLocation(const location_type goal) { m_Location = goal; }int Ability()const { return m_iAbilityLevel; }void SetAbilityLevel(const int val) { m_iAbilityLevel = val; }void AddToAbility(const int val);int MoneyInCard()const { return m_iMoneyInCard; }void SetMoneyInCard(const int val) { m_iMoneyInCard = val; }void ThePayDay(const int val);bool Fatigued()const;bool Rested()const;void DecreaseFatigue(const int val) { m_iFatigue -= val; }void IncreaseFatigue(const int val) { m_iFatigue += val; }bool LowMood()const;void DecreaseMood(const int val) { m_iMoodForDoingStuffs -= val; }void IncreaseMood(const int val) { m_iMoodForDoingStuffs += val; }bool FeelPoor()const;void BuyTheBeer() { m_iMoodForDoingStuffs = Max_Mood; m_iMoneyInCard -= 500; }
};
接下來是狀態 State 類,這是一個純虛類(抽象類),作為狀態對象的一個通用接口。
class State
{
public:virtual ~State(){}virtual void Enter(Miner*) = 0;//當狀態被進入時執行這個virtual void Execute(Miner*) = 0;//每一更新步驟調用virtual void Exit(Miner*) = 0;//退出是執行這個};
在這里使用單例模式實現每一個狀態,一切從簡,不考慮線程安全。分別針對不同的地點,定義在每個地點的狀態類,代碼如下:
class GoWorkAndEarnMoney:public State
{
private:GoWorkAndEarnMoney() = default;
public:static GoWorkAndEarnMoney* Instance();virtual void Enter(Me* pMe);virtual void Execute(Me* pMe);virtual void Exit(Me* pMe);
};class GoSchoolAndStudy:public State
{
private:GoSchoolAndStudy() = default;
public:static GoSchoolAndStudy* Instance();virtual void Enter(Me* pMe);virtual void Execute(Me* pMe);virtual void Exit(Me* pMe);
};class GoHomeAndSleep:public State
{
private:GoHomeAndSleep() = default;
public:static GoHomeAndSleep* Instance();virtual void Enter(Me* pMe);virtual void Execute(Me* pMe);virtual void Exit(Me* pMe);
};class GoBar:public State
{
private:GoBar() = default;
public:static GoBar* Instance();virtual void Enter(Me* pMe);virtual void Execute(Me* pMe);virtual void Exit(Me* pMe);
};