'Ogre3D 삽질란/Intermediate Tutorial 2'에 해당되는 글 3건

  1. 2008.12.10 중급 튜토리얼 2-1 2
  2. 2008.12.10 중급 튜토리얼 2-2
  3. 2008.12.10 중급 튜토리얼 2-3 (마지막)

중급 튜토리얼 2-1

Ogre3D 삽질란/Intermediate Tutorial 2 2008. 12. 10. 18:33

중급 튜토리얼 2 (번역 : n_Sys)

중급 튜토리얼 2: RaySceneQuery, Mouse 조작법 기본

튜토리얼 진행에 있어서 문제가 생긴다면 Help 포럼 문의하세요.

 

목차

                               1 소개

                               2 사전 요구사항

                               3 시작하기

                               4 장면 설정

                               5 FrameListener 소개

                               6 FrameListener 설정

                               7 Mouse Look 추가

                               8 지형충돌 감지

                               9 지형 선택

                               10 심화학습용 문제들

                                       10.1 쉬운 문제

                                       10.2 보통 문제

                                       10.3 어려운 문제

                                       10.4 고급 문제

 

소개

이번 튜토리얼에서는 기초적인 장면 에디터를 만들예정입니다. 진행하면서 다음 내용들을 다룹니다 :

1.    RaySceneQuery 이용하여 지형밖으로 카메라가 이탈하는 것을 방지하는법

2.    the MouseListener MouseMotionListener 인터페이스의 사용법

3.    마우스를 이용해서 지형의 x, y좌표를 선택하는법

튜토리얼에 대한 코드는 여기 찾을 있습니다. 내용을 스스로 천천히 진행하면서 완성되어지는 결과물을 직접 확인하세요.

 

사전 요구사항

튜토리얼은 여러분이 Ogre project 어떻게 설정하고 컴파일 하는지에 대해서 이미 알고있다고 가정합니다. 오우거의 기본적인 객체들(장면노드, 엔티티, 기타등등) 알고 있어야 합니다. 튜토리얼에서 쓰이는 STL 반복자에 대한 지식도 필요합니다. (오우거에서 STL 사용이 잦습니다. STL 대해서 익숙치 않다면 짬을내서 배워두시는게 좋을겁니다.)

 

시작하기

데모를 만들기위해서 새로운 프로젝트를 생성합니다. "MouseQuery.cpp" 이름의 파일을 추가하고 다음 내용을 추가하세요 :

#include <CEGUI/CEGUISystem.h>

#include <CEGUI/CEGUISchemeManager.h>

#include <OgreCEGUIRenderer.h>

 

#include "ExampleApplication.h"

 

class MouseQueryListener : public ExampleFrameListener, public OIS::MouseListener

{

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);

    }

 

    /* MouseListener callbacks. */

    bool mouseMoved(const OIS::MouseEvent &arg)

    {

        return true;

    }

 

    bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)

    {

        return true;

    }

 

    bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)

    {

        return true;

    }

 

 

protected:

    RaySceneQuery *mRaySceneQuery;     // The ray scene query pointer

    bool mLMouseDown, mRMouseDown;     // True if the mouse buttons are down

    int mCount;                        // The number of robots on the screen

    SceneManager *mSceneMgr;           // A pointer to the scene manager

    SceneNode *mCurrentObject;         // The newly created object

    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.

        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

{

    // Create application object

    MouseQueryApplication app;

 

    try {

        app.go();

    } catch(Exception& e) {

#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32

        MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);

#else

        fprintf(stderr, "An exception has occurred: %s\n",

            e.getFullDescription().c_str());

#endif

    }

 

    return 0;

}


컴파일이 제대로 되는지 확인하세요.

 

:

중급 튜토리얼 2-2

Ogre3D 삽질란/Intermediate Tutorial 2 2008. 12. 10. 18:32

장면 설정하기

MouseQueryApplication::createScene함수로 갑시다. 다음코드는 별로 어렵지 않을겁니다. 만약 모르는 코드가 보인다면 API 레퍼런스를 참고하여 먼저 이해한다음 계속 진행하세요. createScene함수에 다음 코드를 추가합니다 :

       // Set ambient light

       mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5));

       mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8);

 

       // World geometry

       mSceneMgr->setWorldGeometry("terrain.cfg");

 

       // Set camera look point

       mCamera->setPosition(40, 100, 580);

       mCamera->pitch(Degree(-30));

       mCamera->yaw(Degree(-45));


기초적인 world geometry 설정되었습니다. 이제 마우스 커서를 보이게 차례입니다. 그럴려면 CEGUI함수를 호출하여야 하므로 먼저 CEGUI 구동시켜야 합니다. 우선 OgreCEGUIRenderer 생성합니다. 다음 시스템 객체를 생성하고 방금 생성시켰던 렌더러를 전달합니다. CEGUI 자세한 설정방법은 나중에 있을 튜토리얼에서 다뤄질 계획입니다. 일단 지금은 CEGUI 어떤 SceneManager 쓰일것인지와 마지막 매개변수로 mGUIRenderer 들어간다는 것만 기억하세요.

       // CEGUI setup

       mGUIRenderer = new CEGUI::OgreCEGUIRenderer(mWindow, Ogre::RENDER_QUEUE_OVERLAY, false, 3000, mSceneMgr);

       mGUISystem = new CEGUI::System(mGUIRenderer);

이제 실제로 마우스 커서를 보이게 차례입니다. 코드에 대한 설명은 생략하겠습니다. 나중에 있을 튜토리얼에서 알려드리겠습니다.

       // Mouse

       CEGUI::SchemeManager::getSingleton().loadScheme((CEGUI::utf8*)"TaharezLookSkin.scheme");

       CEGUI::MouseCursor::getSingleton().setImage("TaharezLook", "MouseArrow");

컴파일 실행시키면 화면 한가운데에서 커서를 발견할 것입니다. 하지만 움직이지는 않습니다(아직은).

 

FrameListener 소개

프로그램에서 필요한건 모두 끝냈습니다. FrameListener 코드에 복잡하게 관여하는 부분입니다. 그러므로 실제로 구현하기전에 뭐가 어떻게 돌아갈지에 대해서 설명을 드려야 구현하기전에 머릿속에서 대충 정리가 것입니다.

*                   첫번째, 마우스 우클릭버튼을 “mouse look”버튼으로 사용할 겁니다. 마우스로 자유롭게 주변상황을 없다는것은 귀찮거든요. 그래서 첫번재 우선과제는 마우스 컨트롤기능을 프로그램에 넣는 입니다(마우스 우클릭을 유지한 상태에서만 동작됩니다).

*                   두번째, 카메라가 지형을 뚫고 지나가지 않게끔 계획입니다. 지형에 가까이 다가갈 수 있도록 보통 흔히들 떠올리는 그런 동작을 보여줄 겁니다.

*                   세번째, 왼쪽클릭으로 지형의 어떤 공간에라도 엔티티를 추가할 있도록 계획입니다.

*                   마지막으로 엔티티를 “drag” 있도록 만들겁니다. 왼쪽버튼을 누르고 유지시키면 엔티티를 놓고 싶은 위치까지 움직일 있게 됩니다. 마우스버튼을 떼면 자리에 위치하게 됩니다.

기능구현을 위해서 몇몇 protected 변수를 사용하게 될겁니다(이미 클래스에 추가되어 있습니다) :

    RaySceneQuery *mRaySceneQuery;     // The ray scene query pointer

    bool mLMouseDown, mRMouseDown;     // True if the mouse buttons are down

    int mCount;                        // The number of robots on the screen

    SceneManager *mSceneMgr;           // A pointer to the scene manager

    SceneNode *mCurrentObject;         // The newly created object

    CEGUI::Renderer *mGUIRenderer;     // cegui renderer

mRaySceneQuery 지형의 좌표를 찾는데 쓰일 RaySceneQuery 복사본을 가집니다. mLMouseDown, mRMouseDown변수는 어떤 마우스 버튼이 눌렸는지를 체크합니다(mLMouseDown변수가 true 왼쪽버튼이 눌린상태로 유지되고 있다는 의미며 false 그렇지 않다는 의미입니다). mCount변수는 화면상에 몇개의 엔티티가 있는지를 헤아립니다. mCurrentObject변수는 가장 최근에 생성된 SceneNode(“drag”하는데 쓰일 엔티티로 사용될겁니다) 가리킵니다. 마지막으로 mGUIRenderer CEGUI 업데이트하는데 사용될 CEGUI 렌더러의 포인터를 가집니다.

Mouse listener에는 수많은 함수들이 연관되어 있습니다. 데모에서 모든 기능을 사용하지는 않지만 적어도 정의는 해둬야 컴파일시 에러를 발생시키지 않습니다.

 

FrameListener 설정

MouseQueryListener생성자로 가서 다음 초기화 코드를 추가하세요. 유심히 보셔야 부분은 지형크기가 줄어듬에 따라서 이동속도와 회전속도역시 감소했다는 입니다.

        // Setup default variables

        mCount = 0;

        mCurrentObject = NULL;

        mLMouseDown = false;

        mRMouseDown = false;

        mSceneMgr = sceneManager;

 

        // Reduce move speed

        mMoveSpeed = 50;

        mRotateSpeed /= 500;

MouseQueryListener 마우스 이벤트를 받기위해서 클래스를 MouseListener로서 등록을 해야 합니다. 무슨말인지 이해가 되지 않는다면 Basic Tutorial 5 참고하세요

        // Register this so that we get mouse events.

        mMouse->setEventCallback(this);

마지막으로 생성자에서 RaySceneQuery객체를 생성해야 합니다. SceneManager에서 해야 하는 호출은 이게 전부입니다:

        // Create RaySceneQuery

        mRaySceneQuery = mSceneMgr->createRayQuery(Ray());

생성자에서 필요한건 모두 했지만 RaySceneQuery 생성했으면 나중에는 파괴시켜줘야 합니다. MouseQueryListener 파괴자로 가서 (~MouseQueryListener) 다음코드를 추가하세요 :

        // We created the query, and we are also responsible for deleting it.

        mSceneMgr->destroyQuery(mRaySceneQuery);

다음 섹션으로 넘어가기전에 컴파일이 제대로 되는지 확인하세요.

 

Mouse Look 추가

마우스의 우클릭을 마우스룩 모드로 바인딩할 차례입니다. 다음과 같은 절차로 구현됩니다 :

*                   마우스가 움직이면 CEGUI 업데이트합니다(커서도 움직입니다)

*                   버튼이 눌려지면 mRMouseButton변수는 true값을 가집니다.

*                   버튼이 떨어지면 mRMouseButton변수는 false 값을 가집니다.

*                   드래그되면 view 바꿉니다(버튼을 누른상태로 움직인다는 의미입니다)

*                   마우스가 드래그중일 경우에는 마우스 커서를 숨깁니다

MouseQueryListener::mouseMoved함수로 가서 마우스가 움직이는 모든 프레임에서 마우스 커서위치를 바꾸는 코드를 넣을겁니다. 다음 코드를 함수로 추가하세요 :

       // Update CEGUI with the mouse motion

       CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);

MouseQueryListener::mousePressed함수로 갑니다. 다음 코드는 마우스 오른쪽클릭 드래깅의 경우 커서를 숨기고 mRMouseDown변수값을 true 만듭니다.

       // Left mouse button down

       if (id == OIS::MB_Left)

       {

           mLMouseDown = true;

       } // if

 

       // Right mouse button down

       else if (id == OIS::MB_Right)

       {

           CEGUI::MouseCursor::getSingleton().hide();

           mRMouseDown = true;

       } // else if

마우스 오른쪽 버튼이 떨어지면 mRMouseDown변수값을 토글시키고 마우스커서를 다시 보여지게 해야 합니다. mouseReleased함수로 가서 다음 코드를 추가하세요 :

       // Left mouse button up

       if (id == OIS::MB_Left)

       {

           mLMouseDown = false;

       } // if

 

       // Right mouse button up

       else if (id == OIS::MB_Right)

       {

           CEGUI::MouseCursor::getSingleton().show();

           mRMouseDown = false;

       } // else if

미리 작성해야할 모든 코드가 완성되었습니다. 이제는 마우스우클릭 드래깅의 경우 view 바꿔야 합니다. 마지막 순간에서 지금까지 마우스가 움직인 거리를 읽어내야 합니다. Basic Tutorial 5 에서 구현했던 카메라 회전과 동일한 방법으로 구현합니다. MouseQueryListener::mouseMoved함수를 찾아서 다음 코드를 return구문 이전에 위치하도록 추가하세요 :

       // If we are dragging the left mouse button.

       if (mLMouseDown)

       {

       } // if

 

       // If we are dragging the right mouse button.

       else if (mRMouseDown)

       {

           mCamera->yaw(Degree(-arg.state.X.rel * mRotateSpeed));

           mCamera->pitch(Degree(-arg.state.Y.rel * mRotateSpeed));

       } // else if

이제 컴파일 실행시키면 마우스우클릭 드래깅으로 주변을 둘러볼 있게 됩니다.

 

지형충돌 감지

이제는 지형을 향해서 이동시 지형을 뚫고 지나가지 못하게끔 만들어 봅시다. BaseFrameListener 이미 카메라이동을 다루고 있기 때문에 그에 관련된 코드는 건드리지 않을겁니다. 대신에 BaseFrameListener 카메라를 움직이고 이후 카메라가 지형으로부터 적어도 10 unit만큼 상위에 위치하게끔 만들겁니다. 만약 충분히 높은위치의 경우에는 제약없이 이동됩니다. 코드를 유심히 읽어주세요. 튜토리얼의 남은부분을 진행하면서 RaySceneQuery 몇가지 기능을 구현할 계획이지만 이번 섹션이후로는 자세한 설명을 드리지 않을겁니다.

MouseQueryListener::frameStarted함수로 가서 모든 내용을 지우세요. 가장 먼저 추가할 코드는 ExampleFrameListener::frameStarted 기본함수를 호출하는 입니다. 만약 false 리턴하면 똑같이 false 리턴합니다.

        // Process the base frame listener code.  Since we are going to be

        // manipulating the translate vector, we need this to happen first.

        if (!ExampleFrameListener::frameStarted(evt))

            return false;

코드는 frameStarted 가장 상위부분에서 수행됩니다. 왜냐하면 ExampleFrameListener's frameStarted함수는 카메라를 이동시키고 함수의 모든 행동은 함수가 수행된 이후에 수행되어야 하기 때문입니다. 우리의 목표는 카메라의 현재 좌표를 얻고 지형을 향해서 수직하단방향으로 광선을 쏩니다. 이것이 바로 RaySceneQuery이며 지형으로부터 얼마나 높이 위치하고 있는지를 알려줍니다. 카메라의 현재위치를 얻은 이후 광선을 생성해야 합니다. 광선은 원점(광선이 시작되는 위치) 방향을 필요로 합니다. 방향에 있어서는 NEGATIVE_UNIT_Y값이 것이며 수직하단방향을 가르킬 겁니다. 광선이 생성되면 RaySceneQuery객체를 통해서 사용합니다.

       // Setup the scene query

       Vector3 camPos = mCamera->getPosition();

       Ray cameraRay(Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y);

       mRaySceneQuery->setRay(cameraRay);

높이를 카메라의 현재위치가 아닌 5000.0f수치를 대신 사용했습니다. 만약 카메라의 실제 Y 위치를 사용한다면 카메라가 지형 아래에 위치할 경우 무조건 지형을 놓치게 됩니다. 이제 쿼리를 실행하여 결과값을 얻습니다. 결과값은 std::iterator형태로 나오게 되는데 간략하게 설명드리겠습니다.

        // Perform the scene query

        RaySceneQueryResult &result = mRaySceneQuery->execute();

        RaySceneQueryResult::iterator itr = result.begin();

간략하게( 심하게 단순화시켜서) 설명드리자면 쿼리 결과값은 worldFragment(지금의 경우는 지형)들과  움직일 있는 객체(나중에 선보일 계획)들의 리스트 입니다. STL 반복자에 대해서 익숙치 않다면 반복자의 요소를 얻는 begin함수만 알아두세요. If the result.begin() == result.end() 경우는 리턴할 결과값이 아무것도 없다는 것을 의미합니다. 다음 데모에서는 다수의 SceneQuery 대한 리턴값을 다루게 것입니다. 지금은 일단 간단한 것만 알아두고 넘어갑시다. 다음코드는 최소한 하나의 결과값(itr != result.end()) 리턴하며 결과값은 지형입니다(itr->worldFragment).

        // Get the results, set the camera height

        if (itr != result.end() && itr->worldFragment)

        {

worldFragment구조체는 광선이 지형 어느 부분에 hit 했는지를 singleIntersection변수(Vector3형식) 담아둡니다. 지형으로부터의 높이를 변수의 y변수값으로부터 얻어낼 있습니다. 높이를 알아낸 다음 만약 카메라가 일정높이 아래에 위치한다면 특정 높이로 카메라를 끌어 올리게 됩니다. 카메라는 지형으로부터 10 unit만큼 떨어지게 것입니다. 그렇게 해서 카메라가 지형으로부터 너무 가까이 위치해서 지형을 뚫고 볼수 없게끔 만들어 줍니다.

            Real terrainHeight = itr->worldFragment->singleIntersection.y;

            if ((terrainHeight + 10.0f) > camPos.y)

                mCamera->setPosition( camPos.x, terrainHeight + 10.0f, camPos.z );

        }

 

        return true;

마지막으로 계속해서 렌더링을 유지하도록 true값을 리턴합니다. 이제 프로그램을 컴파일후 테스트해볼 있습니다.

 

지형 선택

섹션에서는 마우스 왼쪽클릭시 화면상에 객체를 생성하는 기능을 구현합니다. 왼쪽클릭마다 객체가 생성되고 커서에 묶여있게 됩니다. 마우스버튼을 떼기 전까지 객체를 이동시킬 있습니다. 왼쪽클릭시 이러한 동작을 위해서 mousePressed함수의 내용을 변경해야 합니다. MouseQueryListener::mousePressed함수를 찾아서 if 구문 안에 다음 내용을 추가하세요.

       // Left mouse button down

       if (id == OIS::MB_Left)

       {

           mLMouseDown = true;

       } // if

문장은 익숙한 내용입니다. mRaySceneQuery객체에서 사용될 광선을 생성하고 설정합니다. 오우거에서 제공하는 Camera::getCameraToViewportRay함수가 있습니다; 상당히 유용한 함수입니다. 화면상에서 클릭된 좌표(x, y좌표) RaySceneQuery객체에서 사용할 있는 광선으로 변환해 줍니다.

           // Left mouse button down

           if (id == OIS::MB_Left)

           {

               // Setup the ray scene query, use CEGUI's mouse position

               CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();

               Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width), mousePos.d_y/float(arg.state.height));

               mRaySceneQuery->setRay(mouseRay);

쿼리를 실행하고 리턴된 결과값을 확인합니다.

               // Execute query

               RaySceneQueryResult &result = mRaySceneQuery->execute();

               RaySceneQueryResult::iterator itr = result.begin( );

 

               // Get results, create a node/entity on the position

               if (itr != result.end() && itr->worldFragment)

               {

(클릭된 좌표에 대한)worldFragment 얻었고 해당 좌표에 객체를 생성해야 합니다. 첫번째 난관은 오우거에서의 모든 엔티티, SceneNode 중복되지 않는 이름을 가져야 합니다. 문제를 해결하기위해서 엔티티 각각에 대해서 Entity "Robot1", "Robot2", "Robot3"… 이름을 설정하고 SceneNode 대해서는 “Robot1Node", "Robot2Node", "Robot3Node"...이런식으로 설정할 입니다. 우선 이름을 생성합시다(C언어 문법책에서 sprintf 대한 내용을 참고하세요).

               char name[16];

               sprintf( name, "Robot%d", mCount++ );

다음 엔티티와 SceneNode 생성합니다. itr->worldFragment->singleIntersection 통해서 로봇의 최초위치를 설정합니다. 그리고 지형의 크기를 고려해서 로봇을 1/10 크기로 사이즈를 줄입니다. 지금 새롭게 생성된 객체는 mCurrentObject변수에 저장되는것을 눈여겨 봐두세요. mCurrentObject변수는 다음 섹션에서 사용됩니다.

                   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

 

               mLMouseDown = true;

           } // if

컴파일후 실행시켜보세요. 지형의 모든지점에 클릭만으로 로봇을 배치시킬 있습니다. 드래깅만 구현하면 프로그램은 완성됩니다. 이 if 구문 안에 코드를 추가하게 될 것 입니다 :

       // If we are dragging the left mouse button.

        if (mLMouseDown)

        {

        } // if

그다지 설명이 필요없는 코드입니다. 현재 마우스의 위치에서 광선을 생성하고 RaySceneQuery 이용하여 객체를 새로운 위치로 이동시킵니다. mCurrentObject변수의 내용이바른지 아닌지를 판별할 필요가 없는데 이유는 mCurrentObject변수가 mousePressed에서 설정되지 않았다면 mLMouseDown변수가 true값이 될리가 없기때문입니다.

if (mLMouseDown)

       {

           CEGUI::Point mousePos = CEGUI::MouseCursor::getSingleton().getPosition();

           Ray mouseRay = mCamera->getCameraToViewportRay(mousePos.d_x/float(arg.state.width),mousePos.d_y/float(arg.state.height));

           mRaySceneQuery->setRay(mouseRay);

 

           RaySceneQueryResult &result = mRaySceneQuery->execute();

           RaySceneQueryResult::iterator itr = result.begin();

 

           if (itr != result.end() && itr->worldFragment)

               mCurrentObject->setPosition(itr->worldFragment->singleIntersection);

       } // if

컴파일 실행시켜보세요. 완성했습니다! 신경써서 클릭한다면 다음과 같은 결과물도 만들 있습니다 :

사용자 삽입 이미지


주의사항: TerrainSceneManager 이용해서 RaySceneQuery 교차지점을 얻고자 반드시 지형위에서 수행되어야 합니다.

:

중급 튜토리얼 2-3 (마지막)

Ogre3D 삽질란/Intermediate Tutorial 2 2008. 12. 10. 18:30

심화학습용 문제들

쉬운 문제

1.    지형을 바라보기위해 카메라를 지형으로부터 적어도 10 unit만큼 떨어지게끔 했습니다. 10 unit이란 거리는 그냥 적당히 정한 수치입니다. 지형을 꿰뚫지 않고 가까이 있는 수치로 설정할 있을까요? 만약 가능하다면 수치로 static class 멤버변수값으로 설정해 보세요.

2.    SceneEditor 경우 가끔씩은 지형을 통과하고 싶을때가 있습니다. 키보드로 flag값을 토글하여 충돌검사를 on/off 있도록 만들어 보세요. 충돌검사가 off 경우 frameStarted에서 SceneQuery 만들지 마세요.

 

보통 문제

1.    카메라위치가 변경되지 않아도 항상 SceneQuery 수행됩니다. 카메라가 움직인 경우에만 SceneQuery 수행되도록 문제점을 고쳐보세요. (힌트 : ExampleFrameListener translation vector 찾아서 Vector3::ZERO 비교해 보세요.)

 

어려운 문제

1.    장면에서 쿼리를 호출하는 과정에서 중복되는 코드가 너무 많습니다. SceneQuery 관련된 기능을 하나의 protected 함수로 묶으세요. 지형은 항상 선택되지 않는다는 것을 고려해야 합니다.

 

고급 문제

1.    튜토리얼에서는 객체를 지형에 위치시키는데 RaySceneQuery 사용했습니다. 기능은 다양한 목적으로 사용될 있습니다. 우선 튜토리얼1 코드를 가지고 어려운문제 1번을 해결하세요. 그리고 코드를 조합하여 로봇이 텅빈 공간이 아닌 지형위를 걷게끔 만들어 보세요.

2.    지형에서 클릭을 할때마다 로봇이 위치로 이동하게끔 만들어 보세요.

: