最近在研究怪物的AI,由于怪物的AI是使用有限状态机来实现的。所以就查看关于有限状态机的东西。根据这几天的查看资料,知道了有限状态机是计算机科学中一个很重要的概念。而且有限状态机是一个抽象的概念,具体实现是多种多样的。根据维基百科的介绍,它是这样的一个概念:有限状态机(英语:finite-state machine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。
就像上面说的,既然是数学模型当然有很多的实现方法。其实最简单容易想到的方法就是使用switch case的写法:
swtich(case)
{
case STATE1:
do something;
break;
case STATE2:
do something;
break;
.......
}
但是这种写法会带来很多的问题,首先是当状态多的时候,switch case的语句会写的很长,使得可读性非常差。其次,可维护性也很差,每当要添加一个状态转换时,就要在Switch case添加相应的状态转换函数。会使得代码变得冗长,难以扩展。
当使用状态模式写状态机的时候,每一个状态就是一个类,添加状态只需要添加一个状态类就行了。在每一个类中实现相应的状态转换函数。
使用状态模式的好处:
1 每一个状态就是一个类,便于扩展,可读性高
2 每一个类有一个状态转换函数,使得整个代码的层次结构,很清晰
当然,没有一个东西是完美的。使用状态模式实现的时候,会使得类可能会变得很庞大。当总的来说,还是好处大于坏处的。毕竟代码不仅是出产品,还要考虑这个代码的扩展性还有可读性。一个只实现了功能,但扩展性很差,或者是除了你没有人看得懂,或者是很难看懂的代码,绝不是好代码。简单高效才是我们程序员追求的目标。
一个很简单的例子,仿照电梯的例子:
/************************************************************************
Des: 状态机,负责状态的装换
************************************************************************/
#include "statemachine.h"
#include "StateDefine.h"
using namespace std;
StateMachine::StateMachine()
{
//起始的状态为停止状态
m_currentState = m_lastState = STOP_STATE;
}
StateMachine::~StateMachine()
{
}
/**
* @brief 状态机的初始化函数,负责把每一个状态的类放入Map中进行管理
*/
void StateMachine::init()
{
m_stateManager[0] = new CloseState();
m_stateManager[1] = new OpenState();
m_stateManager[2] = new UpState();
m_stateManager[3] = new DownState();
m_stateManager[4] = new StopState();
(m_stateManager[4])->handle(this);
}
/**
* @brief 状态装换函数,当需要切换状态的时候,只需要调用此函数
* @param stateId :每一个状态都有一个相应的ID,定义在StateDefine的头文件中
*/
void StateMachine::changeState(int stateId)
{
if( stateId == m_currentState )
{
cout<<"curent state is "<"doesn't transfrom state"< return;}
else{
int temp = m_lastState;m_lastState = m_currentState;m_currentState = stateId;std::map<int,State*>::iterator it = m_stateManager.find(stateId);if( it != m_stateManager.end() ){
if( !(it->second)->handle(this)){
m_currentState = m_lastState;m_lastState = temp;}
}
}
}/**
* @brief 获得上次的状态* @return 返回状态ID*/int StateMachine::getLastState(){return m_lastState;}/**
* @brief 获得当前的状态* @return 返回状态ID*/int StateMachine::getState(){return m_currentState;}然后是每一个状态的类:/************************************************************************
Des: 下落的状态/************************************************************************/
#include "downstate.h"#include "StateDefine.h"#include "statemachine.h"using namespace std;DownState::DownState()
{}DownState::~DownState(){cout<<"destructor downstate"< }/**
* @brief 处理相应的状态装换* @param 传入状态机* @return*/bool DownState::handle(StateMachine* p_machine){//判断上次的状态是否能正确切换到现在的状态if( (p_machine->getLastState() != CLOSE_STATE) || (p_machine->getLastState() != STOP_STATE)){
if(p_machine->getLastState() != CLOSE_STATE)cout<<"the lift must be close!"< elsecout<<"the lift must be stoped"< return false;}
cout<<"the lift is downing "< return true;}/************************************************************************
Des:关闭的状态/************************************************************************/
#include "closestate.h"#include "StateDefine.h"#include "statemachine.h"using namespace std;CloseState::CloseState()
{}CloseState::~CloseState(){cout<<"destructor closestate"< }bool CloseState::handle(StateMachine* p_machine){if( (p_machine->getLastState() != OPEN_STATE)){
cout<<"the lift must be closed!"< return false;}
cout<<"the lift is closing "< return true;}/************************************************************************
* Des: 开启的状态/************************************************************************/
#include "openstate.h"#include "StateDefine.h"#include "statemachine.h"using namespace std;OpenState::OpenState()
{}OpenState::~OpenState(){}bool OpenState::handle(StateMachine* p_machine){if( p_machine->getLastState() != STOP_STATE ){
cout<<"the lift must be stop!"< return false;}
cout<<"the lift is opening"< return true;}/************************************************************************
* Des: 停止的状态/************************************************************************/
#include "stopstate.h"#include "statemachine.h"#include "StateDefine.h"using namespace std;StopState::StopState()
{}StopState::~StopState(){}bool StopState::handle(StateMachine* p_machine){if(p_machine->getLastState() == p_machine->getState()){
cout<<"the lift is stop "< return true;}
if( p_machine->getLastState() == STOP_STATE ){
cout<<"the lift have already be stopping!"< return false;}
cout<<"the lift is stop "< return true;}/************************************************************************
* Des: 上升的状态/************************************************************************/
#include "upstate.h"#include "StateDefine.h"#include "statemachine.h"using namespace std;UpState::UpState()
{}UpState::~UpState(){cout<<"destrucotor update"< }bool UpState::handle(StateMachine* p_machine){if( p_machine->getLastState() != CLOSE_STATE ){
cout<<"the lift must be close!"< return false;}
cout<<"the lift is uping"< return true;}下面是公共的头文件,定义了每一个状态对应的枚举值,方便管理/************************************************************************
* Des: 定义一个枚举,每一个状态对于一个值/************************************************************************/
#ifndef STATEDEFINE_H#define STATEDEFINE_Henum{
CLOSE_STATE,OPEN_STATE,UP_STATE,DOWN_STATE,STOP_STATE,};#endif // STATEDEFINE_H下面是每一个状态的父类,使用了虚函数,使得对外的切换状态的接口是统一的/************************************************************************
* Des: 每一个状态的父类,定义了虚函数(handle),每一个状态都实现了这个函数,* 实现状态的切换具体实现,多态的体现/************************************************************************/
#ifndef STATE_H#define STATE_Hclass StateMachine;class State{public:
State();virtual ~State();virtual bool handle(StateMachine* p_machine) = 0;};#endif // STATE_H使用次状态模式的具体类:/************************************************************************
* Des: 电梯的具体类,负责实现电梯的具体逻辑/************************************************************************/
#include "lift.h"#include#include "StateDefine.h"using namespace std;Lift::Lift()
{}Lift::~Lift(){delete this->m_stateMachine;this->m_stateMachine = nullptr;}/**
* @brief 电梯的初始化,使用按键模拟电梯的行为*/void Lift::init(){this->m_stateMachine = new StateMachine();this->m_stateMachine->init();while (true) {cout<<"enter the value"< char str[8] = {0};cin>>str;
if( !strcmp(str,"c")){
this->m_stateMachine->changeState(CLOSE_STATE);}
else if( !strcmp(str,"o")){
this->m_stateMachine->changeState(OPEN_STATE);}
else if( !strcmp(str,"u")){
this->m_stateMachine->changeState(UP_STATE);}
else if( !strcmp(str,"d")){
this->m_stateMachine->changeState(DOWN_STATE);}
else if( !strcmp(str,"s")){
this->m_stateMachine->changeState(STOP_STATE);}
}
}使用状态模式确实使得使用避免过度的使用了IF ELSE 或者是Switch case,也使得整个的逻辑变得清晰可见。初探状态模式,还有些不足,以后改进!!(_~ ~_)