一个多态性的游戏状态机系统(二)

2014-11-24 08:50:57 · 作者: · 浏览: 1
)、credits(制作团队介绍)和settings(设置)这三个状态,并且可以从这三个状态返回主菜单状态。在in-game状态下可以进入pause menu(暂停菜单)并返回。

此外,我们有时候需要在一种状态下显示另一种状态。比如在pause menu中显示暂停选项的时候仍然显示游戏背景(用某种颜色的全屏幕半透明矩形覆盖使其暗化,并且游戏逻辑此时不会更新)
这意味着给state增加一个parent pointer会很方便:

[cpp]
class GameState
{

// ...as above

public:
void setParent( GameState* state ) { m_parent = state; }
GameState* getParent() { return m_parent; }
private:
GameState* m_parent;
};

这样,我们可以这样实现pause menu的draw方法:

[cpp]
void GameState_PauseMenu::draw( GraphicsContext& g )
{
m_parent->draw( g );
draw the transparent mask layer...
draw pause menu items...
}

我们首先渲染parent,对于pause menu状态来说,它的parent就是in-game状态。然后渲染半透明覆盖层。最后渲染pause menu的选项。

此外,parent pointer对于状态的转换也是非常方便的。

为了能够方便地操纵游戏状态在状态树上进行转换,我们扩展manager类:

[cpp]
class StateMgr
{

// ...as above

public:
enum StateOP
{
STATE_OP_PUSH = 0,
STATE_OP_POP,
};

public:
void changeState( GameState* newState, int op )
{
if( op == STATE_OP_PUSH )
{
newState->setParent( m_curState );
m_curState = newState;
}
else if( op == STATE_OP_POP )
{
m_curState = m_curParent->getParent();
}
}
};

我们增加了state操作方法StateMgr::changeState并通过两个操作类型:push和pop,可以很方便地在状态树上移动,

Loading状态

以上设计有一个很大的问题,你能看出来吗?似乎所有的state同时存在,这将导致大量的资源存在于内存中。就算是当进入到main menu状态之后,我们再也无法返回trailer或者logo状态,它们的资源也还驻留在内存里。因此,我们需要把这些状态划分阶段(phase),只让当前一个phase内的所有state留在内存里。当游戏从一个phase转到另一个phase的时候,会释放旧phase资源,然后载入新phase资源。这通过一个叫做GameState_Loading的类来实现。在释放旧资源和载入新资源的过程中,GameState_Loading将接管局面,并显示载入进度界面。我们先把目前的状态树划分phase
整个状态树被划分为4个phase:

logo(logo)
trailer(trailer)
main menu(main menu, credits, settings menu)
in-game(in-game, pause menu)

括号里面的就是该phase所包含的状态,会在一个loading过程中全部驻留内存。每一个phase实际上都形成一个子树,通过一个stack结构和上面的push、pop操作进行转换。我们扩展上面的类
[cpp]
class GameState
{
// ...as above

public:
int getStateOP() const { return m_stateOP; }
int getNextPhase() const { return m_phaseToLoad; }

protected:
int m_stateOP;
int m_phaseToLoad;

};

class GameState_Loading : public GameState
{
public:
enum Phase
{
PHASE_LOGO = 0,
PHASE_TRAILER,
PHASE_MAIN_MENU,
PHASE_INGAME,
};

public:
void setNextPhase( int phase ) { m_phaseToLoad = phase; }
GameState* getNextState() { return m_nextState; }

virtual void cycle()
{
free the old phase...
init the new phase frame by frame...
save the new states to StateMgr::m_states...

if( initialization is completed )
{
m_nextState = default state of the phase
m_stateOP = StateMgr::STATE_OP_NEW_STACK;
}
}

virtual void draw( GraphicsContext& g )
{
draw the progress interface...
}

private:
int m_phaseToLoad;
GameState* m_nextState;
};

class StateMgr
{

// ...as above

public:
enum StateOP
{
STATE_OP_NONE = -1,
STATE_OP_PUSH = 0,
STATE_OP_POP,
STATE_OP_LOAD,
STATE_OP_NEW_STACK,
};
public:
void cycle()
{
// ...as above

leaveFrame();
}

private:
void leaveFrame()
{
if( m_curState->getStateOP() != STATE_OP_NONE )
{
if( m_curState->getStat