必備知識
本教程是在假設你已經擁有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編程的樂趣:)