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

资源下载:

http://download.csdn.net/detail/sunstar1989/4094433