射线使用-碰撞检测

来源:互联网 发布:python爬虫scrapy 编辑:程序博客网 时间:2024/05/07 03:56

射线使用-碰撞检测

我们首先实现对鼠标右键的响应是他能够自由查看

然后实现摄像机不能穿越地面

最后单击左键增加实体,并能在单机状态下移动实体

 

首先,一如既往,提供程序运行的代码框架

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

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

运行结果应当是一片漆黑


一、创建场景(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));

既然我们建立了基本的世界空间,那么就要打开光标。打开光标,要使用CEGUI函数调用。

创建一个OgreCEGUIRenderer,然后创建系统对象并将刚创建的Renderer传给它。

创建mGUIRenderer时必须以最后一个参数告诉CEGUI你要用那个场景管理器

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

你可以运行一下你的程序,看到有天,有地,还有一个不会动的图形化光标

——————————————————————————————————————————————

二、将鼠标右键绑定到“鼠标观察”模式

 首先进行变量介绍(这些变量框架代码已经提供了)

    RaySceneQuery *mRaySceneQuery;      // 射线场景查询指针,寻找地面上的坐标


    bool mLMouseDown, mRMouseDown;     // 如果按下鼠标按钮,返回True


    int mCount;                        // 屏幕上机器人的数量,实体数


    SceneManager *mSceneMgr;           // 指向场景管理器的指针


    SceneNode *mCurrentObject;    //指向最近创建的场景节点的指针(我们将用这个“拖拽”实体)。


    CEGUI::Renderer *mGUIRenderer;     // CEGUI渲染器


MouseQueryListener的构造函数中添加代码:

        // Setup default variables
        mCount = 0;
        mCurrentObject = NULL;
        mLMouseDown = false;
        mRMouseDown = false;
        mSceneMgr = sceneManager;

        // Reduce move speed
        mMoveSpeed = 50;
        mRotateSpeed /= 500;

//为了MouseQueryListener能收到鼠标事件,我们必须把它注册为一个鼠标监听器。

        // Register this so that we get mouse events.
        mMouse->setEventCallback(this);
//用场景管理器的一个调用创建一个RaySceneQuery对象

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

 

//创建一个RaySceneQuery,以后我们就必须销毁它。

~MouseQueryListener()析构函数增加代码:

        // We created the query, and we are also responsible for deleting it.
        mSceneMgr->destroyQuery(mRaySceneQuery);

mouseMoved中增加代码:

      // Update CEGUI with the mouse motion
       CEGUI::System::getSingleton().injectMouseMove(arg.state.X.rel, arg.state.Y.rel);

 

 

      // 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


 

 

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

 

mouseReleased中增加代码:
        //右键抬起时,我们需要再次显示光标,并将mRMouseDown设置为false。

          // 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

试一下你的摄像机是否能受右键控制吧!

____________________________________________________________________________

三、地形碰撞检测

    在BaseFrameListener移动了摄像机后,我们要确保摄像机在地面以上10个单位处。如果它不在,我们要把它移到那儿。

frameStarted中增加代码(删除原来的return语句):

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

//因为ExampleFrameListener的frameStarted成员函数移动摄像机,并且在此发生后我们需要在函数中安排

//我们的剩余行动。我们的目标及时找到摄像机的当前位置,并沿着它向地面发射一条射线。这被称为射线

//场景查询,它会告诉我们我们下面的地面的高度。得到了摄像机的当前位置后,我们需要创建一条射线。

//这条射线有一个起点(射线开始的地方),和一个方向(此处y轴负方向)。

 

       // Setup the scene query
       Vector3 camPos = mCamera->getPosition();
       Ray cameraRay(Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y);

       //一旦我们创建了射线,我们就告诉RaySceneQuery对象使用它。 
       mRaySceneQuery->setRay(cameraRay);
       //使用了5000.0f高度代替了摄像机的实际位置。

 

       //现在我们需要执行查询,得到结果。查询结果是std::iterator类型的。

       // Perform the scene query
        RaySceneQueryResult &result = mRaySceneQuery->execute();
        RaySceneQueryResult::iterator itr = result.begin();

//begin方法获得迭代器的第一个元素。如果result.begin() == result.end(),那么无返回结果

 

//保证至少返回一个查询结果(itr != result.end()),那个结果是地面(itr->worldFragment)。

// Get the results, set the camera height
        if (itr != result.end() && itr->worldFragment)
        {
//worldFragment结构包含有在变量singleIntersection(一个Vector3)中射线击中地面的位置。
//我们要得到地面的高度,依靠将这个向量的Y值赋值给一个本地变量。
//一旦我们有了高度,我们就要检查摄像机是否低于这一高度,如果低于这一高度,
//那么我们要将摄像机向上移动至地面高度。
//注意,我们实际将摄像机多移动了10个单位。这样保证我们不能由于太靠近地面而看穿地面。

            Real terrainHeight = itr->worldFragment->singleIntersection.y;
            if ((terrainHeight + 10.0f) > camPos.y)
                mCamera->setPosition( camPos.x, terrainHeight + 10.0f, camPos.z );
        }

        return true;

运行一下,看你还能否“入地”!

 

_______________________________________________________________________________________

 

 


 

 四、添加对象并实现拖拽

    每次点击鼠标左键,我们将向屏幕上创建和添加对象。每次你点击、按住鼠标左键,就会创建一个对象并跟随你的光标。你可以移动对象,直到你松开鼠标左键,同时对象也锁定在那一点上。要做到这些,我们需要改变mousePressed函数。

在mousePressed()中的if语句添加代码:

       // 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));
// Camera::getCameraToViewpointRay;一个将屏幕上的点击(X和Y坐标)转换成一条可供RaySceneQuery对象使用的射线的好用函数
               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(也就是点击的位置),就可以创建对象并把它放到位。
       //解决唯一命名问题
                 char name[16];
                 sprintf( name, "Robot%d", mCount++ );
                 //用itr->worldFragment->singleIntersection作为我们的机器人的默认位置

                  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

在mousemoved()的if(mLMouseDown){}中添加代码:

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

原创粉丝点击