用OGRE1.74搭建游戏框架(一)

新版的OGRE出来了,不知什么原因抛弃了CEGUI。国内的教程好像也更新比较少了,在官网上还是发现不少资料的,现在参考官网上的一些资料来搭建一个游戏的框架。

参考的资料:

http://www.ogre3d.org/tikiwiki/Advanced+Ogre+Framework&structure=Tutorials

首先要先一个能启动OGRE的类(OgreFramework),把OGRE的初始化的相关工作放入其中,大体有如下几个东西:

  • Root
  • RenderWindow
  • Viewport
  • Log
  • Timer
  • InputManager / Keyboard / Mouse
  • SDKTrays Manager

OgreFramework.h

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
/********************************************************************

created: 2012/02/25

created: 25:2:2012 11:31

filename: E:\Ogre\Project\Ogre_Project\Game_Demo\OgreFramework.h

file path: E:\Ogre\Project\Ogre_Project\Game_Demo

file base: OgreFramework

file ext: h

author: Star purpose: ogre framework

*********************************************************************/ #ifndef _ZH_OGREFRAMEWORK_H_

#define _ZH_OGREFRAMEWORK_H_ #pragma once #include <Ogre.h>

#include <ois/OIS.h>

#include <SdkTrays.h> class OgreFramework: public Ogre::Singleton<OgreFramework>, OIS::KeyListener, OIS::MouseListener

{

public:

OgreFramework();

~OgreFramework(); bool InitOgre(Ogre::String wndTitle, OIS::KeyListener* pKeyListener = , OIS::MouseListener* pMouseListener = );

void UpdateOgre(double timeSinceLastFrame); // for key event

bool keyPressed(const OIS::KeyEvent& evt);

bool keyReleased(const OIS::KeyEvent& evt); // for mouse event

bool mouseMoved(const OIS::MouseEvent& evt);

bool mousePressed(const OIS::MouseEvent& evt, OIS::MouseButtonID id);

bool mouseReleased(const OIS::MouseEvent& evt, OIS::MouseButtonID id); public:

Ogre::Root* m_pRoot;

Ogre::RenderWindow* m_pRenderWnd;

Ogre::Viewport* m_pViewport;

Ogre::Log* m_pLog;

Ogre::Timer* m_pTimer; OIS::InputManager* m_pInputMgr;

OIS::Keyboard* m_pKeyboard;

OIS::Mouse* m_pMouse; OgreBites::SdkTrayManager* m_pTrayMgr; private:

OgreFramework(const OgreFramework&);

OgreFramework& operator= (const OgreFramework&); }; #endif //_ZH_OGREFRAMEWORK_H_
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

主要还是要看InitOgre的实现:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
bool OgreFramework::InitOgre(Ogre::String wndTitle, OIS::KeyListener* pKeyListener /* = 0 */, OIS::MouseListener* pMouseListener /* = 0 */)

{

Ogre::LogManager* logMgr = new Ogre::LogManager(); m_pLog = Ogre::LogManager::getSingleton().createLog("OgreLogfile.log", true, true, flase);

m_pLog->setDebugOutputEnabled(true); m_pRoot = new Ogre::Root(); if (!m_pRoot->showConfigDialog())

return false; m_pRenderWnd = m_pRoot->initialise(true, wndTitle); m_pViewport = m_pRenderWnd->addViewport();

m_pViewport->setBackgroundColour(ColourValue(0.5f, 0.5f, 0.5f, 1.0f)); m_pViewport->setCamera(); size_t hWnd = ;

OIS::ParamList paramList;

m_pRenderWnd->getCustomAttribute("WINDOW", &hWnd); paramList.insert(OIS::ParamList::value_type("WINDOW", Ogre::StringConverter::toSgring(hWnd))); m_pInputMgr = OIS::InputManager::createInputSystem(paramList); m_pKeyboard = static_cast<OIS::Keyboard*>(m_pInputMgr->createInputObject(OIS::OISKeyboard, true));

m_pMouse = static_cast<OIS::Mouse*>(m_pInputMgr->createInputObject(OIS::OISMouse, true)); m_pMouse->getMouseState().height = m_pRenderWnd->getHeight();

m_pMouse->getMouseState().width = m_pRenderWnd->getWidth(); if (pKeyListener == NULL)

m_pKeyboard->setEventCallback(this);

else

m_pKeyboard->setEventCallback(pKeyListener); if (pMouseListener == NULL)

m_pMouse->setEventCallback(this);

else

m_pMouse->setEventCallback(pMouseListener); // load resources

Ogre::String secName, typeName, archName;

Ogre::ConfigFile cf;

cf.load("resources.cfg"); Ogre::ConfigFile::SectionIterator seci = cf.getSectionIterator();

while (seci.hasMoreElements)

{

secName = seci.peekNextKey();

Ogre::ConfigFile::SettingsMultiMap* settings = seci.getNext();

Ogre::ConfigFile::SettingsMultiMap::iterator i;

for (i = settings->begin(); i != settings->end(); ++i)

{

typeName = i->first;

archName = i->second;

Ogre::ResourceGroupManager::getSingleton().addResourceLocation(archName, typeName, secName);

}

}

Ogre::TextureManager::getSingleton().setDefaultNumMipmaps();

Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups(); m_pTrayMgr = new OgreBites::SdkTrayManager("OFTrayMgr", m_pRenderWnd, m_pMouse, ); m_pTimer = new Ogre::Timer();

m_pTimer->reset(); m_pRenderWnd->setActive(true); return true;

}
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

里边主要做了以下几个工作:

1.创建日志管理

2.创建OGRE的Root

3.创建渲染窗口(RenderWindow)和视口(Viewport)

4.启动OIS

5.设置鼠标和键盘的监听

6.载入资源

7.设置Timer

8.初始化SDKTrayManager

9.创建并显示debug overlay

注意,这里边没有像机和场景管理器,这个要到后面的状态类中才用到。

void OgreFramework::UpdateOgre(double timeSinceLastFrame)

{ }

这个函数用于在每一帧中更新和OGRE相关的一些东西,自己写的东西是要在每个状态中自己更新。

其实游戏的框架,普遍还是用的State这个设计模式。这样就可以在开始菜单,游戏画面,结束画面中自由的切换。看Android的libgdx引擎也是这样做的,分成不同的Screen来相互切换。

用OGRE1.74搭建游戏框架(二)--添加菜单

在上一节中,已经把OGRE的一个框架搭出来了,而且用的是一个单例模式,这样就意味着我们在后面的状态模式中可以很容易的就使用OGRE来帮我们进行图形绘制。

首先,写一个所有游戏状态的基类,因为我们大体可以抽象出所有游戏状态都应该有:

1.进入这个状态时应该做一些准备工作

2.退出这个状态时应该删除某些东西

3.暂停和继续时的一些动作。

4.更新

而一个游戏里有很多状态的切换,而我们又不想让一个状态知道另一个状态的存在,于是我们就要有一个统一管理这些状态的一个类,这样就能让这些状态之间解耦。(如果不这样做,比如说我想从菜单状态切换到游戏开始状态,那么我得在开始菜单中保存一个指向游戏开始状态的指针,而游戏开始状态想切到其它的状态也得这么做。)

因此我们写一个状态管理类,它实现一个状态监听接口。

状态管理用一个栈来存储所有的状态,当从当前状态切换到另一个状态时,就把栈顶的状态取出,并调用它的Exit()函数,并Pop出去,然后再初始化新的状态,并调用新状态的Enter()函数。

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
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();
}
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

GameState.h文件如下:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
/********************************************************************
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) = ; virtual CGameState* FindByName(Ogre::String stateName) = ;
virtual void ChangeGameState(CGameState* state) = ;
virtual bool PushGameState(CGameState* state) = ;
virtual void PopGameState() = ;
virtual void PauseCurGameState() = ;
virtual void Shutdown() = ;
virtual void PopAllAndPushGameState(CGameState* state) = ;
}; 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() = ;
virtual void Exit() = ;
virtual bool Pause() { return true;}
virtual void Resume() {};
virtual void Update(double timeSinceLastFrame) = ; 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_
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

上面代码中最后有一个宏定义,它的做用就是定义每个状态,并把它自己注册进状态管理类里。这是为了让你少写一些代码,不然你每定义一个状态类就得写一遍这个函数,有这个宏就方便多了。

可以看一下类图:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP

GameStateManager.h

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
/********************************************************************
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_
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

这个就是状态管理类,这里边的Start函数,就是程序消息循环的位置了。状态的切换也都在这里边发生。

这些函数的实现我就不复制,粘贴了,到时最后会发一个代码链接。

有了这些状态定义,我们现在就来试一下,先做一个MenuState。

MenuState.h

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
/********************************************************************
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_
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

主要讲一下Enter函数,这里边就要创建一下菜单界面了,还有Camera和SceneManager的创建,用的是OGRE现在带的SDKTray。然后也可以创建场景,这样就会有比较动感的开始菜单界面了等。

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
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(, , -));
m_pCamera->lookAt(Vector3(, , ));
m_pCamera->setNearClipDistance(); 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", );
COgreFramework::getSingletonPtr()->m_pTrayMgr->createButton(OgreBites::TL_CENTER, "ExitBtn", "Exit OgreFramework", );
COgreFramework::getSingletonPtr()->m_pTrayMgr->createLabel(OgreBites::TL_TOP, "MenuLbl","Menu mode", ); CreateScene();
}
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

这边有一个注意项:

如果没有这句的话

 Ogre::FontManager::getSingleton().getByName("SdkTrays/Caption")->load();

我这边顶上的那个Label里是不显示文字的,在他的论坛里找到这么一个解决方案。

最后的效果截图:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP

通过这样的框架就可以搭建自己的游戏了,只要继承GameState,实现自己的场景绘制和UI,就好了。期待你的游戏的产生。

资源下载:

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

用OGRE1.74搭建游戏框架(三)--加入人物控制和场景

有了前面的状态机,我们就可以很方便的扩展出自己的游戏场景状态的类。

现在我们要写一个RunState的类,这个就是游戏的主场景所在的地方,从MenuState可以跳转过来。

一、加入第三人称控制器

对于人物的控制,新版的OGRE已经给出了一个示例,就是那个Sample_Charater。

里边是对一个OGRE新模型的控制,操作起来感觉不错,就是向前跑动的时候没法看身后的东西,得停下来,Camera才会回到自由模式。

这个地方主要用到两个类:

SdkCameraMan:封装了OGRE的像机,可以比较自由的控制像机。

SinbadCharacterController:对于Sinbad的角色的控制以及动画的播放。

只要声明并定义好这两个类就能快速的实现第三人称的控制。

CRunState.h

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
class CRunState : public CGameState
{
public:
CRunState(); DECLARE_GAMESTATE_CLASS(CRunState) void Enter();
void CreateScene();
void Exit();
bool Pause();
void Resume(); 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 Update(double timeSinceLastFrame); protected:
OgreBites::SdkCameraMan* m_pCameraMan; bool m_bIsQuit; SinbadCharacterController* m_pChara;
};
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

在Enter函数中初始化:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
void CRunState::Enter()
{
COgreFramework::getSingletonPtr()->m_pLog->logMessage("Entering RunState.h."); m_pSceneMgr = COgreFramework::getSingletonPtr()->m_pRoot->createSceneManager(ST_GENERIC, "RunSceneMgr");
m_pSceneMgr->setAmbientLight(Ogre::ColourValue(0.3f, 0.3f, 0.3f)); m_pCamera = m_pSceneMgr->createCamera("MainCam");
m_pCamera->setNearClipDistance(5); m_pCamera->setAspectRatio(Real(COgreFramework::getSingletonPtr()->m_pViewport->getActualWidth())/
Real(COgreFramework::getSingletonPtr()->m_pViewport->getActualHeight())); COgreFramework::getSingletonPtr()->m_pViewport->setCamera(m_pCamera); m_pCameraMan = new OgreBites::SdkCameraMan(m_pCamera);
m_pCameraMan->setStyle(OgreBites::CS_MANUAL); m_pChara = new SinbadCharacterController(m_pCamera); COgreFramework::getSingletonPtr()->m_pTrayMgr->destroyAllWidgets();
COgreFramework::getSingletonPtr()->m_pTrayMgr->hideCursor(); CreateScene(); }
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

在那些键盘和鼠标消息中响应人物和摄像机的事件。最后要在Update中更新动画。

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
void CRunState::Update(double timeSinceLastFrame)
{
if (m_bIsQuit)
{
Shutdown();
return;
} if (m_pChara)
m_pChara->addTime(timeSinceLastFrame/1000); // from miliseconds to seconds }
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

这边注意,因为我们前面用的是毫秒,这边动画更新需要的是秒为单位,所以要除,否则画面不会动。

二、载入.Scene场景文件

如果要用代码加入物体,不仅麻烦(得算坐标),而且不易修改(修改后得重新编译)。于是就有了场景文件的出现,而且也有了开源的场景编辑器(Ogitor)。

Ogitor是一个基于QT的OGRE场景的编辑器,可以到官网上下载:

http://www.ogitor.org/HomePage

在我们的工程中要用到的就是三个文件(在Ogitor的安装目录的/SampleApp_Source/下):

DotSceneLoader.h

DotSceneLoader.cpp

rapidxml.h

把他们拷进我们的工程里,我们将要使用他们来载入场景。

这时,会发现程序有错,DotSceneLoader.cpp里要引用到PagedGeometry的一些东西,这是一个用来植树造林的工具,我们也可以去下载。

http://www.ogre3d.org/tikiwiki/PagedGeometry+Engine

官方没有提供已经编译好的,所以需要自己编译,要配置OGRE的头文件和库的路径。

新的OGRE中的material的一个方法改了,所以编译会有错误:

需要将:

bestTechnique = material->getBestTechnique(material->getLodIndexSquaredDepth(parent->minDistanceSquared));

转换为:

bestTechnique = getLodIndex(parent->minDistanceSquared)

这样就可以编译出LIB库了。

回到我们的工程:

在CreateScene函数中载入场景:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP
void CRunState::CreateScene()
{
DotSceneLoader* pDotSceneLoader = new DotSceneLoader();
pDotSceneLoader->parseDotScene("CubeScene.xml", "General", m_pSceneMgr, m_pSceneMgr->getRootSceneNode());
delete pDotSceneLoader; // add a bright light above the scene
Light* light = m_pSceneMgr->createLight();
light->setType(Light::LT_POINT);
light->setPosition(-10, 40, 20);
light->setSpecularColour(ColourValue::White); // create a floor mesh resource
MeshManager::getSingleton().createPlane("floor", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
Plane(Vector3::UNIT_Y, 0), 100, 100, 10, 10, true, 1, 10, 10, Vector3::UNIT_Z); // create a floor entity, give it a material, and place it at the origin
Entity* floor = m_pSceneMgr->createEntity("Floor", "floor");
floor->setMaterialName("Examples/Rockwall");
floor->setCastShadows(false);
m_pSceneMgr->getRootSceneNode()->attachObject(floor);
}
用用OGRE1.74搭建游戏框架(转载)-LMLPHP

这边要注意在资源文件(resource.cfg)中加入CubeScene.xml和Cube模型的资源路径,否则OGRE不会载入他们,就找不到这些资源。

最后就可以看到结果了:

用用OGRE1.74搭建游戏框架(转载)-LMLPHP

尝试着载入了Ogitor自带的Sample文件,SampleScene3.scene。注意要把资源路径配好。

用用OGRE1.74搭建游戏框架(转载)-LMLPHP

人缩小一倍还比房子大。。。有点像绿巨人,人物的位置要根据地形高低来设置好。

 
 
 
 
05-18 10:25