基础教程六(CEGUI和OGRE)

必备知识

本教程是在假设你已经拥有c++编程基础并能够成功建立和编译Ogre程序(如果你设置程序方面还存在问题,请参阅SettingUpAnApplication来获取详细信息)。 本教程建立在之前的初学者教程的基础上,并且假设你已经学习了它们。

[编辑]工程设置

下面的适用于下载源代码的用户:

    添加include文件夹: $(OGRE_HOME)/Dependencies/include,
                      $(OGRE_HOME)/Dependencies/include/CEGUI
    添加lib库路径:     $(OGRE_HOME)/OgreMain/Dependencies/Lib/Debug

确信已经链接 'CEGUIBase' 和 'OgreGUIRender' 库,也就是说将下面一行添加进你的Makefile文件或g++命令行:

    -L/usr/local/lib -lCEGUIBase -lCEGUIOgreRenderer

下面的适用于SDK的用户:

   添加include文件夹:$(OGRE_HOME)/include/CEGUI

确信已经在debug配置的中添加 'CEGUIBase_d.lib' 和 'OgreGUIRenderer_d.lib' 库( 'CEGUIBase.lib' 和 'OgreGUIRenderer.lib' 在release配置中)。 在Visual C++中添加依赖,依次点击:项目 -> 属性 -> 配置属性 -> 链接。

CEGUIRender源程序现在是从Ogre CVS下载代码中的一部分,一个示例工程,因此你必须将包含OgreGUIRenderer头文件和lib文件的文件夹路径添加到属性配置中。

另外,下面两个目录是必需的。尽管你在你的安装路径中的文件夹找不到。将其作为约定它就会起作用:

    添加Include文件夹: $(OGRE_HOME)/Samples/Common/CEGUIRenderer/include
    添加 Lib 路径:      $(OGRE_HOME)/Samples/Common/CEGUIRenderer/lib

[编辑]介绍

Crazy Eddies GUI系统是一个为不具备或缺乏用户界面制作功能的图形API或引擎提供免费用户界面支持的开源的库。这个使用c++编写的库是针对那些想制作优秀的游戏却又没有GUI(图形用户界面)子系统的专业游戏开发者。

[编辑]开始

首先,你需要架构(skeleton)代码来创建具有CEGUI组件的Ogre程序。 注意:如果你使用,你必须在之前添加#define NOMINMAX。

   //mem probs without this next one
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include "OgreCEGUIRenderer.h"
  #include "OgreCEGUIResourceProvider.h"
  //regular mem handler
  #include  
  #include "ExampleApplication.h"
  class GuiFrameListener : public ExampleFrameListener
  { 
  private:
    CEGUI::Renderer* mGUIRenderer;
  public:
    GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer)  : ExampleFrameListener(win, cam, false, false),
        mGUIRenderer(renderer)
    {
    }
  };

仅仅是一个不做任何动作的空帧监听器,但在你按下“Esc”之前会一直循环。

  class TutorialApplication : public ExampleApplication
  {
  private:
     CEGUI::OgreCEGUIRenderer* mGUIRenderer;
     CEGUI::System* mGUISystem;
     CEGUI::Window* mEditorGuiSheet;

这些是包含所有CEGUI数据的数据成员。我喜欢显式的调用CEGUI成员,一但你开始对Ogre成员添加调用,这将会明确的说明它们是来自CEGUI。

  public:
      TutorialApplication()  : mGUIRenderer(0),
          mGUISystem(0),
          mEditorGuiSheet(0)
      {
      }
      ~TutorialApplication() 
      {
         if(mEditorGuiSheet)
         {
             CEGUI::WindowManager::getSingleton().destroyWindow(mEditorGuiSheet);
         }
         if(mGUISystem)
         {
             delete mGUISystem;
             mGUISystem = 0;
         }
         if(mGUIRenderer)
         {
             delete mGUIRenderer;
             mGUIRenderer = 0;
         }
      }

下面是你可以设置任意Ogre场景的地方,使用你在前五章教程学到的方法。在这个Ogre场景中,你仍要为其添加一个独立的相机(camera)和视窗(viewport)。

  protected:
      void createScene(void)
      {
         // Set ambient light
         mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

下面是创建CEGUI日志的地方,一般都设置为Informative模式的。其具有四种模式:Standard, Errors, Informative 和 Insane。

          // Set up GUI system
         mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow,  
                 Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);
         mGUISystem = new CEGUI::System(mGUIRenderer);   
         CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);

创建一个新的CEGUI系统,使用“TaharezLook”来设置图(sheme)与鼠标指针,使用“BlueHighway-12”来设置字体。

           CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");
          mGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"TaharezLook",(CEGUI::utf8*)"MouseArrow");
          CEGUI::MouseCursor::getSingleton().setImage("TaharezLook","MouseMoveCursor");
          mGUISystem->setDefaultFont((CEGUI::utf8*)"BlueHighway-12");
          mEditorGuiSheet=CEGUI::WindowManager::getSingleton().createWindow((CEGUI::utf8*)"DefaultWindow",
                                                                      (CEGUI::utf8*)"Sheet");  
          mGUISystem->setGUISheet(mEditorGuiSheet);
      }

调用自定义的帧监听器,这样我们可以在需要时访问“mGUIRender”。

  void createFrameListener(void)
     {
         mFrameListener = new GuiFrameListener(mWindow, mCamera, mGUIRenderer);
         mRoot->addFrameListener(mFrameListener);
     }
  };

下面是主函数也是程序的主循环,在本教程并不需要你修改这段代码。

   #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
  #define WIN32_LEAN_AND_MEAN
  #include "windows.h"
  INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
  #else
  int main(int argc, char **argv)
  #endif
  {
      // Create application object
      TutorialApplication app;
      try {
          app.go();
      } catch( Exception& e ) {
  #if OGRE_PLATFORM == OGRE_PLATFORM_WIN32 
          MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
  #else
          fprintf(stderr, "An exception has occured: %s/n",e.getFullDescription().c_str());
  #endif
      }
      return 0;
  }

完成后,编译程序能得到一个空的窗口。请关掉程序,继续我们的学习。 注意:如果实际操作中出现问题,你可以在应用程序所在文件夹中找到“CEGUI.log”文件分析查找错误

[编辑]CEGUI是如何工作的

本质上CEGUI是通过向窗口添加第二个场景,这个场景是在Ogre的基本渲染队列完成后才渲染的。这个场景仅仅是由一系列3D矩形对象组成的。(也就是两个多边形沿着其边压制到一起)。渲染矩阵是为消除矩形的突兀与歪斜而根据他们的位置建立的。使用这些矩形,添加材质和响应就构成了用户界面(GUI)。一般情况下这是很不错的,因为一个3D的用户界面将会自动的缩放其元素来适应屏幕,并且使用硬件材质过滤。其将会比C++标准的2D用户界面更加快速和漂亮。

  “So in one sentence: CEGUI renders a 2D gui using 3D methods and hardware so you don't have to.”——zeroskill

[编辑]添加退出按钮

首先,我们需要为应用程序添加下面的头文件。本例中是“Push Button”

   #include 

我们要在场景底部添加退出按钮。

   CEGUI::PushButton* quitButton = (CEGUI::PushButton*)CEGUI::WindowManager::getSingleton().createWindow
                                             ("TaharezLook/Button", (CEGUI::utf8*)"Quit");
   mEditorGuiSheet->addChildWindow(quitButton);
   quitButton->setPosition(CEGUI::Point(0.35f, 0.45f));
   quitButton->setSize(CEGUI::Size(0.3f, 0.1f));
   quitButton->setText("Quit");

完成后执行程序,一个漂亮的按钮将会出现在屏幕中。但请注意,程序此时仍然不做任何事情,因为我们并没有为其添加响应时间。

如果你编译时遇到了setPosition()和setSize()的调用错误: 'CEGUI::Window::setPosition' : cannot convert parameter 1 from 'CEGUI::Vector2' to 'const CEGUI::UVector2 &'

将setPosition()和setSize()所在行分别用下面的代码替换:

 quitButton->setPosition(CEGUI::UVector2(cegui_reldim(0.35f), cegui_reldim( 0.45f)) );
 quitButton->setSize(CEGUI::UVector2(cegui_reldim(0.35f), cegui_reldim( 0.1f)) );

[编辑]响应事件

将下面函数添加到TutorialApplication的public:中

  void setupEventHandlers(void)
  {
     CEGUI::WindowManager& wmgr = CEGUI::WindowManager::getSingleton();
     wmgr.getWindow((CEGUI::utf8*)"Quit")->subscribeEvent(CEGUI::PushButton::EventClicked, CEGUI::Event::Subscriber
                                       (&TutorialApplication::handleQuit, this));
  }
  bool handleQuit(const CEGUI::EventArgs& e)
  {
     static_cast(mFrameListener)->requestShutdown();
     return true;
  }

重写 GuiFrameListener 类来响应键盘和鼠标输入

  class GuiFrameListener : public ExampleFrameListener, public MouseMotionListener, public MouseListener
  {
  private:
     CEGUI::Renderer* mGUIRenderer;
     bool mShutdownRequested;
  public:
     // NB using buffered input
     GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer)  : ExampleFrameListener(win, cam, true, true), 
           mGUIRenderer(renderer),
           mShutdownRequested(false)
     {
         mEventProcessor->addMouseMotionListener(this);
         mEventProcessor->addMouseListener(this);
         mEventProcessor->addKeyListener(this);
     }
     // Tell the frame listener to exit at the end of the next frame
     void requestShutdown(void)
     {
         mShutdownRequested = true;
     }
     bool frameEnded(const FrameEvent& evt)
     {
         if (mShutdownRequested)
             return false;
         else
             return ExampleFrameListener::frameEnded(evt);
     }
     void mouseMoved (MouseEvent *e)
     {
         CEGUI::System::getSingleton().injectMouseMove(
                 e->getRelX() * mGUIRenderer->getWidth(), 
                 e->getRelY() * mGUIRenderer->getHeight());
         e->consume();
     }
     void mouseDragged (MouseEvent *e) 
     { 
         mouseMoved(e);
     }
     void mousePressed (MouseEvent *e)
     {
         CEGUI::System::getSingleton().injectMouseButtonDown(
                convertOgreButtonToCegui(e->getButtonID()));
         e->consume();
     }
     void mouseReleased (MouseEvent *e)
     {
         CEGUI::System::getSingleton().injectMouseButtonUp(
           convertOgreButtonToCegui(e->getButtonID()));
         e->consume();
     }
     void mouseClicked(MouseEvent* e) {}
     void mouseEntered(MouseEvent* e) {}
     void mouseExited(MouseEvent* e) {}
     void keyPressed(KeyEvent* e)
     {
         if(e->getKey() == KC_ESCAPE)
         {
             mShutdownRequested = true;
             e->consume();
             return;
         }
         CEGUI::System::getSingleton().injectKeyDown(e->getKey());
         CEGUI::System::getSingleton().injectChar(e->getKeyChar());
         e->consume();
     }
     void keyReleased(KeyEvent* e)
     {
         CEGUI::System::getSingleton().injectKeyUp(e->getKey());
         e->consume();
     }
     void keyClicked(KeyEvent* e) 
     {
         // Do nothing
         e->consume();
     }
  };

Ogre 1.4.0

如果你使用的是Ogre 1.4.0 你将会要使用OIS。在Ogre3d中使用OIS的更多细节,请参阅使用OIS并且再看一看基础教程5:

  class GuiFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
  {
  private:
    CEGUI::Renderer* mGUIRenderer;
    bool mShutdownRequested;
  public:
    // NB using buffered input
    GuiFrameListener(RenderWindow* win, Camera* cam, CEGUI::Renderer* renderer)  : ExampleFrameListener(win, cam, true, true), 
          mGUIRenderer(renderer),
          mShutdownRequested(false)
    {
         mMouse->setEventCallback( this );
         mKeyboard->setEventCallback( this );
    }
    // Tell the frame listener to exit at the end of the next frame
    void requestShutdown(void)
    {
        mShutdownRequested = true;
    }
    bool frameEnded(const FrameEvent& evt)
    {
        if (mShutdownRequested)
            return false;
       else
            return ExampleFrameListener::frameEnded(evt);
    }
     bool mouseMoved( const OIS::MouseEvent &e ) 
    {
        using namespace OIS;
        CEGUI::System::getSingleton().injectMouseMove(e.state.X.rel,e.state.Y.rel);
        return true;
   }
    bool mousePressed (const OIS::MouseEvent &e, OIS::MouseButtonID id)
    {
        CEGUI::System::getSingleton().injectMouseButtonDown(convertOgreButtonToCegui(id));
        return true;
    }
   bool mouseReleased( const OIS::MouseEvent &e, OIS::MouseButtonID id )
    {
        CEGUI::System::getSingleton().injectMouseButtonUp(convertOgreButtonToCegui(id));
        return true;
    }
    bool keyPressed( const OIS::KeyEvent &e )
    {
        if(e.key == OIS::KC_ESCAPE)
        {
            mShutdownRequested = true;
            return true;
        }
        CEGUI::System::getSingleton().injectKeyDown(e.key);
        CEGUI::System::getSingleton().injectChar(e.text);
        return true;
    }
    bool keyReleased( const OIS::KeyEvent &e )
    {
        CEGUI::System::getSingleton().injectKeyUp(e.key);
        return true;
    }
  };

在include语句后GuiFrameListener声明前添加下面代码

  CEGUI::MouseButton convertOgreButtonToCegui(int buttonID)
  {
     switch (buttonID)
     {
     case MouseEvent::BUTTON0_MASK:
         return CEGUI::LeftButton;
     case MouseEvent::BUTTON1_MASK:
         return CEGUI::RightButton;
     case MouseEvent::BUTTON2_MASK:
         return CEGUI::MiddleButton;
     case MouseEvent::BUTTON3_MASK:
         return CEGUI::X1Button;
     default:
         return CEGUI::LeftButton;
     }
  }

Ogre 1.4.0

  CEGUI::MouseButton convertOgreButtonToCegui(int buttonID)
  {
      using namespace OIS; 
      switch (buttonID)
      {
      case OIS::MB_Left:
          return CEGUI::LeftButton;
      case OIS::MB_Right:
          return CEGUI::RightButton;
      case OIS::MB_Middle:
          return CEGUI::MiddleButton;
      default:
          return CEGUI::LeftButton;
      }
  }

将下面语句添加到创建场景方法(createscene)的末尾。

  setupEventHandlers();

现在你可以编译并执行程序了。实现效果是点击按钮后退出。

[编辑]加载设置(Layout)

CEGUI使用XML格式来加载图形用户界面样式设置。复制下面xml代码到记事本,并将其以“Tutoral Gui.xml”命名另存在“/media/gui”文件夹下。

    
      
          
               
               
               
               
          
  

Ogre 1.4.0

    
      
          
               
               
               
               
          
  

( 补充说明:在复制上面的.xml代码保存时,请手动删除行前的空格.否则会编译出错. editBy自由骑士笃志2008-04-25 )

现在将程序中下列代码段注释掉

  mEditorGuiSheet= CEGUI::WindowManager::getSingleton().createWindow((CEGUI::utf8*)"DefaultWindow", (CEGUI::utf8*)"Sheet"); 
  mGUISystem->setGUISheet(mEditorGuiSheet);
  CEGUI::PushButton* quitButton = (CEGUI::PushButton*)CEGUI::WindowManager::getSingleton().createWindow
                                          ("TaharezLook/Button",  (CEGUI::utf8*)"Quit");
  mEditorGuiSheet->addChildWindow(quitButton);
  quitButton->setPosition(CEGUI::Point(0.35f, 0.45f));
  quitButton->setSize(CEGUI::Size(0.3f, 0.1f));
  quitButton->setText("Quit");

在同样位置添加下列代码

  mEditorGuiSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"Tutorial Gui.xml");
  mGUISystem->setGUISheet(mEditorGuiSheet);
  CEGUI::PushButton* quitButton=(CEGUI::PushButton*)CEGUI:: 
          WindowManager::getSingleton().getWindow((CEGUI::utf8*)"Quit");

最后一行多余的,因为我们没有在之后使用指针,但是其说明了如何通过加载文件来进行访问。 注意:我们在创建xml文件时要根据实际窗口进行设计。

完成后编译并执行,程序在外观上并没有变化。

[编辑]尝试

•视线相交和选取Ogre mesh—当鼠标没有从GUI元素上移过。 •定义一个在GUI根菜单上的鼠标点击动作。当你鼠标点击一个不在根窗口中的GUI元素时,它将会响应你的鼠标点击。如果你的鼠标并没有从一个 GUI元素上滑过(也就是说当你的鼠标指针还在我们的3D场景中)则根窗口响应鼠标点击。 •将鼠标点击转换到世界坐标系和视线相交((Camera::getCamera)到ViewportRay(mouseX, mouseY))

  // Start a new ray query
   Ogre::Ray cameraRay = root::getSingleton( ).
     getCamera( )->getCameraToViewportRay( mouseX, mouseY );
   Ogre::RaySceneQuery *raySceneQuery = root::getSingleton( ).
     getSceneManager( )->createRayQuery( cameraRay );
   raySceneQuery->execute( );
   Ogre::RaySceneQueryResult result = raySceneQuery->getLastResults( );
   Ogre::MovableObject *closestObject = NULL;
   real closestDistance = LONG_MAX;
   std::list< Ogre::RaySceneQueryResultEntry >::iterator rayIterator;
   for ( rayIterator = result.begin( );
   rayIterator != result.end( );
   rayIterator++ ) {
   if ( ( *rayIterator ).movable->getUserObject( ) != NULL ) 
      {
        if ( ( *rayIterator ).distance < closestDistance ) 
           {
              closestObject = ( *rayIterator ).movable;
              closestDistance = ( *rayIterator ).distance;
           }
      }
   }
   // No object clicked
   if ( closestObject == NULL ) {   
     clickedObject = NULL;                                  ---- clickedObject is a class scoped variable
   } else {
     clickedObject = static_cast< object* >( closestObject->getUserObject( ) );
   }
   raySceneQuery->clearResults( );
   root::getSingleton( ).getSceneManager( )->destroyQuery( raySceneQuery )

[编辑]如何在两个GUI(用户界面)之间转换(使用透明度)

例如:如果你有一个登陆界面,在成功登陆后,进入了你的用户主界面。你将会想在这两个界面间切换。 •第一步,加载登陆用户界面。

    //First loading with this
   mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000,mSceneMgr);
   mGUISystem = new CEGUI::System(mGUIRenderer);
   CEGUI::Logger::getSingleton().setLoggingLevel(CEGUI::Informative);
   CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"WindowsLook.scheme");
   mGUISystem->setDefaultMouseCursor((CEGUI::utf8*)"WindowsLook", (CEGUI::utf8*)"MouseArrow");
   CEGUI::Font *f = CEGUI::FontManager::getSingleton().createFont("Commonwealth-10.font");
   mGUISystem->setDefaultFont(f);
   //End "first loading with this"
   //Load a XML file
   mEditorGuiSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"Presentation.xml");
   mGUISystem->setGUISheet(mEditorGuiSheet);

•第二步,如果你想删除并重建一个GUI,你需要做到以下:

   if(mEditorGuiSheet)
      CEGUI::WindowManager::getSingleton().destroyWindow(mEditorGuiSheet);

•最后一步,加载其他的GUI

   mEditorGuiSheet = CEGUI::WindowManager::getSingleton().loadWindowLayout((CEGUI::utf8*)"Futura.xml");
   mGUISystem->setGUISheet(mEditorGuiSheet);

重做第二步和最后一步来加载其他GUI。

[编辑]结论

这个教程为你展示了在Ogre3D下使用CEGUI的一些基本方法,你可以感受下使用CUEGUI编程的乐趣:)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CEGUI(Crazy Eddie’s GUI http://www.cegui.org.uk)是一个自由免费的GUI库,基于LGPL协议,使用C++实现,完全面向对象设计。CEGUI开发者的目的是希望能够让游戏开发人员从繁琐的GUI实现细节中抽身出来,以便有更多的开发时间可以放在游戏性上。 CEGUI的渲染需要3D图形API的支持,如OpenGL或Direct3D。另外,使用更高级的图形库也是可以的,比如OGRE、Irrlicht和RenderWare等,关键需求可以简化为二点: 纹理(Texture)的支持直接写屏(RHW的顶点格式、正交投影、或者使用shader实现) 本书截止日时,CEGUI的最新版本是0.6.0(本书的讨论也是基于此版本),本书光盘提供了SDK和全部源码的下载。 除此之外,CEGUI还同步提供了官方界面编辑器LayoutEditor和ImageSet编辑器,以方便UI和图像集的制作。作为界面编辑器,它需要系统级界面以提供编辑器操作,0.3.0版是基于MFC实现的;而在0.4.0版本以后,改为基于wxWidgets(跨平台的本地UI框架,这里的UI指Window操作系统底层,如:Windows、Unix和Mac,详见http://www.wxwidgets.org)实现。 目前将CEGUI作为游戏界面库开发的游戏已经有好多种,国内的天龙八部,巨人等游戏就是很好的例子。 CEGUI的功能是非常强大的,而且使用也非常的灵活,可以和脚本配合。可以通过配置文件自定义窗口外观。通过布局文件实现窗口布局等等特性,使得游戏的界面开发更加方便。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值