Rozpoczęcie Zaczniemy pracę z poniższym kodem. Przy pracy z Ms C++ i SDK proszę pamiętać, żeby podczas tworzenia nowego projektu zanaczyć opcję "CEGUI Support".
#define OGRE_CHANGE1 ((1 # 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)
// Domyślne ustawienia zmiennych mCount = 0; mCurrentObject = NULL; mLMouseDown = false; mRMouseDown = false; mSceneMgr = sceneManager;
// Redukujemy szybkość ruchów kamerą mMoveSpeed = 50; mRotateSpeed *= 2;
// Rejestrujemy tę procedurę w celu uzyskania zdarzeń myszki. mEventProcessor->addMouseListener( this ); mEventProcessor->addMouseMotionListener( this );
// Tworzymy zapytanie RaySceneQuery mRaySceneQuery = mSceneMgr->createRayQuery( Ray( ) ); } // MouseQueryListener
~MouseQueryListener( )
// Stworzyliśmy zapytanie a więc musimy je też usunąć. delete mRaySceneQuery;
bool frameStarted(const FrameEvent &evt)
// Przetwarzamy kod bazowego frame listenera. Musimy to zrobić // zanim zaczniemy manipulować wektorem translacji. if ( !ExampleFrameListener::frameStarted( evt ) ) return false;
// Ustawienie zapytania Vector3 camPos = mCamera->getPosition( ); Ray cameraRay( Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y ); mRaySceneQuery->setRay( cameraRay );
// Wykonanie zapytania RaySceneQueryResult &result = mRaySceneQuery->execute(); RaySceneQueryResult::iterator itr = result.begin( );
// Pobieramy rezultat i ustawiamy wysokość kamery if ( itr != result.end() && itr->worldFragment )
Real terrainHeight = itr->worldFragment->singleIntersection.y; if ((terrainHeight + 10.0f) > camPos.y) mCamera->setPosition( camPos.x, terrainHeight + 10.0f, camPos.z );
return true;
virtual void onLeftPressed( MouseEvent *e )
// Ustawienie zapytania Ray mouseRay = mCamera->getCameraToViewportRay( e->getX(), e->getY() ); mRaySceneQuery->setRay( mouseRay );
// Wykonanie zapytania RaySceneQueryResult &result = mRaySceneQuery->execute(); RaySceneQueryResult::iterator itr = result.begin( );
// Pobranie wyniku, utworzenie obiektu w pozycji kursora myszki if ( itr != result.end() && itr->worldFragment )
char name; sprintf( name, "Robot%d", mCount++ );
Entity *ent = mSceneMgr->createEntity( name, "robot.mesh" ); mCurrentObject = mSceneMgr->getRootSceneNode( )->createChildSceneNode( String(name) + "Node", itr->worldFragment->singleIntersection ); mCurrentObject->attachObject( ent ); mCurrentObject->setScale( 0.1f, 0.1f, 0.1f ); } // if
virtual void onRightPressed( MouseEvent *e )
CEGUI::MouseCursor::getSingleton().hide( );
virtual void onLeftReleased( MouseEvent *e )
virtual void onRightReleased( MouseEvent *e )
CEGUI::MouseCursor::getSingleton().show( );
virtual void onLeftDragged( MouseEvent *e )
Ray mouseRay = mCamera->getCameraToViewportRay( e->getX(), e->getY() ); mRaySceneQuery->setRay( mouseRay );
RaySceneQueryResult &result = mRaySceneQuery->execute(); RaySceneQueryResult::iterator itr = result.begin( );
if ( itr != result.end() && itr->worldFragment )
mCurrentObject->setPosition( itr->worldFragment->singleIntersection ); } // if
virtual void onRightDragged( MouseEvent *e )
mCamera->yaw( -e->getRelX() * mRotateSpeed * 2 ); mCamera->pitch( -e->getRelY() * mRotateSpeed * 2 );
/* Zdarzenia MouseListenera. */ virtual void mouseClicked(MouseEvent* e) { } virtual void mouseEntered(MouseEvent* e) { } virtual void mouseExited(MouseEvent* e) { }
// Kiedy naciskamy klawisz. virtual void mousePressed(MouseEvent* e)
// Naciskamy lewy klawisz if (e->getButtonID() & MouseEvent::BUTTON0_MASK)
mLMouseDown = true; onLeftPressed( e ); } // if
// Naciskamy prawy klawisz else if (e->getButtonID() & MouseEvent::BUTTON1_MASK)
mRMouseDown = true; onRightPressed( e ); } // else if } // mousePressed
// Kiedy puszczamy klawisz. virtual void mouseReleased(MouseEvent* e)
// Puszczamy lewy klawisz. if (e->getButtonID() & MouseEvent::BUTTON0_MASK)
mLMouseDown = false; onLeftReleased( e ); } // if
// Puszczamy prawy klawisz. else if (e->getButtonID() & MouseEvent::BUTTON1_MASK)
mRMouseDown = false; onRightReleased( e ); } // else if } // mouseReleased
/* Zdarzenia MouseMotionListenera */ virtual void mouseMoved (MouseEvent *e)
// Uaktualnienie CEGUI według ruchów myszki CEGUI::System::getSingleton().injectMouseMove(e->getRelX() * mGUIRenderer->getWidth(), e->getRelY() * mGUIRenderer->getHeight());
// Wywoływane gdy następuje przeciąganie myszką. virtual void mouseDragged (MouseEvent *e)
// Uaktualnienie CEGUI według ruchów myszki mouseMoved(e);
// Kiedy przęciągamy lewym klawiszem i mamy obiekt na kursorze if ( mLMouseDown )
onLeftDragged( e ); } // if
// Kiedy przeciągamy prawym klawiszem. else if ( mRMouseDown )
onRightDragged( e ); } // else if } // mouseDragged
protected: RaySceneQuery *mRaySceneQuery; // Wskaźnik zapytania bool mLMouseDown, mRMouseDown; // Prawda gdy odpowiedni klawisz myszki jest naciśnięty int mCount; // Ilość utworzonych robotów SceneManager *mSceneMgr; // Wskaźnik do managera sceny SceneNode *mCurrentObject; // Nowo tworzony obiekt CEGUI::Renderer *mGUIRenderer; // Renderer CEGUI
class MouseQueryApplication : public ExampleApplication { protected: CEGUI::OgreCEGUIRenderer *mGUIRenderer; CEGUI::System *mGUISystem; public: MouseQueryApplication() : mGUIRenderer(0), mGUISystem(0)
~MouseQueryApplication()
delete mGUISystem; delete mGUIRenderer;
protected: void chooseSceneManager(void)
// Tworzymy managera sceny # if OGRE_VERSION getSceneManager( ST_EXTERIOR_CLOSE ); # else //[Ogre > 1.1] mSceneMgr = mRoot->createSceneManager( ST_EXTERIOR_CLOSE); # endif
void createScene(void)
// Ustawiamy światło otoczenia mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5)); mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);
// Pobieramy geometrię terenu mSceneMgr->setWorldGeometry( "terrain.cfg" );
// Ustawiamy kamerę mCamera->setPosition( 40, 100, 580 ); mCamera->pitch( Degree(-30) ); mCamera->yaw( Degree(-45) );
// Ustawiamy CEGUI # if OGRE_VERSION 1.1] mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr); # endif mGUISystem = new CEGUI::System(mGUIRenderer);
// Myszka //Zwróć uwagę na właściwe nazwy. Może wystąpić TaharezLookSkin. CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLook.scheme"); CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");
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 == PLATFORM_WIN32 || 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; }
Zanim przejdziemy dalej, sprawdź czy możesz skompilować i wykonać powyższy kod. Powinien on robić to samo co opisywaliśmy w poprzednim artykule.
Pokazanie, który obiekt jest zaznaczony W tym artykule rozbudujemy kod tak by można było zaznaczać już wcześniej umieszczone obiekty w terenie a następnie je przesuwać. Chcemy mieć możliwość pokazania użytkownikowi, który obiekt wybrał. W grze, prawdopodobnie wybralibyście jakiś specjalny sposób zaznaczania, ale w tym przykładzie użyjemy prostej metody showBoundingBox tworzącej prostopadłościan dookoła obiektu.
Nasz pomysł polega na wyłączeniu "pudełka" otaczającego poprzednio wybrany obiekt i właczeniu go dookoła nowo wybranego. W tym celu musimy doać poniższy kod na początku zdarzenia onLeftPressed:
// Wyłaczamy "pudełko" if ( mCurrentObject ) mCurrentObject->showBoundingBox( false );
a poniższy na samym końcu tego samego zdarzenia:
// Włączamy "pudełko" if ( mCurrentObject ) mCurrentObject->showBoundingBox( true );
W tym momencie mCurrentObject zawsze będzie zaznaczony na ekranie.
Dodajemy Ninja Dodatkowo rozbudujemy nasz kod tak by obsługiwał nie tylko roboty ale również umożliwiał wstawianie i przesuwanie wojowników ninja. Musimy zatem mieć "tryb robota" i "tryb ninja", które będą determinować jaki obiekt mamy wstawić na powierzchnię gruntu. Użyjemy do tego celu klawisza spacji jako przełącznika a dodatkowo będziemy wyświetlać na ekranie informacje w jakim trybie w danej chwili się znajdujemy.
Najpierw ustawimy MouseQueryListener tak by był na początku w trybie robota. Musimy dodać zmienną która będzie trzymać stan obiektu (czyli będzie wskazywać nasz tryb). Znajdź chronioną sekcję MouseQueryListener i dodaj zmienną:
bool mRobotMode; // bieżący tryb
A teraz dodaj ten kod na końcu konstruktora MouseQueryListener:
// Ustawiamy wstępne wartości trybu i tekstu pomocy mRobotMode = true; mWindow->setDebugText( "Tryb [Robot] - by przelaczyc nacisnij klawisz spacji" );
I jesteśmy w trybie robota. Teraz musimy wprowadzić możliwość tworzenia obiektu na podstawie definicji robota lub ninji w zależności od zmiennej mRobotMode. Odnajdź taki kawałek kodu w funkcji onLeftPressed:
char name; sprintf( name, "Robot%d", mCount++ );
Entity *ent = mSceneMgr->createEntity( name, "robot.mesh" );
Zamień go na poniższy. W zależności od trybu zapisanego w mRobotMode pobieramy inną definicję i wstawiamy inną nazwę:
Entity *ent; char name;
if ( mRobotMode )
sprintf( name, "Robot%d", mCount++ ); ent = mSceneMgr->createEntity( name, "robot.mesh" ); } // if else
sprintf( name, "Ninja%d", mCount++ ); ent = mSceneMgr->createEntity( name, "ninja.mesh" ); } // else
Prawie skończone. Teraz tylko trzeba przypisać klawisz spacji do przełaczania trybu. Odszukaj następujący kod w frameStarted:
if ( !ExampleFrameListener::frameStarted( evt ) ) return false;
I zaraz za nim wstaw poniższy kod:
// Przełącza tryb if (mInputDevice->isKeyDown(KC_SPACE) && mTimeUntilNextToggle setDebugText( "Tryb ["+(mRobotMode ? String("Robot") : String("Ninja")) + "] - by przelaczyc nacisnij klawisz spacji" );
Zrobione. Skompiluj kod i uruchom. Powinieneś mieć możliwość wybrania obiektu, który chcesz wstawić.
Wybieranie obiektów A teraz zanurzmy się w najważniejszą część tego artykułu: użycie zapytań RaySceneQueries do wybierania obiektów na ekranie. Zanim jednak zaczniemy modyfikować nasz kod, pozwólcie, że wyjaśnię bardziej dokładnie RaySceneQueryResultEntry. (Proszę klikać na linki i przeglądać na bieżąco strukturę.)
RaySceneQueryResult zwraca iterator do struktur RaySceneQueryResultEntry. Struktura ta zawiera trzy zmienne. Zmienna distance mówi jak daleko znajduje się obiekt wskazany promieniem. Jedna z pozostałych zmiennych nie może być pusta. Zmiena movable zawiera MovableObject jeżeli promień dotknął jakiegoś. Zmienna worldFragment zawiera obiekt WorldFragment jeżeli promień dotknął jakiegoś fragmentu świata, np. terenu.