在上一節中,已經把OGRE的一個框架搭出來了,而且用的是一個單例模式,這樣就意味着我們在后面的狀態模式中可以很容易的就使用OGRE來幫我們進行圖形繪制。
首先,寫一個所有游戲狀態的基類,因為我們大體可以抽象出所有游戲狀態都應該有:
1.進入這個狀態時應該做一些准備工作
2.退出這個狀態時應該刪除某些東西
3.暫停和繼續時的一些動作。
4.更新
而一個游戲里有很多狀態的切換,而我們又不想讓一個狀態知道另一個狀態的存在,於是我們就要有一個統一管理這些狀態的一個類,這樣就能讓這些狀態之間解耦。(如果不這樣做,比如說我想從菜單狀態切換到游戲開始狀態,那么我得在開始菜單中保存一個指向游戲開始狀態的指針,而游戲開始狀態想切到其它的狀態也得這么做。)
因此我們寫一個狀態管理類,它實現一個狀態監聽接口。
狀態管理用一個棧來存儲所有的狀態,當從當前狀態切換到另一個狀態時,就把棧頂的狀態取出,並調用它的Exit()函數,並Pop出去,然后再初始化新的狀態,並調用新狀態的Enter()函數。
void CGameStateManager::ChangeGameState(CGameState* state)
{
if (!m_ActiveStateStack.empty())
{
m_ActiveStateStack.back()->Exit();
m_ActiveStateStack.pop_back();
}
m_ActiveStateStack.push_back(state);
Init(state);
m_ActiveStateStack.back()->Enter();
}
GameState.h文件如下:
/********************************************************************
created: 2012/02/25
created: 25:2:2012 15:05
filename: E:\Ogre\Project\Ogre_Project\Game_Demo\GameState.h
file path: E:\Ogre\Project\Ogre_Project\Game_Demo
file base: GameState
file ext: h
author: Star
purpose: the parent of all the game state
*********************************************************************/
#ifndef _ZH_GAMESTATE_H_
#define _ZH_GAMESTATE_H_
#pragma once
#include "OgreFramework.h"
class CGameState;
class CGameStateListener
{
public:
CGameStateListener(){};
virtual ~CGameStateListener(){};
virtual void ManageGameState(Ogre::String stateName, CGameState* state) = 0;
virtual CGameState* FindByName(Ogre::String stateName) = 0;
virtual void ChangeGameState(CGameState* state) = 0;
virtual bool PushGameState(CGameState* state) = 0;
virtual void PopGameState() = 0;
virtual void PauseCurGameState() = 0;
virtual void Shutdown() = 0;
virtual void PopAllAndPushGameState(CGameState* state) = 0;
};
class CGameState : public OIS::KeyListener,public OIS::MouseListener, public OgreBites::SdkTrayListener
{
public:
static void Create(CGameStateListener* parent, const Ogre::String name) {};
void destroy() { delete this; }
virtual void Enter() = 0;
virtual void Exit() = 0;
virtual bool Pause() { return true;}
virtual void Resume() {};
virtual void Update(double timeSinceLastFrame) = 0;
protected:
CGameState(){};
CGameState* FindByName(Ogre::String stateName) {return m_pListener->FindByName(stateName); }
void ChangeGameState(CGameState* state) { m_pListener->ChangeGameState(state); }
bool PushGameState(CGameState* state) { return m_pListener->PushGameState(state);}
void PopGameState() {m_pListener->PopGameState(); }
void Shutdown() { m_pListener->Shutdown(); }
void PopAllAndPushGameState(CGameState* state) { m_pListener->PopAllAndPushGameState(state); }
CGameStateListener* m_pListener;
Ogre::Camera* m_pCamera;
Ogre::SceneManager* m_pSceneMgr;
Ogre::FrameEvent m_FrameEvent;
};
// macro define
#define DECLARE_GAMESTATE_CLASS(T) \
static void Create(CGameStateListener* pListener, const Ogre::String name) \
{ \
T* newState = new T(); \
newState->m_pListener = pListener; \
pListener->ManageGameState(name, newState); \
} \
#endif //_ZH_GAMESTATE_H_
上面代碼中最后有一個宏定義,它的做用就是定義每個狀態,並把它自己注冊進狀態管理類里。這是為了讓你少寫一些代碼,不然你每定義一個狀態類就得寫一遍這個函數,有這個宏就方便多了。
可以看一下類圖:
GameStateManager.h
/********************************************************************
created: 2012/02/25
created: 25:2:2012 16:05
filename: E:\Ogre\Project\Ogre_Project\Game_Demo\GameStateManager.h
file path: E:\Ogre\Project\Ogre_Project\Game_Demo
file base: GameStateManager
file ext: h
author: Star
purpose: Manage all the game states
*********************************************************************/
#ifndef _ZH_GAMESTATEMANAGER_H_
#define _ZH_GAMESTATEMANAGER_H_
#pragma once
#include "GameState.h"
class CGameStateManager : public CGameStateListener
{
public:
typedef struct
{
Ogre::String name;
CGameState* state;
} SState_info;
CGameStateManager();
~CGameStateManager();
void ManageGameState(Ogre::String stateName, CGameState* state);
CGameState* FindByName(Ogre::String stateName);
void Start(CGameState* state);
void ChangeGameState(CGameState* state);
bool PushGameState(CGameState* state);
void PopGameState();
void PauseCurGameState();
void Shutdown();
void PopAllAndPushGameState(CGameState* state);
protected:
void Init(CGameState* state);
std::vector<CGameState*> m_ActiveStateStack;
std::vector<SState_info> m_States;
bool m_bShutdown;
};
#endif //_ZH_GAMESTATEMANAGER_H_
這個就是狀態管理類,這里邊的Start函數,就是程序消息循環的位置了。狀態的切換也都在這里邊發生。
這些函數的實現我就不復制,粘貼了,到時最后會發一個代碼鏈接。
有了這些狀態定義,我們現在就來試一下,先做一個MenuState。
MenuState.h
/********************************************************************
created: 2012/02/27
created: 27:2:2012 11:40
filename: E:\Ogre\Project\Ogre_Project\Game_Demo\MenuState.h
file path: E:\Ogre\Project\Ogre_Project\Game_Demo
file base: MenuState
file ext: h
author: Star
purpose: the menu of the game
*********************************************************************/
#ifndef _ZH_MENUSTATE_H_
#define _ZH_MENUSTATE_H_
#pragma once
#include "GameState.h"
class CMenuState : public CGameState
{
public:
CMenuState();
DECLARE_GAMESTATE_CLASS(CMenuState)
void Enter();
void CreateScene();
void Exit();
bool keyPressed(const OIS::KeyEvent &evt);
bool keyReleased(const OIS::KeyEvent &evt);
bool mouseMoved( const OIS::MouseEvent &evt);
bool mousePressed( const OIS::MouseEvent &evt, OIS::MouseButtonID id );
bool mouseReleased( const OIS::MouseEvent &evt, OIS::MouseButtonID id );
void buttonHit(OgreBites::Button* button);
void Update(double timeSinceLastFrame);
private:
bool m_bQuit;
};
#endif //_ZH_MENUSTATE_H_
主要講一下Enter函數,這里邊就要創建一下菜單界面了,還有Camera和SceneManager的創建,用的是OGRE現在帶的SDKTray。然后也可以創建場景,這樣就會有比較動感的開始菜單界面了等。
void CMenuState::Enter()
{
Ogre::FontManager::getSingleton().getByName("SdkTrays/Caption")->load();
COgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering MenuState.h.");
m_pSceneMgr = COgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "MenuSceneMgr");
m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.7f, 0.7f, 0.7f));
m_pCamera = m_pSceneMgr->createCamera("MenuCam");
m_pCamera->setPosition(Vector3(0, 25, -50));
m_pCamera->lookAt(Vector3(0, 0, 0));
m_pCamera->setNearClipDistance(1);
m_pCamera->setAspectRatio(Real(COgreFramework::getSingletonPtr()->m_pViewport->getActualWidth())/
Real(COgreFramework::getSingletonPtr()->m_pViewport->getActualHeight()));
COgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera);
COgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
COgreFramework::getSingletonPtr()->m_pTrayMgr->showFrameStats(OgreBites::TL_BOTTOMLEFT);
COgreFramework::getSingletonPtr()->m_pTrayMgr->showLogo(OgreBites::TL_BOTTOMRIGHT);
COgreFramework::getSingletonPtr()->m_pTrayMgr->showCursor();
COgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "EnterBtn", "Enter GameState", 250);
COgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "ExitBtn", "Exit OgreFramework", 250);
COgreFramework::getSingletonPtr()->m_pTrayMgr->createLabel(OgreBites::TL_TOP, "MenuLbl","Menu mode", 250);
CreateScene();
}
這邊有一個注意項:
如果沒有這句的話
Ogre::FontManager::getSingleton().getByName("SdkTrays/Caption")->load();
我這邊頂上的那個Label里是不顯示文字的,在他的論壇里找到這么一個解決方案。
最后的效果截圖:
通過這樣的框架就可以搭建自己的游戲了,只要繼承GameState,實現自己的場景繪制和UI,就好了。期待你的游戲的產生。
資源下載: