必备知识
本教程是在假设你已经拥有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编程的乐趣:)