有限状态机(FSM)

Finite State Machine(FSM)是一种人工智能的实现方式,对象在不同的状态间转换,且某一时刻只能处于一个状态;FSM中对象的行为是由我们自己设置的流程, 在加入消息功能后, 不同对象之间可以通过消息通信的方式来完成互动, 因此会显然非常只能;


案例

参考<<游戏人工只能编程案例精粹>>,这里写了一个类似的渔夫与妻子的一天的简单生活案例;

状态 state

单例模板如下, 程序中状态都设计为单例,这样转换过程不用重新生成释放对象;

template<typename T>
class Singleton
{
protected:
    Singleton(){};
    ~Singleton(){};
    static T* pInstance;
public:
    static T* Instance()
    {
        if(!pInstance)
            pInstance = new T();
        return pInstance;
    }
};
template<typename T>
T* Singleton<T>::pInstance = 0;

参考<<设计模式>>状态模式, 设计了了state基类, 考虑到状态对于渔夫和妻子会不同,所以这里的state采用模板类;

template<typename T>
class State
{
public:
    State(void){};
    virtual ~State(void){};
    virtual void vExecute(T* afisher)=0;
    virtual void vEnter(T* afisher)=0;
    virtual void vExit(T* afisher)=0;
};

基于上面的基类, 我们可以将渔夫与妻子的不同具体状态继承于base;如:

// 渔夫的状态
class FishingState : public State<Fisher>,public Singleton<FishingState>
{
public:
    virtual void vExit(Fisher* afisher);
    virtual void vExecute(Fisher* afisher);
    virtual void vEnter(Fisher* afisher);
};
class EatFoodState :public State<Fisher>,public Singleton<EatFoodState>
{
public:
    //....
};
class GoHomeState : public State<Fisher>,public Singleton<GoHomeState>
{
public:
    //....
};

// 渔夫妻子的状态
class WfWashState : public State<FisherWife>,public Singleton<WfWashState>
{
public:
    virtual void vExit(FisherWife* afisher);
    virtual void vExecute(FisherWife* afisher);
    virtual void vEnter(FisherWife* afisher);
};
class WfCookState : public State<FisherWife>,public Singleton<WfCookState>
{
public:
    //....
};
class WfBashRoom : public State<FisherWife>,public Singleton<WfBashRoom>
{
public:
    //....
};

状态管理类

一个对象身上只要有一个状态管理类, 里面放者当前状态, 全局状态, 上一个状态;之所以要全局状态, 是为了处理一些全程可能转换的状态,如渔夫唱歌, 在家可以, 打渔也可以, 如果在每个状态中写上唱歌的状态转换就重复也比较麻烦, 添加全局状态可以在tick函数中直接使用, 就免去了上面的麻烦,; 而上一个状态则可以用来处理返回, 即如唱完歌应当回到当前状态而不是直接进入下一个状态;
statemachine采用模板来处理, 对于渔夫与妻子传入相应的类型进去:

// StateMachine.h
template<typename T>
class StateMachine
{
    public:
        inline State<T>* GetCurState() { return mCurState; }
        // .... another get set function
        void ChangeState(State<T>* pst);
        void Tick();
    private:
        T* mOwner;
        State<T>* mCurState;
        State<T>* mPreState;
        State<T>* mGlobalState;
};
// template function need defined in the .h file
template<typename T>
void StateMachine<T>::ChangeState(State<T>* pst)
{e->vExit(mOwner);
    mCurState = pst;
    mCurState->vEnter(mOwner);
}
template<typename T>
void StateMachine<T>::Tick()
{
    if(mGlobalState)
        mGlobalState->vExecute(mOwner);
    if(mCurState)
        mCurState->vExecute(mOwner);
}

对象基类

struct Telegram; //通信包结构
class GameObject
{
private:
    int mId; //每个对象的唯一识别ID
public:
    GameObject(int Id):mId(Id){};
    virtual ~GameObject(void);
    inline int GetId(){return mId;}
    virtual void Tick() = 0; //每帧调用的函数
    virtual void HandleMessage(const Telegram& pMsg) = 0; //消息处理函数
};

渔夫与妻子都继承并实现该类:

// fisher
class Fisher:public GameObject
{
private:
    State<Fisher>* mCurState; //当前状态
    int mNumFish; // 鱼数量
    int mMoney; 
    int mFatigue; // 体力
public: 
    void FindFish(); // 打渔
    void SaleFish(); // 卖鱼
    inline bool  FishBinFull(){return mNumFish >= MAX_NUM_FISH;}
    void ChangeState(State<Fisher>* st);
    /* 其它函数 */
    virtual void Tick();
    virtual void HandleMessage(const Telegram& pMsg);
    Fisher(int id);
};
// fisherwife
class FisherWife : public GameObject
{
    public:
        FisherWife(int id);
        virtual void Tick();
        inline StateMachine<FisherWife>* GetFSM(){return mFSM;}
        virtual void HandleMessage(const Telegram& pMsg);
    private:
        StateMachine<FisherWife>* mFSM;
};

渔夫和妻子的实现代码如下:

// fisher
void Fisher::Tick()
{
 #ifdef _WIN32
    SetTextColor(FOREGROUND_RED|FOREGROUND_INTENSITY);
 #endif
    if(mCurState)
        mCurState->vExecute(this);
 #ifndef _WIN32
    cout<<endl;
 #endif
}
/* other function */
// state change
void Fisher::ChangeState(State<Fisher>* st)
{
    mCurState->vExit(this);
    mCurState = st;
    mCurState->vEnter(this);
}
// msg is dealed in fisher class but state class
void Fisher::HandleMessage(const Telegram& pMsg)
{
    switch(pMsg.Id)
    {
    case MSG_BACKHOME:
        break;
    case MSG_BREADOK:
        break;
    case MSG_FOODREADY:
        cout<<"Thank you my  dear for making the meal!"<<endl;
        ChangeState(GEatFoodState);
        break;
    default:
        cout<<"Can't deal MSG"<<endl;
    }
}
// fisher wife
void FisherWife::Tick()
{
 #ifdef _WIN32
    SetTextColor(FOREGROUND_GREEN|FOREGROUND_INTENSITY);
 #endif
    GetFSM()->Tick();
 #ifndef _WIN32
    cout<<endl;
 #endif
}
void FisherWife::HandleMessage(const Telegram& pMsg)
{
    switch(pMsg.Id)
    {
    case MSG_BACKHOME:
        cout<<"o my dear, you are back home!, you must be hungry, let me make meal for you !"<<endl;
        GetFSM()->ChangeState(GWfCookState);
        break;
    case MSG_BREADOK:
        cout<<"oh, my dear, the bread is OK!"<<endl;
        GMsgDisptchr->DispatchMessage(0, FISHER_WIFE,FISHER,MSG_FOODREADY,NULL, 0);
        break;
    case MSG_FOODREADY:
        break;
    default:
        cout<<"Can't deal MSG"<<endl;
    }
}

全局对象管理

该类是单例类, 管理了所有的对象,通过ID来识别; 有了这个管理类, 在派发消息时就可以通过ID来获得对象指针, 从而通知对象;

class ObjectManager : public Singleton<ObjectManager>
{
    public:
        ObjectManager();
        virtual ~ObjectManager();
        GameObject* FindObjpt(int id);
        void Remove(GameObject* ply);
        void RegisterObj(GameObject* ply); // 生成新对象时调用这个注册到管理器
    private:
        std::map<int,GameObject*> AllGmObjMap;
};

消息类

消息结构

struct Telegram
{
    MSG_ID Id; //the defined msg id in a enum structure
    int Receiver;
    int Sender;
    int MsgLen;
    int DispatchTime; // mark the time that the msg is send (for delay send msg)
    void* Data;
    Telegram(MSG_ID id):Id(id), Receiver(-1), Sender(-1), MsgLen(0), DispatchTime(0), Data(NULL)
    { }
    // overload the ==,  > symbol because the msg will put in a priority queue
    inline bool operator==(const Telegram& t1)
    {
        return DispatchTime ==  t1.DispatchTime;
    }
};
// 由于比较, 以时间为优先队列的排列顺序
inline bool operator<(const Telegram& t1,const Telegram& t2)
{
        return t1.DispatchTime < t2.DispatchTime;
}

消息派发类
将需要立即处理的消息在当前帧就调用; 将延迟发送的消息存储到一个消息队列中,在每个主循环中遍历一遍,如果时间过期了则立刻处理;

class GameObject;
class MessageDispatcher : public Singleton<MessageDispatcher>
{
    public:
        MessageDispatcher();
        virtual ~MessageDispatcher();
        // immediate msg be send
        void DispatchMessage(const int delay, const int Sender,
                             const int Receiver, const MSG_ID msg, void* pInfo, const int len);
        // delay send msg , detected in each main tick
        void DispatchDelayMessage();
        void Discharge(GameObject* pRecv, const Telegram& msg);
    protected:
    private:
        std::set<Telegram>  mMsgQueue; //use as a priority queue
};

下面是几个个主要的消息处理派发函数:

// 通过对象指针, 让对象调用消息处理函数来处理当前的消息
void MessageDispatcher::Discharge(GameObject* pRecv, const Telegram& msg)
 {
        assert(pRecv);
        pRecv->HandleMessage(msg);
 }
// immediate msg be send
void MessageDispatcher::DispatchMessage(const int delay, const int Sender,
                     const int Receiver, const MSG_ID msgId, void* pInfo, const int len)
{
    GameObject* pReceiver = GObjMgr->FindObjpt(Receiver);
    if(!pReceiver)
    {
        cout<<"Not Find Message Receiver!"<<endl;
        return;
    }
    Telegram msg(msgId);
    msg.MsgLen = len;
    msg.DispatchTime = -1;
    msg.Receiver = Receiver;
    msg.Sender = Sender;
    msg.Data = pInfo;
    // not delay
    if(delay <= 0)
    {
        cout<<" receiver: "<<Receiver<<" get a imme msg from "<< Sender<<endl;
        Discharge(pReceiver, msg);
    }
    else
    {
        msg.DispatchTime = NowTime + delay;
        mMsgQueue.insert(msg);
    }
}
// delay send msg , detected in each tick
void MessageDispatcher::DispatchDelayMessage()
{
    std::set<Telegram>::iterator itr = mMsgQueue.begin();
    // loop will be break once when the dispatchtime is bigger than nowtime
    // because the set is a priority queue sorted by time
    while(itr!=mMsgQueue.end() && itr->DispatchTime >= NowTime
        && itr->DispatchTime > 0)
    {
        Discharge(GObjMgr->FindObjpt(itr->Receiver),*itr);
        mMsgQueue.erase(itr);
        itr = mMsgQueue.begin();
    }
}

全局变量

// define the Global Singleton class object
 #define GFishingState FishingState::Instance()
 #define GEatFoodState EatFoodState::Instance()
// other fisher state...
 #define GWfWashState WfWashState::Instance()
// other fisher wife state...
 #define GObjMgr ObjectManager::Instance()
 #define GMsgDisptchr MessageDispatcher::Instance()
const int MAX_NUM_FISH = 150;
// Gameobject id
enum ObjPly
{
    FISHER,
    FISHER_WIFE,
};
extern unsigned long NowTime; // gloabl current time, update at each loop
// cross platform at linux or windows with different header
 #ifdef _WIN32
 #include <windows.h>
 #include <time.h>
 #include <conio.h>
//default text colors can be found in wincon.h
inline void SetTextColor(WORD colors)
{
    HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, colors);
}
 #else
 #include <unistd.h>
 #include <sys/time.h>
 #define Sleep(s) sleep(s/1000)
 #endif
// msg defined
enum MSG_ID
{
    MSG_BACKHOME,
    MSG_BREADOK,
    MSG_FOODREADY,
};

main

// update global time
unsigned long NowTime = 0;
void UpdateTIme()
{
 #ifdef _WIN32
    NowTime = GetTickCount();
 #else
    struct timeval ts;
    struct timezone tus;
    gettimeofday(&ts,&tus);
    NowTime = ts.tv_sec*1000 + ts.tv_usec/1000;
 #endif
}
// main
int main()
{
    UpdateTIme();
    Fisher fisr(FISHER);
    GObjMgr->RegisterObj(&fisr);
    FisherWife wife(FISHER_WIFE);
    GObjMgr->RegisterObj(&wife);
    fisr.ChangeState(GFishingState);
    while(1)
    {
        fisr.Tick();
        wife.Tick();
    GMsgDisptchr->DispatchDelayMessage(); // msg  traversal
        Sleep(1000);
    }
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值