简述游戏开发中的状态机
为什么我们需要状态机
实行较多状态的角色,把动作全写在一个部分中会导致维护成本高,拓展性低
例如:走路,跳跃,射击,躲避的相互转换,有些可以转换,有些不能,实现逻辑复杂
(满屏幕都是if - else)
状态模式switch实现
//包含着所有的状态
enum class State{StateA, StateB, StateC, ...} activeState;
...
//通过switch语句切换状态,根据具体情况实现细节
switch (activeState)
{
case State.StateA:
...
break;
case State.StateB:
...
break;
.......
}
...
状态机的原形,用一个枚举表示当前的状态,通过填充完善switch语句实现状态之间的切换,但是依然有维护成本高拓展低的缺点(虽然确实是比用if - else堆好)
Finite State Machine(FSN)有限状态机
最基本的状态机,一般来说其他状态机都是这种状态机的变体
对于状态机的理解,最好就是画个图(如图结构,方框是状态,箭头是状态之间的联系)
有限状态机强调的是状态之间的切换,以及对不同状态的封装,所以实现方法一般可以根据需求调整
以下参考了《游戏人工智能》的实现
首先需要个基类State和基类Translation
//状态基类,所有状态都继承这个类
class State
{
public:
virtual ~State(){}
virtual OnStateEnter(){} //进入此状态执行一次
virtual OnUpdate(){} //每一帧执行一次
virtual OnStateExit(){} //跳出状态时调用一次
list<Translation> translations; //状态迁移列表
};
//状态迁移
class Translation
{
public:
virtual ~Translation(){}
virtual bool isValid() = 0; //用于判定迁移,可切换返回true
virtual State* getNextState() = 0; //进入下一个状态
virtual void onTransition(){} //迁移时调用
};
State是每一个状态都会继承的基类,translations中存着他指向其他状态的Translation,通过每一帧遍历所有Translation的isValid()来判定是否可以跳转,若可以跳转则执行自身的OnStateExit(),并将当前的状态设置为getNextState()获得的状态
然后还有状态机类,用来管理所有状态:
//状态机类
class FiniteStateMachine
{
public:
void Update(); //每一帧运行一次
State* initialState; //初始状态
State* activeState; //正在运行的状态
protected:
list<State> states; //所有状态的实例
};
void FiniteStateMachine::Update()
{
//遍历活动状态的所有迁移,若有可用的,则切换状态
list<Translation>::iterator itr = activeState->translations.begin();
for (int i = 0; i < activeState->translations.size(); ++i, ++itr)
{
if (itr->isValid())
{
activeState->OnStateExit(); //退出调用
activeState = itr->getNextState(); //切换活动状态
itr->onTransition(); //切换调用
activeState->OnStateEnter(); //进入调用
return; //直接返回
}
}
//如果没有状态切换,运行一次update
activeState->OnUpdate();
}
接下来具体的状态细节就要具体继承,具体实现
Hierarchical Finite State Machine (HFSM) 分层状态机
分层状态机相当于对有限状态机的进一步封装,将复数个状态封装成一个大的状态,再用一个“历史状态”记录切出此状态时运行的子状态,就可以在大状态间切换(结构如图)
图中将StateA和StateB加入到一个更高层的状态StateD中,只需退出时记录状态,就可以在CD状态间切换,增加了状态C的复用性(省略了AC,BC间的联系)
主要思想一是包装更高的层次,二是高层的切换放到更高层来觉得以提高复用
分层状态机的实现主要分两种:
一、父状态就是一个状态机,可以往里面添加状态
二、添加一个状态栈储存父子状态,每一个状态都是栈下一个状态的子状态,进入状态时入栈,结束状态时出栈并发送一个消息,新栈顶状态不能处理就再出栈,直到栈顶状态能处理这个消息为止。
并发状态机
并发状态机可以理解为一个对象拥有两个同时运作的状态机,两个状态机相互独立(但可以通过修改对象的状态进行通信),例如我们可以将腿部动作(站立,下蹲,奔跑)和手部动作(持枪,空手,瞄准)分离。
下推状态机
下推状态机的核心是他有一个状态栈(但和层次状态机实现的栈完全不同,层次状态机的栈是为了记录父状态,这个栈是为了纪录上一个状态)。
状态栈主要解决的是状态机无法得到上一轮执行的状态。
实现时,在状态机中加一个栈,在进入状态时将状态入栈,退出状态时将状态出栈,运行时则运行栈顶的状态。
例如,我在做作业的时候饿了,想去吃东西。饿了去吃东西是多数状态都能进入的状态,并且结束“吃东西”的状态后我要回复原来的状态。
在运行时,状态机已经将“做作业“入栈
然后“我饿了”,将“吃东西”入栈
“吃东西”结束,将“吃东西”出栈
继续运行栈顶的“做作业”(事实上不可能)
若有错误,欢迎指出