说到状态管理,很多人第一时间想到的,就是状态机了,根据不同的游戏状态,调用不同的游戏过程,来进行游戏更新和渲染 。一般来说,我们都是在游戏更新和渲染过程里面,用一个大大的 switch 语句,来确定当前的游戏状态,如果状态数量少的话,倒没所谓,但是,一旦游戏状态多起来了,全部状态都塞到一个过程里面,阅读起来,就变得非常困难了,维护起来的话,就更加难上加难 。
那么,有没有什么比较好的办法,对游戏中的各种状态进行一个统一的管理呢 ? 至少,看起来,代码不会那么乱七八糟(笑),答案显然易见的,废话不说,我们直接上代码 :
1 //----------------------------------- 2 // 游戏状态祖先类 3 //----------------------------------- 4 5 class IGameState 6 { 7 // 8 // 初始化 9 // 10 virtual initialize( ) = 0; 11 12 // 13 // 销毁 14 // 15 virtual shutdown( ) = 0; 16 17 // 18 // 更新 19 // 20 virtual update( ) = 0; 21 22 // 23 // 渲染 24 // 25 virtual render( ) = 0; 26 27 // 28 // 暂停 29 // 30 virtual pause( ) = 0; 31 32 // 33 // 恢复 34 // 35 virtual resume( ) = 0; 36 37 // 38 // windows 消息处理 39 // 40 virtual msgproc( UINT _msg, WPARAM _wParam, LPARAM _lParam ) = 0; 41 };
我们创建一个 IGameState 纯虚类,抽象出一个游戏状态应有的各种行为和能力,然后,我们再来实际创建不同的游戏状态 :
1 //----------------------------------- 2 // 创建一个游戏介绍状态 3 //----------------------------------- 4 5 class CIntroGameState : public IGameState 6 { 7 }; 8 9 //----------------------------------- 10 // 创建一个游戏主菜单状态 11 //----------------------------------- 12 13 class CMainMenuState : public IGameState 14 { 15 }; 16 17 //----------------------------------- 18 // 创建一个系统设定状态 19 //----------------------------------- 20 21 class CSystemSetting : public IGameState 22 { 23 }; 24 25 // ...
最后一行注释里面的三个点是告诉我们,我们可以创建更多的游戏状态,而不是仅仅限制在上面三个状态,然后,我们的游戏状态,理所当然地就交给我们的游戏对象来进行维护啦 :
1 //----------------------------------- 2 // 游戏对象类 3 //----------------------------------- 4 5 class CGame : public IGameState 6 { 7 // CGame 可以通过切换 game state 来更新绘制不同的状态 8 9 setGameState( IGameState * _state ){ cur_game_state = _state; } 10 11 // 返回 cur_game_state 可以得知当前正在更新绘制哪一个状态 12 13 IGameState * getGameState( void ){ return cur_game_state; } 14 15 // 保存游戏状态 16 17 pushGameState( IGameState * _state ){ game_state.push( cur_game_state ); cur_game_state = _state; } 18 19 // 恢复游戏状态 20 21 popGameState( void ){ cur_game_state = game_state.top( ); game_state.pop( ); } 22 23 private: 24 25 // 当前正在运行哪一个状态 26 27 IGameState * cur_game_state; 28 29 // 游戏状态堆栈, 用于游戏状态的回溯 30 31 stack< IGameState * > game_state; 32 };
为了让各种游戏状态对象之间可以互相交互,我们必须提供一些全局的函数以及全局的对象,当然,如果各位读者们有更好的方法,请留言给我哈(笑) :
1 //----------------------------------- 2 // 全局对象和函数 3 //----------------------------------- 4 5 CGame _game; 6 7 CGame * getGame( void ){ return & _game; } 8 9 IGameState * intro_game = new CIntroGameState( ); 10 IGameState * main_menu = new CMainMenuGameState( ); 11 IGameState * sys_setting = new CSystemSetting( );
然后就是我们的游戏初始化过程:
1 //----------------------------------- 2 // 程序入口点 3 //----------------------------------- 4 5 void main( void ) 6 { 7 // 初始化游戏 8 9 _game.initialize( ); 10 11 // 游戏初始化后进入介绍界面 12 13 _game.setGameState( intro_game ); 14 15 // 运行游戏 16 17 while( true ) 18 { 19 _game.update( ); 20 _game.render( ); 21 } 22 23 // 销毁游戏 24 25 _game.shutdown( ); 26 }
最后,重头戏到了,我们每一个游戏状态对象,只管负责自己的更新和渲染,如果需要通知游戏对象进行状态切换,那么可以看看下面代码 :
1 class CIntroGameState : public IGameState 2 { 3 update( ) 4 { 5 // 如果在播放游戏介绍画面时, 用户按下了 Esc 按键 6 7 if( ESC_key pressed ) 8 { 9 // 切换到游戏主菜单状态 10 11 getGame( )->setGameState( main_menu ); 12 } 13 } 14 }; 15 16 class CMainMenuGameState : public IGameState 17 { 18 update( ) 19 { 20 // 如果在游戏主菜单里面, 用户点击了系统设置按钮 21 22 if( sys_setting_button clicked ) 23 { 24 // 切换到系统设置状态 25 26 getGame( )->setGameState( sys_setting ); 27 } 28 } 29 }; 30 31 class CSysMenuGameState : public IGameState 32 { 33 // ...... 34 };
---------------------------------------------------------------------------------
OK,这些代码,应该不需要太多的描述了,看起来也很容易懂 。
最后,请大家不要吝啬自己的技术,把自己用过的管理游戏状态或者游戏对象的方法都说出来,大家互相学习学习哈,因为如果我们死死将知识揣在口袋里面,那么那些没有掌握到这些知识的人,又非常迫切需要这些知识的人,就会非常着急,甚至会退缩,这样子,我们就当真成了罪人咯嘻嘻 。