何为DFA?确定的有穷状态机。这里
看完上面那篇博客,就明白状态机还挺好用的。 那么现在,转场。
要开发一款游戏,咱负责的模块是处理游戏角色属性框架的搭建,目前已知角色有:坦克、法师、射手,他们都有属性:血量、物攻、物抗、法攻、法抗、角色转换技能。
同样,可以采用自动机的方式来进行处理:
坦克 | 法师 | 射手 | |
---|---|---|---|
坦克 | - | 双抗-,法攻+ | 双抗-,物攻+ |
法师 | 双抗+,法攻- | - | 物攻+,法攻- |
射手 | 双抗+,物攻- | 物攻-,法攻+ | - |
也挺好办的,就这样封装好了。
但是,这毕竟是C语言的方式,面向过程,如果我要拓展,加俩新角色进去呢?加个近战战士,怎么办?把写好的代码拆了重写?那明天再加个辅助进去,再重写?
既然学了面向对象,那就用面向对象的方式来解决这类问题:将每种状态封装成类,然后该怎么办就不用我多说了。
类怎么设计呢?看这样好不好:
class 坦克{
private:
int 血量;
int 物攻;
int 物抗;
int 法攻;
int 法抗;
public:
void 攻击();
void 防守();
void 技能();
}
可还行? 然后场景类就这样:
int main(){
class* 坦克兵 = new 坦克();
class* 魔法师 = new 法师();
class* 狙击手 = new 射手();
//被人打了,用坦克
坦克兵->防守();
//要打消耗,用法师
魔法师->攻击();
//敌人要跑,用射手
狙击手->技能();
···
}
这样始终是零散,疏于管教。而且,并不能实现自动啊,顶多就是个手动状态机。 那怎么弄哦?将前边的DFA再看看,再配上责任链的思想,想想就知道该来了父类,然后每个子类中自己管理自己的状态,再维护一条状态链表,实现有穷的自动状态机(C++状态模式版)。
你看这样可好?
class 状态基类{
private:
string 当前状态;
状态基类* 下一状态;
public:
virtual void 攻击();
virtual void 防守();
virtual void 技能();
void setState(string 状态){this->当前状态 = 状态;}
void setNextNode(状态基类* 下一状态){this->下一状态 = 下一状态;};
状态基类* 遍历状态(string 目标状态){
if(this->当前状态 == 目标状态)
return this;
else if(this->下一状态 != NULL)
下一状态->遍历状态(目标状态);
else
return NULL;
}
}
class 坦克:public 状态基类{
public:
void 攻击(){···}
void 防守(){···}
void 技能(){···}
}
···
场景类就这样实现了:
状态基类* 状态1 = new 坦克();
状态基类* 状态2 = new 战士();
状态基类* 状态3 = new 法师();
状态基类* 状态4 = new 射手();
状态基类* 当前状态;
状态1->setState("坦克");
状态2->setState("战士");
状态3->setState("法师");
状态4->setState("射手");
状态1->setNextNode(状态2);
状态2->setNextNode(状态3);
状态3->setNextNode(状态4);
//被人打了
当前状态 = 状态1->遍历状态(坦克);
当前状态->防守();
//要打消耗,用法师
当前状态 = 状态1->遍历状态(法师);
当前状态->攻击();
//敌人要跑,用射手
当前状态 = 状态1->遍历状态(射手);
当前状态->技能();
//中埋伏了,换战士
当前状态 = 状态1->遍历状态(战士);
当前状态->攻击();
当前状态->防守();
当前状态->放技能();
这样连轴转。
当一个对象内在状态改变时允许其改变行为,这个对象看起来像改变了其种类。
状态模式的核心是封装,状态的变更引起了行为的变更,从外部看起来就好像这个对象对应的类发生了改变一样。
结构清晰、遵循设计原则、封装性好
要避免状态过多。