Rozpoczęcie Podobnie jak to wcześniej bywało zaczniemy od pewnego, bazowego kodu, który będziemy rozszerzać. Nazwijmy plik z tym kodem MouseQuery.cpp. Program będzie korzystać z CEGUI zatem przypadku użycia VC++ i SDK należy pamiętać o zanaczeniu opcji "CEGUI Support" w opcjach tworzonego projektu. Pamiętaj też o dostosowaniu kodu do wersji Ogre. Jeżeli używasz Ogre 1.0 odkomentuj linię po każdym komentarzu "//[Ogre 1.0]" i zakomentuj jedną linię po każdym komentarzu "//[Ogre 1.2]".

#include #include #include

#include "ExampleApplication.h"

class MouseQueryListener : public ExampleFrameListener, public MouseListener, public MouseMotionListener { public:

MouseQueryListener(RenderWindow* win, Camera* cam, SceneManager *sceneManager, CEGUI::Renderer *renderer) : ExampleFrameListener(win, cam, false, true), mGUIRenderer(renderer)

} // MouseQueryListener

~MouseQueryListener( )

bool frameStarted(const FrameEvent &evt)

return ExampleFrameListener::frameStarted( evt );

/* zdarzenia MouseListenera. */ virtual void mouseClicked(MouseEvent* e) { } virtual void mouseEntered(MouseEvent* e) { } virtual void mouseExited(MouseEvent* e) { }

// Te wywoływane jest przy naciśnieciu dowolnego klawisza myszy virtual void mousePressed(MouseEvent* e)

} // mousePressed

// Te wywoływane jest gdy klawisz myszy jest zwalniany α virtual void mouseReleased(MouseEvent* e)

} // mouseReleased

/* zdarzenia MouseMotionListenera */ virtual void mouseMoved (MouseEvent *e)

} // mouseMoved

// To wywoływane jest gdy nacisnąwszy klawisz trzymamy go i przesuwamy myszką. virtual void mouseDragged (MouseEvent *e)

} // mouseDragged

protected: RaySceneQuery *mRaySceneQuery; // wskaźnik do zapytania ray scene bool mLMouseDown, mRMouseDown; // prawda gdy klawisze myszki są wciśnięte int mCount; // ilość robotów na scenie SceneManager *mSceneMgr; // wskaźnik do managera sceny SceneNode *mCurrentObject; // nowo tworzony obiekt CEGUI::Renderer *mGUIRenderer; // cegui renderer

class MouseQueryApplication : public ExampleApplication { protected: CEGUI::OgreCEGUIRenderer *mGUIRenderer; CEGUI::System *mGUISystem; // cegui system public: MouseQueryApplication()

~MouseQueryApplication()

protected: void chooseSceneManager(void)

// Use the terrain scene manager. //[Ogre 1.0] //mSceneMgr = mRoot->getSceneManager( ST_EXTERIOR_CLOSE ); //[Ogre 1.2] mSceneMgr = mRoot->createSceneManager( ST_EXTERIOR_CLOSE);

void createScene(void)

void createFrameListener(void)

mFrameListener= new MouseQueryListener(mWindow, mCamera, mSceneMgr, mGUIRenderer); mFrameListener->showDebugOverlay(true); mRoot->addFrameListener(mFrameListener);

#if OGRE_PLATFORM == PLATFORM_WIN32 || 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 { // tworzymy obiekt aplikacji MouseQueryApplication 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; }

Upewnij się, że możesz bez błędów skompilować powyższy kod.

Ustawienie sceny Odszukaj metodę createScene. Kod, który w nią wprowadzimy powinien być dla Ciebie znajomy.

// Ustawienie światła otoczenia mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5)); mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

// Wczytanie geometrii terenu mSceneMgr->setWorldGeometry( "terrain.cfg" );

// Ustawienie pozycji kamery mCamera->setPosition( 40, 100, 580 ); mCamera->pitch( Degree(-30) ); mCamera->yaw( Degree(-45) );

Po ewentualnym przekompilowaniu i uruchomieniu zobaczymy standardowy obrazek szarego terenu pod płynącymi chmurami. Teraz potrzebujemy właczyć kursor myszki. Jednak zanim to będziemy mogli zrobić trzeba uruchomić CEGUI. Po pierwsze tworzymy obiekt OgreCEGUIRenderer a następnie obiekt System, który przekazujemy do stworzonego Rendera. Opis specyfikacji ustawieć CEGUI zostawimy do poruszenia w innym artykule. Jedyne co powinienieś wiedzieć to to, że zawsze trzeba podać CEGUI z którego SceneManagera korzystamy co uczyniono za pomocą ostatniego parametru przekazanego do mGUIRenderer. Pamiętaj o dostosowaniu kodu do uzywanej wersji CEGUI. W starszej wersji ww. wywołanie ma inne parametry.

// start CEGUI //[Stare CEGUI] //mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, ST_EXTERIOR_CLOSE); //[Obecne CEGUI] mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr); mGUISystem = new CEGUI::System(mGUIRenderer);

Teraz wyświetlimy kursor. Tu też nie będziemy wgłębiali się w szczegóły pozostawiając to na inny artykuł.

// Kursor myszy CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLook.scheme"); CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");

Gdy teraz skompilujesz i uruchomisz kod, zobaczysz dodatkowo kursor w środku ekranu. [Uwaga: jeśli wystąpią błędy z komentarzem "can't find TaharezLook..." lub ekran będzie ciemny] popróbuj zmienić użyć nazwy "TaharezLookSkin.scheme". (Jeśli dalej występują błędy odszukaj plik definicji schematu, który znajduje się w katalogu media/gui. Sprawdź jak się naprawdę nazywa i czy NAZWA w linii "GUIScheme Name=NAZWA" jest taka sama. Sprawdź też czy umieszczone w nim wskazania na inne pliki podane są prawidłowo.)

Rozpoczynamy pracę z FrameListenerem Wszystko co było potrzebne wprowadziliśmy do naszej aplikacji. Teraz musimy wprowadzić kod zarządzający tymi elementami. Robiąc to będziemy modyfikować FrameListener. Spróbujmy podsumować czego bedziemy od niego wymagać:

Chcemy połączyć ruch myszki z wciśniętym prawym przyciskiem z trybem zmiany kierunku patrzenia kamery. W chwili obecnej najbardziej dziwaczną rzeczą jest niemożność obejrzenia całego otoczenia za pomocą poruszenia myszką i przywrócenie tego będzie naszym pierwszym zadaniem. Po drugie zablokujemy możliwość przechodzenia kamery przez powierzchnię ziemi. Sytuacja znalezienia się "pod ziemią" nie jest oczekiwana w tego typu programach. Po trzecie chcemy uzyskać możliwość stawiania obiektów (entities) w dowolnym miejscu na powierzchni ziemi wskazanym myszką za pomocą kliknięcia lewym przyciskiem. Wreszcie chcielibyśmy uzyskać możliwość przesuwania obiektów. Powinno byc to możliwe po najechaniu na obiekt myszką, nacisnięcie lewego klawisza i bez jego puszczania przesunięcie myszką na nowe miejsce gdzie zwalniamy klawisz, czyli tak zwany "drag-drop". W tym celu użyjemy kilku chronionych zmiennych (które już są dodane do klasy):

RaySceneQuery *mRaySceneQuery; // wskaźnik do zapytania ray scene bool mLMouseDown, mRMouseDown; // prawda gdy klawisz myszki jest wciśnięty int mCount; // ilość robotów na ekranie SceneManager *mSceneMgr; // wskaźnik do managera sceny SceneNode *mCurrentObject; // nowo tworzony obiekt CEGUI::Renderer *mGUIRenderer; // cegui renderer

Zmienna mRaySceneQuery utrzymuje kopię RaySceneQuery którą będziemy używali do odszukania współrzędnych w terenie. Zmienne mLMouseDown i mRMouseDown będą nas informować czy i który klawisz myszki jest wciśnięty (np. mLMouseDown zawierać będzie prawdę gdy użytkownik wciśnie lewy klawisz myszki, w przeciwnym przypadku będzie tam fałsz). mCount zlicza ilość obiektów wstawianych na podłoże. mCurrentObject zawiera wskaźnik do najświerzszej utworzonej SceneNode (będziemy jej używać do "przeciągania" obiektów). Wreszcie, mGUIRenderer zawiera wskaźnik do Rendera CEGUI, który będzie używany do uaktualniania środowiska CEGUI.

Zauważy, że mamy też wiele funkcji związanych z listenerami myszki. Nie będziemy ich wszystkich uzywać w tym programie, ale muszą zostać w nim zdefiniowane gdyż w przeciwnym razie nie zdołamy prawidłowo skompilować naszego programu.

Konfigurujemy FrameListenera Odszukaj konstruktor MouseQueryListener i dodaj następujący kod inicjalizacyjny. Proszę również zauważyć, że zmniejszamy szybkość przesuwania kamery (w przód i w tył) tworząc złudzenie powiększenia przestrzeni a zarazem zwiększyliśmy szybkość jej obrotu.

// Ustawienie domyślnych wartości zmiennych mCount = 0; mCurrentObject = NULL; mLMouseDown = false; mRMouseDown = false; mSceneMgr = sceneManager;

// Zmiana szybkości kamery mMoveSpeed = 50; mRotateSpeed *= 2;

By umożliwić MouseQueryListenerowi odbieranie zdarzeń myszki, musimy zarejestrować go jako MouseListener i jako MouseMotionListener. Sprawdź w Ogre API reference jakie metody są zdefiniowane dla MouseListener i dla MouseMotionListener.

// Rejestrujemy tak by odbierać zdarzenia myszki. mEventProcessor->addMouseListener( this ); mEventProcessor->addMouseMotionListener( this );

Wreszcie w konstruktorze musimy utworzyć obiekt RaySceneQuery. Robimy to wywołując funkcję managera sceny.

// Tworzymy RaySceneQuery mRaySceneQuery = mSceneMgr->createRayQuery( Ray() );

To wszystko co musimy zrobić w konstruktorze, jednak ponieważ utworzyliśmy nowy obiekt RaySceneQuery, musimy także zadbać o jego zniszczenie. Odszukaj destruktor MouseQueryListener (~MouseQueryListener) i dodaj do niego poniższą linię:

// Wcześniej stworzylismy zapytanie, teraz musimy zadbać o jego usunięcie. delete mRaySceneQuery;

Przed przejściem do następnej sekcji sprawdź czy obecny kod da się prawidłowo skompilować.

Dodajemy "Rozglądanie się" za pomocą myszy Postaramy się teraz dodać do naszego programu możliwość zmiany kierunku patrzenia kamery przy przesuwaniu myszki z naciśniętym prawym klawiszem. By to dokonać musimy:

uaktualnić środowisko CEGUI gdy mysz się porusza (przecież kursor też się porusza); ustawić mRMouseButton tak by wskazywał prawdę gdy prawy klawisz zostanie wciśnięty; ustawić mRMouseButton tak by wskazywał fałsz gdy klawisz zostanie puszczony; zmienić widok gdy będziemy "przeciągać" myszką (klawisz naciśnięty i trzymany podczas ruchu) ukryć kursor gdy "przeciągamy"

Odszukaj metodę MouseQueryListener::mouseMoved. Dodamy do niej poniższy kod powodujący przesuwanie kursora za każdym razem gdy poruszymy myszką.

// Uaktualnij CEGUI przy ruchu myszki CEGUI::System::getSingleton().injectMouseMove(e->getRelX() * mGUIRenderer->getWidth(), e->getRelY() * mGUIRenderer->getHeight());

Ponieważ w momencie rozpoczęcia "przeciągania" ruchy myszki przesyłane są już do innego zdarzenia (a przecież pozycja kursora musi i wtedy się zmieniać) musimy dodać uaktualnienie także na poczatku metody mouseDragged:

// Uaktualnij CEGUI przy ruchu myszki mouseMoved(e);

Teraz odszukaj metodę MouseQueryListener::mousePressed. Ten kawałek kodu będzie zapewniał ukrywanie kursora gdy naciśniemy prawy klawisz myszki a zarazem będzie zmieniał stan mLMouseDown i mRMouseDown na prawdę.

// naciśnięty lewy klawisz myszki if (e->getButtonID() & InputEvent::BUTTON0_MASK)

mLMouseDown = true; } // if

// naciśnięty prawy klawisz myszki else if (e->getButtonID() & InputEvent::BUTTON1_MASK)

CEGUI::MouseCursor::getSingleton().hide( ); mRMouseDown = true; } // else if

Teraz musimy pokazać na nowo kursor a także zmienić mLMouseDown i mRMouseDown na fałsz gdy puścimy odpowiedni klawisz. Odszukaj metodę mouseReleased i dodaj poniższy kod:

// lewy klawisz myszki został puszczony if (e->getButtonID() & InputEvent::BUTTON0_MASK)

mLMouseDown = false; } // if

// prawy klawisz myszki został puszczony else if (e->getButtonID() & InputEvent::BUTTON1_MASK)

CEGUI::MouseCursor::getSingleton().show( ); mRMouseDown = false; } // else if