关闭

状态模式

标签: 魂斗罗状态模式State设计模式Design
233人阅读 评论(0) 收藏 举报
分类:

状态模式

定义:

Allow an object to alter its behavior when its internal state changes. The object will appear to change its class.
允许一个对象在其内部状态改变时改变它的行为,看起来就像修改了它的类

  类似魂斗罗的游戏中,我们操纵的英雄会有不同的状态,而在不同的状态下,对应不同的输入会有不同的行为。例如在平地上我们按下B按钮英雄就会跳跃,而在高空中我们按B按钮就不会跳跃。(老版魂斗罗高空中按下pause键会清除跳跃状态,所以可以在高空进行跳跃,这是一个我们不想也是不合理的Bug)

1.有限自动机简单实现

首先我们勾画出一幅状态转图:
这里写图片描述
代码实现:

enum State         //表示状态的枚举类
{
  STATE_STANDING,
  STATE_JUMPING,
  STATE_DUCKING,
  STATE_DIVING
};

void Heroine::handleInput(Input input)   //英雄针对不同输入在不同状态下的行为
{
  switch (state_)
  {
    case STATE_STANDING:
      if (input == PRESS_B)
      {
        state_ = STATE_JUMPING;
        yVelocity_ = JUMP_VELOCITY;
        setGraphics(IMAGE_JUMP);
      }
      else if (input == PRESS_DOWN)
      {
        state_ = STATE_DUCKING;
        setGraphics(IMAGE_DUCK);
      }
      break;

    case STATE_JUMPING:
      if (input == PRESS_DOWN)
      {
        state_ = STATE_DIVING;
        setGraphics(IMAGE_DIVE);
      }
      break;

    case STATE_DUCKING:
      if (input == RELEASE_DOWN)
      {
        state_ = STATE_STANDING;
        setGraphics(IMAGE_STAND);
      }
      break;
  }
}

相比一系列的if,else语句,这种实现已经有了很大的改进,总体逻辑变得清晰。

2.状态类实现

class HeroineState             //状态基类
{
public:
  virtual ~HeroineState() {}
  virtual void handleInput(Heroine& heroine, Input input) {}
  virtual void update(Heroine& heroine) {}
};

class DuckingState : public HeroineState     //具体状态类
{
public:
  DuckingState()
  : chargeTime_(0)
  {}

  virtual void handleInput(Heroine& heroine, Input input) {
    if (input == RELEASE_DOWN)
    {
      // Change to standing state...
      heroine.setGraphics(IMAGE_STAND);
    }
  }

  virtual void update(Heroine& heroine) {
    chargeTime_++;
    if (chargeTime_ > MAX_CHARGE)
    {
      heroine.superBomb();
    }
  }

private:
  int chargeTime_;
};

class Heroine
{
public:
  virtual void handleInput(Input input)
  {
    state_->handleInput(*this, input);
  }

  virtual void update()
  {
    state_->update(*this);
  }

  // Other methods...
private:
  HeroineState* state_;       //通过指针指向不同的状态
};

由于状态是影响的一个成员指针,所以不同的状态我们需要存放在一个地方。
2.1静态存放:

class HeroineState
{
public:
  static StandingState standing;
  static DuckingState ducking;
  static JumpingState jumping;
  static DivingState diving;

  // Other code...
};

if (input == PRESS_B)
{
  heroine.state_ = &HeroineState::jumping;
  heroine.setGraphics(IMAGE_JUMP);
}

若状态中没有针对英雄对象实体的信息记录,这是一种好的选择。
2.2实例创建

void Heroine::handleInput(Input input)
{
  HeroineState* state = state_->handleInput(*this, input);
  if (state != NULL)
  {
    delete state_;     //先释放先前的状态
    state_ = state;
  }
}

HeroineState* StandingState::handleInput(Heroine& heroine,
                                         Input input)
{
  if (input == PRESS_DOWN)
  {
    // Other code...
    return new DuckingState();
  }

  // Stay in this state.
  return NULL;
}

这种方式使得每个英雄实例拥有自己的状态,状态实例中可以存放相应的信息,但这种频繁创建释放对于CPU来说本身就是一种消耗。
2.3为状态类添加entry和exit方法

class StandingState : public HeroineState
{
public:
  virtual void enter(Heroine& heroine)
  {
    heroine.setGraphics(IMAGE_STAND);
  }

  // Other code...
};
void Heroine::handleInput(Input input)
{
  HeroineState* state = state_->handleInput(*this, input);
  if (state != NULL)
  {
    delete state_;
    state_ = state;

    // Call the enter action on the new state.
    state_->enter(*this);
  }
}

这样我们可以方便的在进入和退出状态时,进行一些信息的处理。
2.4并发状态
当英雄携带不同的武器时,我们的各种行为动作将会有并行的存在,例如跳跃的时候进行射击等。此时我们可以添加一个装备转换状态,使得人物可以完成不同状态的组合。

class Heroine
{
  // Other code...

private:
  HeroineState* state_;
  HeroineState* equipment_;   //添加装备状态
};
void Heroine::handleInput(Input input)
{
  state_->handleInput(*this, input);
  equipment_->handleInput(*this, input);   //装备状态的处理
}

3.下推自动机

我们可以通过下推自动机进行状态的保存,这样在进行状态转换处理后,我们可以方便的返回到先前的状态,即用栈实现。
这里写图片描述

  • 栈顶表示当前的状态,我们可以执行push将新的状态压入栈中,而之前的状态将被保留到栈中,不丢弃。
  • 我们执行pop丢弃当前的状态,此时将回到之前的状态

状态模式的优点

  • 封装了转换规则。
  • 将所有与某个状态有关的行为放到一个类中,并且可以方便地增加新的状态,只需要改变对象状态即可改变对象的行为。
  • 允许状态转换逻辑与状态对象合成一体,而不是某一个巨大的条件语句块。
  • 可以让多个环境对象共享一个状态对象,从而减少系统中对象的个数。

状态模式的缺点

  • 状态模式的使用必然会增加系统类和对象的个数。
  • 状态模式的结构与实现都较为复杂,如果使用不当将导致程序结构和代码的混乱。
  • 状态模式对“开闭原则”的支持并不太好,对于可以切换状态的状态模式,增加新的状态类需要修改那些负责状态转换的源代码,否则无法切换到新增状态;而且修改某个状态类的行为也需修改对应类的源代码。

参考Game Programming Patterns-State

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:17247次
    • 积分:844
    • 等级:
    • 排名:千里之外
    • 原创:70篇
    • 转载:1篇
    • 译文:0篇
    • 评论:1条