//待完善
有限状态机是一个很常用的技术,在流程控制和游戏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);
};