游戏状态管理之C++实现

游戏状态管理之C++实现

Managing Game States in C++

 

文章来源:TonyandPaige.com

翻译: Mythma

 

多年前看的一个demo使我第一次完全明白了游戏中的不同状态。这个demo能够从一个特效平滑的过渡到另一个特效,并能从2D漩涡效果直接过渡到3D渲染环境,就像是几个不同的程序写成的。

 

多状态不仅在demo中重要,在游戏中也同样重要。每个游戏程序基本上都是从介绍开始,显示菜单,然后进行游戏。当被击败时,游戏就结束了(通常返回到菜单界面)。大多数游戏中,很可能会同时出现多个状态——比如游戏过程中弹出菜单。

 

传统的处理多状态的方式是使用一大堆的ifswitch loop语句。程序从介绍状态开始一直循环,直到按下一个键;然后菜单就显示了,直到完成了选择操作;游戏开始了,就一直循环,直到游戏结束。每一次游戏循环,程序还得检查是应该显示菜单还是简单的绘制下一帧。而且,处理事件的那部分程序还必须判断输入究竟是菜单来相应还是游戏来相应。所有的这些组合起来就形成了一个主循环,可以发现这个大杂烩很难理解,并且调试和维护起来也不方便。

 

状态是什么呢?(What’s a state?)

 

游戏状态就像是游戏的一个单独的程序。每个状态需要以不同的方式处理事件,需要在屏幕上绘制不同的内容。每个状态还需要处理它自己的事件、更新游戏场景、绘制下一帧等等。因此,我们可以确定状态应该至少拥有三个方法。

 

状态又应该能够加载图形、自初始化、释放资源。并且有时状态还需要暂停,还要能够恢复。

 

到此为止,状态类就应该看起来如下所示:

class CGameState
{
public:
  void Init();
  void Cleanup();

 

  void Pause();
  void Resume();

 

  void HandleEvents();
  void Update();
  void Draw();
};

 

上面的状态类设计应该能够满足游戏的状态需求。把它当作基类,从它可以派生出游戏所需要的每一种状态——介绍状态(intro state)、菜单状态(menu state)、游戏状态(play state)等等。

 

状态管理器(The State Manager

 

接下来,我们需要一个管理状态的方法——状态管理器。状态管理器是游戏引擎本身的一部分。别人可能会选择创建一个单独的状态管理类,而我只是把它直接加到引擎中。我们先来看看一个游戏引擎能做什么,然后再设计一个类来完成这些功能。

 

我们的这个例子很简单,整个引擎需要做到是初始化SDL和完成后的清理工作。由于在主循环需要用到这个引擎,因此需要检查引擎是否仍然在运行、是否退出,以及处理事件、更新游戏、绘制帧序列。

 

状态管理器实际上很简单。我们用 “状态栈”来管理各种不同的游戏状态。在此用STLvector来实现状态栈。除此之外,还需要改变状态的方法,以及入栈和出栈方法。

 

至此,游戏引擎类就如下所示:

class CGameEngine
{
public:

 

  void Init();
  void Cleanup();

 

  void ChangeState(CGameState* state);
  void PushState(CGameState* state);
  void PopState();

 

  void HandleEvents();
  void Update();
  void Draw();

 

  bool Running() { return m_running; }
  void Quit() { m_running = false; }

 

private:
  // the stack of states
  vector<CGameState*> states;

 

  bool m_running;
};

 

这些成员函数中有几个非常简单的——HandleEvents(), Update() Draw()。它们只需简单的调用栈顶状态对象相应的成员函数即可。由于需要经常访问游戏引擎中的数据,因此需要在以上三个成员函数中加一个指向引擎指针的参数。

 

最后需要考虑的是状态之间的切换。引擎如何知道状态何时切换到另一个状态?答案是:它不知道。只有当前的状态知道什么时候转向下一个状态。于是,我们又需要为状态类加一个改变状态的成员函数。

 

对于状态类时,我们把它设计为一个抽象基类——它的大部分成员函数设计为纯虚函数。这就保证了它的子类都必须得实现这些方法。经过这些更改后,最后的游戏状态类如下所示:

class CGameState
{
public:
  virtual void Init() = 0;
  virtual void Cleanup() = 0;

 

  virtual void Pause() = 0;
  virtual void Resume() = 0;

 

  virtual void HandleEvents(CGameEngine* game) = 0;
  virtual void Update(CGameEngine* game) = 0;
  virtual void Draw(CGameEngine* game) = 0;

 

  void ChangeState(CGameEngine* game, 
                   CGameState* state) {
    game->ChangeState(state);
  }

 

protected:
  CGameState() { }
};

 

现在要为游戏增加状态就非常简单了,只需从基类继承一个子类,实现那几个纯虚函数就可以了。对于某种状态,在游戏中只需一个实例,因此把它们设计成Singleton模式是个不错的选择。

 

这种方法究竟能够把游戏简化到什么程度,如下所示的main.cpp中包含的所有代码可以说明:

int main ( int argc, char *argv[] )
{
  CGameEngine game;

 

  // initialize the engine
  game.Init( "Engine Test v1.0" );

 

  // load the intro
  game.ChangeState( CIntroState::Instance() );

 

  // main loop
  while ( game.Running() )
  {
    game.HandleEvents();
    game.Update();
    game.Draw();
  }

 

  // cleanup the engine
  game.Cleanup();

 

  return 0;
}

 

下载(Downloads

 

这个例子里包含了三个不同的状态——introduction stateplaying statepauses state。每种状态都用一幅背景图片来表示。

·              stateman.zip - Tutorial Source, Graphics, and Project files for Visual C++ 6.

·              stateman.tar.gz - Tutorial Source, Graphics, and Makefile for Linux.

这个例子的代码用的是SDL。如果你不熟悉,可以看一下这篇文章:Getting Started with SDL

posted on 2005-05-01 16:18  张大大123  阅读(173)  评论(0编辑  收藏  举报

导航