• ogre3D学习基础10 -- 键盘控制与鼠标控制(直接控制)


    要实现键盘,鼠标对场景的控制,首先要帧监听,就是在每一帧的渲染前后对它进行操作。这里的操作没有用到缓冲区,只是简单的直接获取。

      1、这些步骤和前面的一样,直接上代码,操作还是在createScene函数里。代码如下:

     1     void createScene()
     2     {
     3         //载入实体
     4         mSceneMgr->setAmbientLight(ColourValue(0.25,0.25,0.25));//设置环境色
     5         Entity *ent1 = mSceneMgr->createEntity("ninja","ninja.mesh");
     6         SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninjaNode");
     7         node1->attachObject(ent1);
     8 
     9         //点光源
    10         Light *light = mSceneMgr->createLight("light1");
    11         light->setType(Light::LT_POINT);//点光源
    12         light->setPosition(Vector3(250,150,250));
    13         light->setDiffuseColour(ColourValue::White);//漫射光
    14         light->setSpecularColour(ColourValue::White);//反射光
    15 
    16         //第一个摄像机节点
    17         node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1",Vector3(-400,200,400));
    18         node1->yaw(Degree(-45));//绕y轴顺时针旋转45度
    19         node1->attachObject(mCamera);
    20         //第二个摄像机节点
    21         node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2",Vector3(0,200,400));
    22     }

      这些代码就不解释了,如果不懂,请往前看。

      2、绑定摄像机到节点

    1 void createCamera()
    2     {
    3         //其他参数选择默认
    4         mCamera = mSceneMgr->createCamera("PlayerCam");
    5         mCamera->setNearClipDistance(5);
    6     }

      3、新建一个类ExampleTestFrameListener,父类为ExampleFrameListener,对鼠标和键盘的控制主要在这里面进行。其中是定义在ExampleApplication.h中的一个类,负责帧监听。构造函数如下:ExampleTestFrameListener(RenderWindow *win,Camera *cam,SceneManager *sceneMgr);

      现在开始逐渐对他进行完善:

      首先添加成员变量

    1 bool mMouseDown; // 鼠标左键是否在上一帧被按下
    2 Real mToggle; // 直到下一次触发的时间
    3 Real mRotate; // 滚动常量
    4 Real mMove; // 移动常量
    5 SceneManager *mSceneMgr; // 当前场景管理器
    6 SceneNode *mCamNode; // 当前摄像机所附在的场景节点

      在构造函数里对他们进行初始化

     1 ExampleTestFrameListener(RenderWindow *win,Camera *cam,SceneManager *sceneMgr)
     2         :ExampleFrameListener(win,cam,false,false)//第三个参数指明是否使用带缓冲的键盘输入,第四个参数指明是否使用带缓冲的鼠标输入
     3     {
     4         mMouseDown = false;//键盘状态记录
     5         mToggle = 0;//鼠标状态记录
     6         mCamNode = cam->getParentSceneNode();//始终指向父节点
     7         mSceneMgr = sceneMgr;
     8         mRotate = 0.13;
     9         mMove = 250;
    10     }

      然后我们在每一帧里执行动作,方法就是在frameStarted()写入我们的操作。这个函数会在每一帧渲染前执行。

      第一,按下鼠标左键打开关闭灯光。

        首先获取鼠标的控制权,代码:mMouse->capture();//俘获鼠标

        然后读取鼠标按键的当前状态,代码不难理解。

          bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left);//获取鼠标当前的状态

        最后判断是否更改灯的状态

        1 if(currMouse && !mMouseDown)
        2         {
        3             Light *light = mSceneMgr->getLight("light1");//获取光源指针
        4             light->setVisible(!light->isVisible());//根据上一次的状态改变,相当于取反,由开到关,由关到开。
        5         }
        6 
        7         mMouseDown = currMouse;//更新鼠标的状态

        关于鼠标:

        通常 0 是鼠标左键,1 是右键,2 是中键。在某些系统里 1 是 中键、2 是右键。所以我们读取鼠标状态没有使用数字,而是使用了更加健壮的代码

        mMouse->getMouseState().buttonDown(OIS::MB_Left);这样能避免由于系统差别造成的问题。

      第二步,用键盘实现上下左右前后的移动。

        首先,也是捕获键盘 mKeyboard->capture();//俘获键盘

        其次,实现按下数字键1,2时切换不同的摄像机

       1      mToggle -= evt.timeSinceLastFrame;//减去从上一帧到现在所经历的时间,保证正在渲染时不切换摄像机
       2         if ((mToggle <0.0f) && mKeyboard->isKeyDown(OIS::KC_1))//按下数字键1时选择摄像机1
       3         {
       4             mToggle = 0.5f;//
       5             mCamera->getParentSceneNode()->detachObject(mCamera);//将当前摄像机从场景节点上卸载下来
       6 mCamNode = mSceneMgr->getSceneNode("CamNode1");//获取摄像机1的指针    7 mCamNode->attachObject(mCamera);//重新绑定新的摄像机    8 }    9 else if ((mToggle <0.0f) && mKeyboard->isKeyDown(OIS::KC_2))//按下数字键1时选择摄像机1   10 {   11 mToggle = 0.5f;//   12 mCamera->getParentSceneNode()->detachObject(mCamera);//将当前摄像机从场景节点上卸载下来   13 mCamNode = mSceneMgr->getSceneNode("CamNode2");//获取摄像机2的指针   14 mCamNode->attachObject(mCamera);//重新绑定新的摄像机   15 }

        其中的变量mToggle很关键,可以保证每一帧的渲染操作都是原子操作,不被中途打断,避免产生不可预测问题。 

        再次,实现按键W,A,S,D的平移,这里需要一个三元组transVector3用来保存当前摄像机的位置。

          实现前后的移动,其实就是让坐标z增大或者减小,代码如下

      1      Vector3 transVector = Vector3::ZERO;//保存平移方位
      2         if (mKeyboard->isKeyDown(OIS::KC_UP) || mKeyboard->isKeyDown(OIS::KC_W))//
      3         {
      4             transVector.z -= mMove;//z轴负方向移动
      5         }
      6         if (mKeyboard->isKeyDown(OIS::KC_DOWN) || mKeyboard->isKeyDown(OIS::KC_S))//
      7         {
      8             transVector.z += mMove;//z轴正方向移动
      9         }

          其他的移动操作,基本一样,只需改变z为x或y。具体代码在最后面。

        OIS:  

        开放输入系统(OIS)提供了三个主要的类来获得输入:Keyboard, Mouse, 和Joystick(摇杆) 。

      第三,实现按下鼠标右键时以一定倾斜角度拖动摄像机

        按下鼠标右键时拖动,摄像机以一定的倾斜角度改变

           //根据从上一帧开始鼠标移动的距离来控制摄像机的俯仰偏斜。
            if (mMouse->getMouseState().buttonDown(OIS::MB_Right))
            {
                mCamNode->yaw(Degree(-mRotate * mMouse->getMouseState().X.rel),Node::TS_WORLD);//以y为基础旋转
                mCamNode->pitch(Degree(-mRotate * mMouse->getMouseState().Y.rel),Node::TS_LOCAL);//以x为基础旋转
            }

      第四,在ExampleTestApplication中注册帧监听器

        具体操作时在createFrameListener()函数中,添加以下几行代码

      1      mFrameListener = new ExampleTestFrameListener(mWindow,mCamera,mSceneMgr);//创建一个帧监听器
      2         mRoot->addFrameListener(mFrameListener);//注册到Root类中
      3         mFrameListener->showDebugOverlay(true);//在层上显示帧状态

      好了,完整代码:

      

      1 #include "ExampleApplication.h"
      2 using namespace Ogre;
      3 
      4 class ExampleTestFrameListener:public ExampleFrameListener
      5 {
      6 public:
      7     ExampleTestFrameListener(RenderWindow *win,Camera *cam,SceneManager *sceneMgr)
      8         :ExampleFrameListener(win,cam,false,false)//第三个参数指明是否使用
      9         //带缓冲的键盘输入,第四个参数指明是否使用带缓冲的鼠标输入(
     10     {
     11         mMouseDown = false;// 键盘和鼠标状态追踪
     12         mToggle = 0;
     13 
     14         mCamNode = cam->getParentSceneNode();
     15         mSceneMgr = sceneMgr;
     16         //设置旋转和移动速度
     17         mRotate = 0.13;
     18         mMove = 250;
     19     }
     20     bool frameStarted(const FrameEvent& evt)
     21     {
     22         mMouse->capture();//俘获鼠标
     23         mKeyboard->capture();//俘获键盘
     24 
     25         if (mKeyboard->isKeyDown(OIS::KC_ESCAPE))//按下esc键
     26         {
     27             return false;
     28         }
     29 
     30         bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left);//获取鼠标当前的状态
     31         if(currMouse && !mMouseDown)
     32         {
     33             Light *light = mSceneMgr->getLight("light1");//获取灯光指针
     34             light->setVisible(!light->isVisible());//根据上一次的状态改变
     35         }
     36 
     37         mMouseDown = currMouse;//更新鼠标状态
     38 
     39         mToggle -= evt.timeSinceLastFrame;//减去从上一帧到现在所经历的时间,保证正在渲染时不切换摄像机
     40         if ((mToggle <0.0f) && mKeyboard->isKeyDown(OIS::KC_1))//按下数字键1时选择摄像机1
     41         {
     42             mToggle = 0.5f;
     43             mCamera->getParentSceneNode()->detachObject(mCamera);
     44             mCamNode = mSceneMgr->getSceneNode("CamNode1");
     45             mCamNode->attachObject(mCamera);
     46         }
     47         else if ((mToggle <0.0f) && mKeyboard->isKeyDown(OIS::KC_2))//按下数字键1时选择摄像机1
     48         {
     49             mToggle = 0.5f;
     50             mCamera->getParentSceneNode()->detachObject(mCamera);
     51             mCamNode = mSceneMgr->getSceneNode("CamNode2");
     52             mCamNode->attachObject(mCamera);
     53         }
     54 
     55         Vector3 transVector = Vector3::ZERO;//保存平移方位
     56         if (mKeyboard->isKeyDown(OIS::KC_UP) || mKeyboard->isKeyDown(OIS::KC_W))//
     57         {
     58             transVector.z -= mMove;//z轴负方向移动
     59         }
     60         if (mKeyboard->isKeyDown(OIS::KC_DOWN) || mKeyboard->isKeyDown(OIS::KC_S))//
     61         {
     62             transVector.z += mMove;//z轴正方向移动
     63         }
     64         if (mKeyboard->isKeyDown(OIS::KC_LEFT) || mKeyboard->isKeyDown(OIS::KC_A))//
     65         {
     66             transVector.x -= mMove;//x轴负方向移动
     67         }
     68         if (mKeyboard->isKeyDown(OIS::KC_RIGHT) || mKeyboard->isKeyDown(OIS::KC_D))//
     69         {
     70             transVector.x += mMove;//x轴正方向移动
     71         }
     72         if (mKeyboard->isKeyDown(OIS::KC_PGUP) || mKeyboard->isKeyDown(OIS::KC_Q))//
     73         {
     74             transVector.y -= mMove;//Y轴负方向移动
     75         }
     76         if (mKeyboard->isKeyDown(OIS::KC_PGDOWN) || mKeyboard->isKeyDown(OIS::KC_E))//
     77         {
     78             transVector.y += mMove;//Y轴正方向移动
     79         }
     80 
     81         //根据从上一帧开始鼠标移动的距离来控制摄像机的俯仰偏斜。
     82         if (mMouse->getMouseState().buttonDown(OIS::MB_Right))
     83         {
     84             mCamNode->yaw(Degree(-mRotate * mMouse->getMouseState().X.rel),Node::TS_WORLD);
     85             mCamNode->pitch(Degree(-mRotate * mMouse->getMouseState().Y.rel),Node::TS_LOCAL);
     86         }
     87         return true;
     88         //return ExampleFrameListener::frameStarted(evt);
     89     }
     90     bool frameEnded(const FrameEvent& evt)
     91     {
     92         return ExampleFrameListener::frameEnded(evt);
     93     }
     94 protected:
     95     bool mMouseDown;// 鼠标左键是否在上一帧被按下
     96     Real mToggle;//直到下一次触发的时间间隔
     97     Real mRotate;//滚动常量
     98     Real mMove;//移动常量
     99     SceneManager *mSceneMgr;//当前的场景管理器
    100     SceneNode *mCamNode;//当前的摄像机所绑定的场景节点
    101 private:
    102 };
    103 
    104 
    105 class ExampleTestApplication:public ExampleApplication
    106 {
    107 public:
    108     ExampleTestApplication()
    109     {
    110 
    111     }
    112     ~ExampleTestApplication()
    113     {
    114 
    115     }
    116 
    117 protected:
    118     void createCamera()
    119     {
    120         //其他参数选择默认
    121         mCamera = mSceneMgr->createCamera("PlayerCam");
    122         mCamera->setNearClipDistance(5);
    123     }
    124     void createScene()
    125     {
    126         //载入实体
    127         mSceneMgr->setAmbientLight(ColourValue(0.25,0.25,0.25));//设置环境色
    128         Entity *ent1 = mSceneMgr->createEntity("ninja","ninja.mesh");
    129         SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninjaNode");
    130         node1->attachObject(ent1);
    131 
    132         //点光源
    133         Light *light = mSceneMgr->createLight("light1");
    134         light->setType(Light::LT_POINT);//点光源
    135         light->setPosition(Vector3(250,150,250));
    136         light->setDiffuseColour(ColourValue::White);//漫射光
    137         light->setSpecularColour(ColourValue::White);//反射光
    138 
    139         //第一个摄像机节点
    140         node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1",Vector3(-400,200,400));
    141         node1->yaw(Degree(-45));//绕y轴顺时针旋转45度
    142         node1->attachObject(mCamera);
    143         //第二个摄像机节点
    144         node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2",Vector3(0,200,400));
    145     }
    146     void createFrameListener()
    147     {
    148         mFrameListener = new ExampleTestFrameListener(mWindow,mCamera,mSceneMgr);//创建一个帧监听器
    149         mRoot->addFrameListener(mFrameListener);//注册到Root类中
    150         mFrameListener->showDebugOverlay(true);//在层上显示帧状态
    151     }
    152 };
    153 
    154 #include "windows.h"
    155 
    156 
    157 
    158 INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
    159 
    160 
    161 {
    162     ExampleTestApplication app;
    163         app.go();
    164     return 0;
    165 }
    全部代码

      图片就不上了,本来想截取几个gif格式的动画上来的,可一直没找到好工具。

  • 相关阅读:
    2013-11-23 sentence patterns
    面试题 盛水 twitter
    Uva 10025 The ? 1 ? 2 ? ... ? n = k problem
    FTP服务(5)客户连接常见故障现象
    FTP服务(4)基于MySQL数据库认证
    FTP服务(3)实现基于文件验证的vsftpd虚拟用户
    FTP服务(2)实现基于SSL的FTPS
    FTP服务(1)
    Apache httpd服务
    Apache httpd服务——常用配置
  • 原文地址:https://www.cnblogs.com/songliquan/p/3302228.html
Copyright © 2020-2023  润新知