首先状态机模式是处理一个类在内部状态改变的时候,其方法处理信息的模式也会改变;
这里说一个在RTS游戏里的应用:有限状态机。
我们要赋予每个战斗单位一个智能,比如一定范围内检测到地方单位,且自身处于游荡或者Patrol状态,那么就转换为攻击状态,再比如我们给这个单位一个通往目的地的路径,那么这个单位会自动前往目的地,如果到达目的地,那么就进入Idle状态。
这里我们可以写一个状态管理类:
class StateSystem{
void State state;
public:
void Run(){
if(state==State::Idle){
//...
if(传递路径){
LeaveState();
state==State::Path;
InitState();
}
}else if(state==State::Path){
//...
if(//到达终点){
LeaveState();
state=State::Idle;
InitState();
}
}else if(state==State::Patrol){
//...
if(周边有敌人){
LeaveState();
state=State::Attack;
InitState();
}
}else if(state==State::Attack){
//...
if(敌人被消灭){
LeaveState();
state=Satte::Idle;
InitState();
}
}
}
void InitState(){
if(state==State::Idle){
//...
}else if(state==State::Path){
//...
}else if(state==State::Patrol){
//...
}else if(state==State::Attack){
//...
}
}
void LeaveState(){
if(state==State::Idle){
//...
}else if(state==State::Path){
//...
}else if(state==State::Patrol){
//...
}else if(state==State::Attack){
//...
}
}
}
可以看到,典型的bad smell,这里我们可以将每个状态抽象出来,设计好接口,然后用多态来替代if_else。
这个就是状态机模式的精髓,因为处理的问题就是一个类的方法处理信息的模式和类的状态有关,那么换句话说技术类中的每个状态都要处理相同的信息,那么我们为什么不把这种状态抽离出来呢,然后单独考虑呢?这样我们可以在原本的类中存储这些类的抽象接口,然后通过接口来调用这些状态。
看以下代码:
#include<unordered_map>
enum State {
Idle,Patrol,Path,Attack,None
};
class State_Ifc {
State state;
public:
virtual void Init() = 0;//初始化状态
virtual void Leave() = 0;//卸载状态
virtual void Run() = 0;//处理信息
virtual State Reason() = 0;//转移到其他状态
virtual State GetState() { return state; }
};
class IdleState :public State_Ifc {
//....重写
};
class PatrolState :public State_Ifc {
//....重写
};
class PathState :public State_Ifc {
//....重写
};
class AttackState :public State_Ifc {
//....重写
};
class StateSystem {
State_Ifc* Current;//当前的状态
std::unordered_map<State, State_Ifc*> map;
//
public:
void UpData() {
Current->Run();
}
void Trans() {
State temp = Current->Reason();
if (temp != State::None) {
Current->Leave();
Current = map[temp];
Current->Init();
}
}
};
这样子的话,State不依赖于具体的状态,而是依赖于抽象接口,这样子的话,就为拓展带来了可能。
但是,这个模式也有坏处,那就是没当添加新的状态,如果这个状态有和其他状态有转换,那么我们就不得不修改源代码,这里,本菜想,可以通过中介者模式来解决这个问题,具体方法是将
Reason方法从State子类中抽离出来,这样,我们修改转换时,只需要修改Mediator类即可:
具体如下:
#include<unordered_map>
#include<list>
enum State {
Idle,Patrol,Path,Attack,None
};
class Mediator_Ifc {
public:
virtual State Trans(State stateS, State stateE) = 0;
};
class State_Ifc {
std::list<State> list;
State state;
Mediator_Ifc* Mediator;
public:
virtual void Init() = 0;//初始化状态
virtual void Leave() = 0;//卸载状态
virtual void Run() = 0;//处理信息
virtual State Reason() {
for (auto t : list) {
State temp = Mediator->Trans(state, t);
if (temp != State::None) {
return temp;
}
}
return State::None;
}
virtual State GetState() { return state; }
};
class IdleState :public State_Ifc {
//....重写
};
class PatrolState :public State_Ifc {
//....重写
};
class PathState :public State_Ifc {
//....重写
};
class AttackState :public State_Ifc {
//....重写
};
class MediatorCenter :public Mediator_Ifc {
State Trans(State stateS, State stateE) {
//...实现
}
};
class StateSystem {
Mediator_Ifc* Mediator;
State_Ifc* Current;//当前的状态
std::unordered_map<State, State_Ifc*> map;
//
public:
void UpData() {
Current->Run();
}
void Trans() {
State temp = Current->Reason();
if (temp != State::None) {
Current->Leave();
Current = map[temp];
Current->Init();
}
}
};
这样就可以了。
总之一句话:状态机模式是处理一个类在内部状态改变的时候,其方法处理信息的模式也会改变;