在上一节中,已经把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,就好了。期待你的游戏的产生。
资源下载: