{
GameState_Loading* state = new GameState_Loading;
state->setNextPhase( m_curState->getNextPhase() );
m_curState = state;
}
else if( m_curState->getStateOP() == STATE_OP_NEW_STACK )
{
GameState_Loading* state = static_cast< GameState_Loading*>( m_curState );
changeState( state->getNextState(), STATE_OP_PUSH );
delete state;
}
}
}
};
GameState_Loading类处理所有的状态转换工作,这当然包括旧资源释放和新资源初始化,同时绘制loading界面。
StateMgr新增了两个操作方式。StateMgr::STATE_OP_LOAD就是开始建立一个新的phase,也就是从旧phase进入loading状态,然后进行资源载入和新phase中各个state的建立等工作,这些工作在GameState_Loading::cycle中逐帧完成。StateMgr::STATE_OP_NEW_STACK表示从当前loading状态进入到新建立的phase的默认state中。
StateMgr::cycle方法中新增加调用一个新加入的方法StateMgr::leaveFrame。该方法用于在离开当前帧的时候做一些事情。在这里我们主要处理state转换。
GameState增加了两个成员,m_stateOP用于告诉StateMgr是否需要转换到另一个phase,默认值是StateMgr::STATE_OP_NONE——什么也不做。m_phaseToLoad告诉StateMgr它要转换到哪一个phase。这些phase都定义在GameState_Loading中。比如在logo状态中需要转换到trailer状态,我们可以在GameState_Logo::cycle中写:
m_stateOP = StateMgr::STATE_OP_LOAD;
m_phaseToLoad = GameState_Loading::PHASE_TRAILER;
StateMgr::leaveFrame就会建立一个loading状态来进行状态转换。当GameState_Loading::cycle完成了初始化,它就会通过StateMgr::STATE_OP_NEW_STACK让流程进入新的phase的默认state中,正如上面代码所示。
(我在程序中使用了一些伪码来避免陷入过多细节,目的是更好的表达出这个结构的思路。如果你非常需要了解该系统的具体实现,可以和我联系)
改进方向
好了!我们已经完成了该系统的基本框架。读者完全可以根据该框架实现一个自己的游戏状态机,并取得良好的运行效果。但我还是要说,这和真正游戏中使用的工程级别代码比,还差一些!下面我会指出一些设计上的改进和扩展,让该系统更容易在游戏产品中使用。感兴趣的读者可以自行实现。
1 给GameState加上自定义“构造函数”和“析构函数”
如果能给state增加方法:
GameState::onActive
GameState::onUnactive
会让很多事情事半功倍,且可以得到良好的结构和健壮性。 在StateMgr::changeState中进行state转换(push和pop)的时候, 给即将停止的state调用onUnactive,给即将运行的state调用onActive,可以给这些state一个机会做一些构造和析构工作(比如释放和申请一些小资源,或重新初始化一些数据等等)。我们的代码就强烈地依赖这些方便的小方法。
2 增加state之间的界面过渡
很多游戏在界面过渡之间都使用了一些特效,最常见的就是淡入淡出效果。令人兴奋的是,通过上面的状态机系统增加这样的过渡效果非常方便。比如我们自己设计了一个叫做FullScreenEffect的基类,通过设计不同的子类来完成不同的过渡效果。
提示:在StateMgr里面合成该类的一个实例,然后在StateMgr::cycle和StateMgr::draw中调用FullScreenEffect::cycle和FullScreenEffect::draw方法,并通过一些标志来禁止和启动StateMgr::m_curState的更新和渲染。
3 通过事件分发系统进行状态改变通知
通过我们之前介绍的事件分发系统(http://blog.csdn.net/popy007/article/details/8242787)来通知系统进行state转换是个很不错的设计思路!
4 把StateMgr写成一个singleton
StateMgr应该只有一个且可以被方便地访问,写成一个singleton吧!(关于singleton模式,可以参考GoF的著作)
5 给loading状态增加一个资源载入管理器
在loading状态中,我们有时候需要画出当前的进度比例,这个比例如何计算出呢?很多游戏用的是假数据——只体现一个递增的效果。但还有些用的是真实数据,对于真实数据来说,该机制和你游戏的资源管理系统有很大关系,这里我提供一个简单思路。
我们将需要载入或申请的所有资源进行分类,比如:
字符串
纹理
关卡数据
逻辑脚本
缓存
自定义回调函数
...
给这些资源定义一个通用的结构,并用一个ID来区分。然后这些资源就有了一个统一的表示结构,比如
struct Res;
然后建立一个(你喜欢的任何容器都可以)
std::list< Res >