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


    用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

    复制代码
    /********************************************************************
    
    created: 2012/02/25
    created: 25:2:2012 11:31
    filename: E:OgreProjectOgre_ProjectGame_DemoOgreFramework.h
    file path: E:OgreProjectOgre_ProjectGame_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 = 0, OIS::MouseListener* pMouseListener = 0);
    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_
    复制代码

    主要还是要看InitOgre的实现:

    复制代码
    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(0);
    m_pViewport->setBackgroundColour(ColourValue(0.5f, 0.5f, 0.5f, 1.0f));

    m_pViewport->setCamera(0);

    size_t hWnd = 0;
    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(5);
    Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

    m_pTrayMgr = new OgreBites::SdkTrayManager("OFTrayMgr", m_pRenderWnd, m_pMouse, 0);

    m_pTimer = new Ogre::Timer();
    m_pTimer->reset();

    m_pRenderWnd->setActive(true);

    return true;
    }
    复制代码

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

    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()函数。

    复制代码
    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:OgreProjectOgre_ProjectGame_DemoGameState.h
    file path: E:OgreProjectOgre_ProjectGame_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:OgreProjectOgre_ProjectGame_DemoGameStateManager.h
    file path: E:OgreProjectOgre_ProjectGame_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:OgreProjectOgre_ProjectGame_DemoMenuState.h
    file path: E:OgreProjectOgre_ProjectGame_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

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

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

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

    一、加入第三人称控制器

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

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

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

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

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

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

    CRunState.h

    复制代码
    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;
    };
    复制代码

    在Enter函数中初始化:

    复制代码
    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();


    }
    复制代码

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

    复制代码
    void CRunState::Update(double timeSinceLastFrame)
    {
    if (m_bIsQuit)
    {
    Shutdown();
    return;
    }

    if (m_pChara)
    m_pChara->addTime(timeSinceLastFrame/1000); // from miliseconds to seconds

    }
    复制代码


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

    二、载入.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函数中载入场景:

    复制代码
    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);
    }
    复制代码


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

    最后就可以看到结果了:

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

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

     
     
     
     
  • 相关阅读:
    IE 创建条件样式
    Weinre
    three.js 之 透明物体不能正常显示
    用shader实现流动的水面(webgl)
    用js的方式运行c程序之webassemly
    three.js效果之热力图和轨迹线
    three.js后期之自定义shader通道实现扫光效果
    《计算机网络》物理层
    《计算机网络》概述
    pixi小游戏开发(vue+typescript)
  • 原文地址:https://www.cnblogs.com/hnfxs/p/3156693.html
Copyright © 2020-2023  润新知