本节书摘来异步社区《游戏编程模式》一书中的第7章,第7.9节,作者: 【美】Robert Nystrom (尼斯卓姆) 译者: 赵卫兵 , 许新星 , 姜召阳 , 陈侃 , 屈光辉 , 郑炯彬 责编: 陈冀康,更多章节内容可以访问云栖社区“异步社区”公众号查看。
7.9 层次状态机
在我们把主角的行为更加具象化以后,她可能会包含大量相似的状态。比如,她可能有站立、走路、跑步和滑动状态。在这些状态中的任何一个状态时按下B键,我们的主角要跳跃;按下下方向键,我们的主角要躲避。
如果只是使用一个简单的状态机实现,我们可能会在这些状态中重复不少代码。更好的解决方案是,我们只需要实现一次然后它便可以在所有的状态下都复用。
这可能同时带来好坏两种影响。继承是一种强大的代码重用方式,但是,它也会使得子类与基类之间的代码变得紧耦合。它是一个很大的“锤子”,需小心使用才行。
如果我们抛开状态机来谈面向对象,有一种共享代码的方式便是继承。我们可以定义一个类来表示“on ground”的状态,它用来处理跳跃状态和躲避状态。站立、走路、跑步和滑行状态从这个“on ground”的状态继承而来,并且在其类里面实现一些特殊行为。
这里,我们通常把这种状态机叫做层次状态机。一个状态有一个父状态。当有一个事件进来的时候,如果子状态不处理它,那么沿着继承链传给它的父状态来处理。换句话说,它有点像覆盖继承的方法。
实际上,如果我们正在使用状态模式来实现有限状态机,那么我们可以使用继承类来实现继承。我们首先定义一个基类来表示父状态:
class OnGroundState : public HeroineState
{
public:
virtual void handleInput(Heroine& heroine,
Input input)
{
if (input == PRESS_B) // Jump...
else if (input == PRESS_DOWN) // Duck...
}
}
};
然后,每一个子状态都继承至它:
class DuckingState : public OnGroundState
{
public:
virtual void handleInput(Heroine& heroine,
Input input)
{
if (input == RELEASE_DOWN)
{
// Stand up...
}
else
{
// Didn't handle input, so walk up hierarchy.
OnGroundState::handleInput(heroine, input);
}
}
};
```
当然,这不是实现继承的唯一方式。如果你没有使用GoF的状态模式,这种做法可能并不奏效。不过,你可以在基类中使用状态栈而不是单单一个状态的方法来更加明确地表示父状态的状态链。