3d Game Walkman

3d图形渲染,网络引擎 — tonykee's Blog
随笔 - 45, 文章 - 0, 评论 - 309, 引用 - 0
数据加载中……

浅谈状态机FSM设计方法

看了一晚上FSM方面的资料,颇有收获,写写感悟:

状态机,在游戏里面是非常重要的,最简单的状态机,莫过于

switch()
 case 1:
    if(not 反复执行状态1)
       进入1状态前要做的准备

    进入1状态的过程

    if(not 反复执行状态1)
       离开状态1的过程

  case2:
...

但这种方式不能很有效预定义所有的状态,也不能把这些状态之间的切换过程合理的定义出来,“状态”本身没有一个合理的定义,几乎是一种面向过程的方式,只过这种方式足够简单,也最容易让人接受,缺点就没有“状态”的定义和指派功能,导致状态的混乱,出现状态处理重复代码,甚至处理不一致的问题,按照OO的观念,状态描述本来就应该是一种实体

比如“吃饭”这种状态,进入要做什么,进行时要做什么,退出时要做什么,需要进行一个描述,以下是我写的状态机管理策略:

//状态的定义
class State
{
public:

 State();

 State(const char * name);

 //状态的名字
 std::string statename;
 //比较两个状态是否相同
 inline bool operator ==(const State& other);
};


//状态机基类
class BaseFsm
{

public:

 //状态机的状态描述
 State state;

 //进入状态
 virtual void Enter(FsmEntity *entity);

 //执行状态
 virtual void Execute(FsmEntity *entity);

 //离开状态
 virtual void Exit(FsmEntity *entity);

 //比较两个状态机是否相同
 inline bool operator ==(const BaseFsm& other);
};


//状态机实体
class FsmEntity
{

protected:

  //当前所使用的状态机
  BaseFsm *mCurrentFsm;

public:

  //构造函数
  FsmEntity();

  //析构函数
  virtual ~FsmEntity();

  //设置开始状态
  void InitState(BaseFsm *fsm);

  //状态是否初始化了
  bool IsStateInited();

  //保持状态的方法
  void KeepState();

  //返回当前的状态机
  BaseFsm * GetCurrentFsm();

  //改变状态
  void ChangeState(BaseFsm *newFsm);
};


//状态机容器
class FsmManager
{

private:

//状态机容器的名称
 std::string name;

 //所有的状态集合
 std::map<std::string, BaseFsm *> mStatusCollection;

public:

 //命令一些实体去达到某个状态
 void Transaction(std::vector<FsmEntity *> & entities, const char * stateName);

 //令某个实体达到某个状态
 void Transaction(FsmEntity * entity, const char * stateName);

 //添加状态机
 void AddFsm( BaseFsm * fsm);

 //删除状态机
 void RemoveFsm(const char * stateName);

 //获取状态机
 BaseFsm * FindFsm(const char * stateName);

    //构造
 FsmManager(const char *fsname);

 //析构
 virtual ~FsmManager();
};




State::State()
{

}

State::State(const char * name)
{
 statename = name;
}


//构造函数
FsmEntity::FsmEntity()
{
  mCurrentFsm = 0;
}

//析构函数
FsmEntity::~FsmEntity()
{
 
}

//返回当前的状态机
BaseFsm * FsmEntity::GetCurrentFsm()
{
 return mCurrentFsm;
}

//状态是否初始化了
bool FsmEntity::IsStateInited()
{
 if(mCurrentFsm)
   return true;
 else
   return false;
}

//设置当前状态
void FsmEntity::InitState(BaseFsm *fsm)
{
 if(mCurrentFsm == 0)
 {
   mCurrentFsm = fsm;
 }
 else
 {
  LOG(0, WARN_LV, "初始状态已经设定");
 }
}

//保持状态的方法
void FsmEntity::KeepState()
{
 mCurrentFsm->Execute(this);
}

//改变状态
void FsmEntity::ChangeState(BaseFsm *newFsm)
{
 if(mCurrentFsm)
    //离开原来的状态
    mCurrentFsm->Exit(this);

 //设定现有状态
 mCurrentFsm = newFsm;

 //进入现有状态
 mCurrentFsm->Enter(this);

 //执行现有的状态
    mCurrentFsm->Execute(this);

}


//比较两个状态是否相同
bool State::operator ==(const State& other)
{
 return statename == other.statename;
}

 

//进入状态
void BaseFsm::Enter(FsmEntity *entity)
{
  LOG(0, DEBUG_LV, "进入%s状态", state.statename.c_str());
}

//执行状态
void BaseFsm::Execute(FsmEntity *entity)
{
  LOG(0, DEBUG_LV, "执行%s状态", state.statename.c_str());
}

//离开状态
void BaseFsm::Exit(FsmEntity *entity)
{
  LOG(0, DEBUG_LV, "离开%s状态", state.statename.c_str());
}


bool BaseFsm::operator ==(const BaseFsm& other)
{
    return state == other.state;
}

//命令一些实体去达到某个状态
void FsmManager::Transaction(std::vector<FsmEntity *> & entities, const char * stateName)
{
 for(size_t i = 0; i < entities.size(); i ++)
 {
  FsmEntity *entity = entities[i];
  Transaction(entity, stateName);
 }
}

//令某个实体达到某个状态
void FsmManager::Transaction(FsmEntity * entity, const char * stateName)
{
 if(entity->GetCurrentFsm() && entity->GetCurrentFsm()->state.statename == stateName)
 {
  entity->KeepState(); //保持之前的状态
 }
 else
 {
  BaseFsm * fsm = mStatusCollection[stateName];
  if(fsm)
  {
   //执行状态
   entity->ChangeState(fsm);
  }
  else
  {
            LOG(0, ERROR_LV, "找不到%s状态", stateName);
  }
 }
}

 

//添加状态机
void FsmManager::AddFsm( BaseFsm * fsm)
{
  if(mStatusCollection.find(fsm->state.statename.c_str()) != mStatusCollection.end())
   return; //已经添加过了
  //添加
  mStatusCollection[fsm->state.statename] = fsm;
}

//删除状态机
void FsmManager::RemoveFsm(const char * stateName)
{
  std::map<std::string, BaseFsm *>::iterator it = 0;
  if((it = mStatusCollection.find(stateName)) != mStatusCollection.end())
  {
     mStatusCollection.erase(it);
  }
}

//获取状态机
BaseFsm * FsmManager::FindFsm(const char * stateName)
{
 std::map<std::string, BaseFsm *>::iterator it = 0;
 if((it = mStatusCollection.find(stateName)) != mStatusCollection.end())
 {
   BaseFsm * fsm = it->second;
   return fsm;
 }
 return 0;
}

 

//构造
FsmManager::FsmManager(const char *fsname)
{
   name = fsname;
   LOG(0, DEBUG_LV, "构造状态机容器 %s", fsname);
}

//析构
FsmManager::~FsmManager()
{

 //移出所有的状态机
 for(std::map<std::string, BaseFsm *>::iterator it = mStatusCollection.begin(); it!= mStatusCollection.end(); it++)
 {
       BaseFsm * fsm = it->second;
    if(fsm)
   delete fsm;
 }

}






posted on 2009-08-07 17:30 李侃 阅读(5651) 评论(5)  编辑 收藏 引用 所属分类: 设计思路

评论

# re: 浅谈状态机FSM设计方法[未登录]  回复  更多评论   

请问能不能介绍几本关于这方面的书呢..谢谢!!
2009-08-08 00:30 | vibilin

# re: 浅谈状态机FSM设计方法[未登录]  回复  更多评论   

其实设计模式解决这个问题的最后也得使用配置文件才能解决问题,当时有C语言时,你要是使用配置文件,一样是可以解决的
2009-08-08 09:11 | Alex

# re: 浅谈状态机FSM设计方法  回复  更多评论   

全部是网上找的资料,GEM系列书上有介绍过,资料很零碎,具体上面的状态机封装形式还要结合脚本引擎,才能真正发挥它的威力,我已经这么做了,总之,就是一句话:很好很强大 o(∩_∩)o...
2009-08-08 09:43 | 李侃

# re: 浅谈状态机FSM设计方法  回复  更多评论   


状态机三个字足够包含一切
2009-08-08 17:11 | Vincent

# re: 浅谈状态机FSM设计方法  回复  更多评论   

第一个真不错,三言两语就解释出来了,后面这个OO的,不知道写起来维护会如何?几千个状态转换起来。。。。
2009-08-10 12:01 | j

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理