《游戏编程模式》一7.5 状态对象应该放在哪里呢

    xiaoxiao2024-05-18  102

    本节书摘来异步社区《游戏编程模式》一书中的第7章,第7.5节,作者: 【美】Robert Nystrom (尼斯卓姆) 译者: 赵卫兵 , 许新星 , 姜召阳 , 陈侃 , 屈光辉 , 郑炯彬 责编: 陈冀康,更多章节内容可以访问云栖社区“异步社区”公众号查看。

    7.5 状态对象应该放在哪里呢

    我这里忽略了一些细节。为了修改一个状态,我们需要给state_指针赋值为一个新的状态,但是这个新的状态对象要从哪里来呢?我们之前的枚举方法是定义一些数字。但是,现在我们的状态是类,我们需要获取这些类的实例。通常来说,有两种实现方法。

    7.5.1 静态状态

    如果一个状态对象没有任何数据成员,那么它的唯一数据成员便是虚表指针了。那样的话,我们就没有必要创建此状态的多个实例了,因为它们的每一个实例都是相同的。

    在那种情况下,我们可以定义一个静态实例。即使你有一系列的FSM在同时运转,所有的状态机也能同时指向这一个唯一的实例。

    如果你的状态类没有任何数据成员,并且只有一个虚函数方法。那么我们还可以进一步简化此模式。我们可以使用一个普通的状态函数来替换状态类。这样的话,我们的state_变量就变成一个状态函数指针。

    这个就是享元模式。(第3章)你把静态方法放置在哪里,这个由你自己来决定。如果没有任何特殊原因的话,我们可以把它放置到基类状态类中:

    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); }

    7.5.2 实例化状态

    有时候上面的方法可能不行。一个静态状态对于躲避状态而言是行不通的。因为它有一个chargeTime_成员变量,所以这个具体取决于每一个躲避状态下的主角类。如果我们的游戏里面只有一个主角的话,那么定义一个静态类也是没有什么问题的。但是,如果我们想加入多个玩家,那么此方法就行不通了。

    当你为状态实例动态分配空间时,你不得不考虑碎片化问题了。对象池模式(第19章)可以帮助到你。在那种情况下,我们不得不在状态切换的时候动态地创建一个躲避状态实例。这样,我们的有限状态机就拥有了它自己的实例。当然,如果我们又动态分配了一个新的状态实例,则要负责清理老的状态实例。这里必须相当小心,因为修改状态的函数是在当前状态里面,所以我们需要小心地处理删除的顺序。

    另外,我们也可以选择在HeroineState类中的handleInput()方法里面可选地返回一个新的状态。当这个状态返回的时候,主角将会删除老的状态并切换到这个新的状态,如下所示:

    void Heroine::handleInput(Input input) {  HeroineState* state = state_−>handleInput(       *this, input);  if (state != NULL)  {   delete state_;   state_ = state;  } }

    那样的话,我们只有在从handleInput方法返回的时候才有可能去删除前面的状态对象。现在,站立状态可以通过创建一个躲避状态的实例来切换状态了。

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

    通常情况下,我倾向于使用静态状态。因为它们不会占用太多的CPU和内存资源。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)