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.