OGRE 选中物体

选中物体

这次对于所有的鼠标事件,我创建了封闭函数来处理它们。当用户按下鼠标左键,"onLeftPressed"函数被调用,当按下右键时"onRightReleased"函数被调用,等等。

一如既往,首先给出框架代码:

   #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)
       {
           // Setup default variables
           mCount = 0;
           mCurrentObject = NULL;
           mLMouseDown = false;
           mRMouseDown = false;
           mSceneMgr = sceneManager;
  
           // Reduce move speed
           mMoveSpeed = 50;
           mRotateSpeed /= 500;
  
           // Register this so that we get mouse events.
           mMouse->setEventCallback(this);
  
           // Create RaySceneQuery
           mRaySceneQuery = mSceneMgr->createRayQuery(Ray());
       } // MouseQueryListener
  
       ~MouseQueryListener()
       {
           mSceneMgr->destroyQuery(mRaySceneQuery);
       }
  
       bool frameStarted(const FrameEvent &evt)
       {
           // 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;
  
           // Setup the scene query
           Vector3 camPos = mCamera->getPosition();
           Ray cameraRay(Vector3(camPos.x, 5000.0f, camPos.z), Vector3::NEGATIVE_UNIT_Y);
           mRaySceneQuery->setRay(cameraRay);
  
           // Perform the scene query
           RaySceneQueryResult &result = mRaySceneQuery->execute();
           RaySceneQueryResult::iterator itr = result.begin();
  
           // Get the results, set the camera height
           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;
       }
  
      
       bool mouseReleased(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
       {
           // Left mouse button up
           if (id == OIS::MB_Left)
           {
               onLeftReleased(arg);
               mLMouseDown = false;
           } // if
  
           // Right mouse button up
           else if (id == OIS::MB_Right)
           {
               onRightReleased(arg);
               mRMouseDown = false;
           } // else if
  
           return true;
       }
  
       bool mousePressed(const OIS::MouseEvent &arg, OIS::MouseButtonID id)
       {
           // Left mouse button down
           if (id == OIS::MB_Left)
           {
               onLeftPressed(arg);
               mLMouseDown = true;
           } // if
  
           // Right mouse button down
           else if (id == OIS::MB_Right)
           {
               onRightPressed(arg);
               mRMouseDown = true;
           } // else if
  
           return true;
       }
  
       bool mouseMoved(const OIS::MouseEvent &arg)
       {
           // 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)
           {
               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
  
           // 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
  
           return true;
       }
  
       // Specific handlers
       void onLeftPressed(const OIS::MouseEvent &arg)
       {
           // 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)
           {
               char name[16];
               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
       }
  
       void onLeftReleased(const OIS::MouseEvent &arg)
       {
       }
  
       void onRightPressed(const OIS::MouseEvent &arg)
       {
           CEGUI::MouseCursor::getSingleton().hide();
       }
  
       virtual void onRightReleased(const OIS::MouseEvent &arg)
       {
           CEGUI::MouseCursor::getSingleton().show();
       }
  
   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)
       {
           // 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 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");
       }
  
       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
           MessageBoxA(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;
   }


    运行程序会发现程序运行的结果和手札19给出的代码,即手札18的程序的最终代码运行结果是一样的,只不过正如前面说的将相应实践放到了封闭的函数中了,你应该花一些时间来消化这些不同之处!

一、实现选中物体

 

    最终能够做到当你放置物体后,能“拾取”并移动它。我们希望用户知道他目前正在操纵哪一个物体。在游戏里,我们可能以某种特殊的方式来高亮这个物体。而在这里我们用showBoundingBox方法来创建一个围绕该物体的方盒。当鼠标首次按下时,取消旧的选择物体上的包围盒,然后当选择了一新物体时,给新物体加上包围盒。为此,我们

在onLeftPressed函数的开头添加如下代码:

      // 打开包围盒
      if (mCurrentObject)
          mCurrentObject->showBoundingBox(false);

并在onLeftPressed末尾添加代码:

      // Show the bounding box to highlight the selected object
      if (mCurrentObject)
          mCurrentObject->showBoundingBox(true);
运行你的程序发现单击创建的机器人会有一圈的白框

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

二、实现添加不同种类的物体

 

实现代码不仅能够添加机器人还能够放置和移动忍者。

我们需要一个“机器人模式”和一个“忍者模式”,来决定在屏幕上放置的物体。

我们把空格键设置成切换按钮,并且显示信息提示用户目前处于哪一种模式。

 

首先,我们把MouseQueryListener设置成机器人模式。我们添加一个变量来保存物体状态

 

在MouseQueryListener类的protected变量区域添加这个变量:

       bool mRobotMode;   //变量用于标明当前状态

 

 在MouseQueryListener类的构造函数进行初始化:

       // 设置文本、缺省状态
        mRobotMode = true;
        mDebugText = "Robot Mode Enabled - Press Space to Toggle";

下面有修改单击响应代码,单击时根据mRobotMode的状态来断定应当添加什么模型:

在onLeftPressed里修改代码:

将这三句

         char name[16];
         sprintf(name, "Robot%d", mCount++);
   
         Entity *ent = mSceneMgr->createEntity(name, "robot.mesh");

修改为:

          Entity *ent;
          char name[16];

          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这些代码大家一定能够看懂我就不解释了


下面绑定空格键,来改变状态。

在frameStarted里(第一个if语句之后)添加:

     // 切换模式
        if(mKeyboard->isKeyDown(OIS::KC_SPACE) && mTimeUntilNextToggle <= 0)
       {
               mRobotMode = !mRobotMode;
               mTimeUntilNextToggle = 1;
               mDebugText = (mRobotMode ? String("Robot") : String("Ninja")) + " Mode Enabled - Press Space to Toggle";
       } 

 

看看效果吧!

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

 

三、选中物体

 

首先介绍一下下面这个结构体,你可以通过连接查看的类结构

 

RaySceneQueryResultEntry

RaySceneQueryResult返回一个RaySceneQueryResultEntry结构体的iterator。

这个结构体包含三个变量。

distance变量告诉你这个物体沿着射线有多远。另外两个变量的其中一个将是null(movable或者worldFramegment)。

movable变量包含一个MovableObject对象(MovableObject基本上可以是任何你能绑在SceneNode上的对象(像实体、光源,等))你可以看一下后面那个继承关系图

如果与射线相交的话。如果射线接触到一个地形片段,worldFragment将保存这个worldFragment对象(比如地形)。

大多数RaySceneQueries的应用包括选取和操纵MovableObject对象,以及它们所绑定到的SceneNodes 。调用getName方法获取MovableObject的名称。调用getParentSceneNode(或getParentNode)获取它们所绑定到的SceneNode。如果RaySceneQueryResultEntry的结果不是一个MovableObject,movable变量则为null。

 

 WorldFragment是完全另一种怪物。当RaySceneQueryResult中的worldFragment成员被设置时,就意味着返回结果是SceneManager创建的世界几何(world geometry)的一部分。返回的world fragment的类型是基于SceneManager的。它是这样实现的,WorldFragment结构体包含一个fragmentType变量,以指明world fragment的类型。基于这个fragmentType变量,设置其它成员变量(singleIntersection, planes, geometry, 或者renderOp)。一般来说,RaySceneQueries只返回WFT_SINGLE_INTERSECTION类型的WorldFragments。singleIntersection变量只是一个Vector3,用来报告交点的坐标。

 

介绍到这里,书归正传

    我们要做的另一件事情是“拾起”并拖拽已经被放置的物体。当前你若点击一个已经放置的物体,程序会忽略它,并在它后面放置另一个机器人。我们现在来修正它。

 

    首先要保证当我们点击鼠标,我们能得到沿射线上的第一个东西。为此,我们需要设置RaySceneQuery按深度排序。找到onLeftPressed函数里的如下代码:

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

修改成这样(两处变化):



      // Setup the ray scene query

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

      mRaySceneQuery->setSortByDistance(true);//按顺序返回结果
      // Execute query

      RaySceneQueryResult &result = mRaySceneQuery->execute();

      RaySceneQueryResult::iterator itr;

以下这段代码是我们要重写的,因为我们不能在单击时在创建实体   

        // Get results, create a node/entity on the position
           if (itr != result.end() && itr->worldFragment)
           {
          Entity *ent;
          char name[16];

          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这些代码大家一定能够看懂我就不解释了


               mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
               mCurrentObject->attachObject(ent);
               mCurrentObject->setScale(0.1f, 0.1f, 0.1f);
           } // if

          }

实现能够选取已经放置在屏幕上的物体分为两步。

 

首先,如果用户点击一个物体,则使mCurrentObject等于它的父节点。

如果用户没有点击在物体上(而是点在地型上)时,就像以前一样放置一个新的机器人。

 

第一个要做的修改就是,使用一个for循环来代替if语句:

       for ( itr = result.begin(); itr != result.end(); itr++ )
      {
   if (itr->movable && itr->movable->getName().substr(0, 5) != "tile[")
          {
              mCurrentObject = itr->movable->getParentSceneNode();
              break;
    // 首先我们要检查第一个交点的是不是一个MovableObject
     //如果是,我们把它的父节点赋给mCurrentObject。还要做另一个判断
     //TerrainSceneManager会为地型本身创建MovableObject
     //所以我们可能实际上会与他们相交。为了修正这个问题,我通过检查对象的名称来保证
     //它们的名称不类似于地型名称。一个典型的地形名称比如"tile[0][0,2]"。
     //最后,注意这个break语句。我们只需要在第一个物体上做操作,一旦我们找到一个合法的,

     //我们就应该跳出循环。
          } // if
    else if (itr->worldFragment)
          {
              Entity *ent;
              char name[16];
              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
              mCurrentObject = mSceneMgr->getRootSceneNode()->createChildSceneNode(String(name) + "Node", itr->worldFragment->singleIntersection);
              mCurrentObject->attachObject(ent);
              mCurrentObject->setScale(0.1f, 0.1f, 0.1f);
              break;
          } // else if
      } // for

 

运行你的程序可以实现选中了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值