限状态机将一个对象的行为分解为容易处理的“块”或者状态,对象执行了某些变换对象将从一个状态变成了另一种状态,说的简单点就是状态流程图,然后这些状态的数量是有限的。
毛星云的博客里举了一个很好的例子说明我们为什么要使用有限状态机。
http://blog.csdn.net/poem_qianmo/article/details/52824776
就拿他举的例子说明吧:
假如我们现在正在开发一款横版游戏。当前的任务是实现玩家用按键操纵女英雄。当按下向上方向键的时候,女英雄应该跳跃。那么我们可以这样实现:
void Heroine::handleInput(Input input)
{
if (input == PRESS_UP)
{
yVelocity_ = JUMP_VELOCITY;
setGraphics(IMAGE_JUMP);
}
}
OK,实现是实现了,但是一堆BUG。比如,我们没有防止主角“在空中跳跃“,当主角跳起来后持续按向上键,会导致她一直飘在空中。简单地修复方法可以是:添加一个 isJumping布尔值变量。当主角跳起来后,就把该变量设置为True.只有当该变量为False时,才让主角跳跃,代码如下:
void Heroine::handleInput(Input input)
{
if (input == PRESS_UP)
{
if (!isJumping_)
{
isJumping_ = true;
// Jump...
}
}
}
接下来,我们想实现主角的闪避动作。当主角站在地面上的时候,如果玩家按下向下方向键,则下蹲躲避,如果松开此键,则起立。代码如下:
void Heroine::handleInput(Input input)
{
if (input == PRESS_UP)
{
// Jump if not jumping...
}
else if (input == PRESS_DOWN)
{
if (!isJumping_)
{
setGraphics(IMAGE_DUCK);
}
}
else if (input == RELEASE_DOWN)
{
setGraphics(IMAGE_STAND);
}
}
找找看, 这次bug又在哪里?
使用这段代码,玩家可以:按向下键下蹲,按向上键则从下蹲状态跳起,英雄会在跳跃的半路上变成站立图片…….是时候增加另一个标识了……
上面的内容就是他的博客中的,具体的见:https://blog.csdn.net/poem_qianmo/article/details/52824776
那么为什么会出现需要反复添加标记变量的问题呢,对于那些标记也可以看做一个个的状态啊,原因是上述代码执行逻辑的出发点是操作而非状态,可以用如下图表示:
用下面的代码详细说明下吧。
void Heroine::handleInput(Input input)
{
if (input == PRESS_UP)
{
if (!isJumping_)
{
isJumping_ = true;
// Jump...
}
}
}
这里首先判断的不是状态,是操作,也就是if( input == PRESS_UP ),这样直接导致的后果是你需要在后面的代码中判断角色之前处于什么状态,然后将状态转换到相应的另一种状态。并且从上面的图也可以看到,这种方式不可能将任意多个状态串联到一起的,当按下向上的按钮后,再按下向下的按钮这是是两个独立的分析判断。
而采用有限状态机时,由于判断的出发点是状态,我们只需要关注于当前处理的状态,这样可以将整个逻辑串联成一个整体,也就是一个流程。比如起始状态是状态A,由于已经知道了当前的状态,此时如果知道操作是什么那么从状态流程图上就知道下一个状态是什么,因为一个状态执行一个操作后只能到达的状态只能是其中一种。
比如下图:从状态D如果执行某一种变换,它只可能到达状态B或者状态E,而不可能即处于状态B又处于状态E。
对有限状态机的介绍先到这里。
那么很明显,可以使用if else 或者 switch case实现FSM。但这样如果添加新的角色会很麻烦。对于游戏中很多的角色存在的状态相同,只是具体的方式有些区别(比如攻击,很多角色都有,只是攻击方式可能不同),总不能到处复制张贴,然后对每个角色单独的修改吧。
这时候就可以使用状态模式了。我个人对状态模式的理解是,它最大的特点就是对每个状态都建立一个单独的类,在类中定义状态的变换。比如,攻击状态类中当敌人不在范围内那么角色就从攻击状态变为追击状态,否则执行相应的攻击操作,这就是攻击状态类中需要做的事。当然,每个角色不同,攻击方式也不同,攻击方式由具体角色的实例来调用自身的攻击函数。
就拿设计模式与游戏完美开发这本书的给出的UML图结尾吧:
ICharacterAI:双方阵营角色AI接口
SoliderAI,EnemyAI:双方阵营在AI行为上不同,所以需要两个子类。
IAIState:角色AI状态
AttackAIState,ChaseAIState,MoveAIState:代表角色AI的状态,并负责不同状态的行为和判断(状态转换)
那么问题来了,FSM限制了同时只能存在一种状态,那么如果角色既被冰住了又晕了怎么办?
还有并发状态机,多层次状态机。后续学习吧。
参考:https://blog.csdn.net/poem_qianmo/article/details/52824776
设计模式与游戏完美开发