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

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

     void createScene()
{
//载入实体
mSceneMgr->setAmbientLight(ColourValue(0.25,0.25,0.25));//设置环境色
Entity *ent1 = mSceneMgr->createEntity("ninja","ninja.mesh");
SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninjaNode");
node1->attachObject(ent1); //点光源
Light *light = mSceneMgr->createLight("light1");
light->setType(Light::LT_POINT);//点光源
light->setPosition(Vector3(,,));
light->setDiffuseColour(ColourValue::White);//漫射光
light->setSpecularColour(ColourValue::White);//反射光 //第一个摄像机节点
node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1",Vector3(-,,));
node1->yaw(Degree(-));//绕y轴顺时针旋转45度
node1->attachObject(mCamera);
//第二个摄像机节点
node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2",Vector3(,,));
}

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

  2、绑定摄像机到节点

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

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

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

  首先添加成员变量

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

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

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

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

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

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

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

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

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

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

    关于鼠标:

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

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

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

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

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

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

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

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

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

  1      Vector3 transVector = Vector3::ZERO;//保存平移方位
   if (mKeyboard->isKeyDown(OIS::KC_UP) || mKeyboard->isKeyDown(OIS::KC_W))//前
   {
   transVector.z -= mMove;//z轴负方向移动
   }
   if (mKeyboard->isKeyDown(OIS::KC_DOWN) || mKeyboard->isKeyDown(OIS::KC_S))//后
   {
   transVector.z += mMove;//z轴正方向移动
   }

      其他的移动操作,基本一样,只需改变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);//创建一个帧监听器
   mRoot->addFrameListener(mFrameListener);//注册到Root类中
   mFrameListener->showDebugOverlay(true);//在层上显示帧状态

  好了,完整代码:

  

 #include "ExampleApplication.h"
using namespace Ogre; class ExampleTestFrameListener:public ExampleFrameListener
{
public:
ExampleTestFrameListener(RenderWindow *win,Camera *cam,SceneManager *sceneMgr)
:ExampleFrameListener(win,cam,false,false)//第三个参数指明是否使用
//带缓冲的键盘输入,第四个参数指明是否使用带缓冲的鼠标输入(
{
mMouseDown = false;// 键盘和鼠标状态追踪
mToggle = ; mCamNode = cam->getParentSceneNode();
mSceneMgr = sceneMgr;
//设置旋转和移动速度
mRotate = 0.13;
mMove = ;
}
bool frameStarted(const FrameEvent& evt)
{
mMouse->capture();//俘获鼠标
mKeyboard->capture();//俘获键盘 if (mKeyboard->isKeyDown(OIS::KC_ESCAPE))//按下esc键
{
return false;
} bool currMouse = mMouse->getMouseState().buttonDown(OIS::MB_Left);//获取鼠标当前的状态
if(currMouse && !mMouseDown)
{
Light *light = mSceneMgr->getLight("light1");//获取灯光指针
light->setVisible(!light->isVisible());//根据上一次的状态改变
} mMouseDown = currMouse;//更新鼠标状态 mToggle -= evt.timeSinceLastFrame;//减去从上一帧到现在所经历的时间,保证正在渲染时不切换摄像机
if ((mToggle <0.0f) && mKeyboard->isKeyDown(OIS::KC_1))//按下数字键1时选择摄像机1
{
mToggle = 0.5f;
mCamera->getParentSceneNode()->detachObject(mCamera);
mCamNode = mSceneMgr->getSceneNode("CamNode1");
mCamNode->attachObject(mCamera);
}
else if ((mToggle <0.0f) && mKeyboard->isKeyDown(OIS::KC_2))//按下数字键1时选择摄像机1
{
mToggle = 0.5f;
mCamera->getParentSceneNode()->detachObject(mCamera);
mCamNode = mSceneMgr->getSceneNode("CamNode2");
mCamNode->attachObject(mCamera);
} Vector3 transVector = Vector3::ZERO;//保存平移方位
if (mKeyboard->isKeyDown(OIS::KC_UP) || mKeyboard->isKeyDown(OIS::KC_W))//前
{
transVector.z -= mMove;//z轴负方向移动
}
if (mKeyboard->isKeyDown(OIS::KC_DOWN) || mKeyboard->isKeyDown(OIS::KC_S))//后
{
transVector.z += mMove;//z轴正方向移动
}
if (mKeyboard->isKeyDown(OIS::KC_LEFT) || mKeyboard->isKeyDown(OIS::KC_A))//左
{
transVector.x -= mMove;//x轴负方向移动
}
if (mKeyboard->isKeyDown(OIS::KC_RIGHT) || mKeyboard->isKeyDown(OIS::KC_D))//右
{
transVector.x += mMove;//x轴正方向移动
}
if (mKeyboard->isKeyDown(OIS::KC_PGUP) || mKeyboard->isKeyDown(OIS::KC_Q))//上
{
transVector.y -= mMove;//Y轴负方向移动
}
if (mKeyboard->isKeyDown(OIS::KC_PGDOWN) || mKeyboard->isKeyDown(OIS::KC_E))//下
{
transVector.y += mMove;//Y轴正方向移动
} //根据从上一帧开始鼠标移动的距离来控制摄像机的俯仰偏斜。
if (mMouse->getMouseState().buttonDown(OIS::MB_Right))
{
mCamNode->yaw(Degree(-mRotate * mMouse->getMouseState().X.rel),Node::TS_WORLD);
mCamNode->pitch(Degree(-mRotate * mMouse->getMouseState().Y.rel),Node::TS_LOCAL);
}
return true;
//return ExampleFrameListener::frameStarted(evt);
}
bool frameEnded(const FrameEvent& evt)
{
return ExampleFrameListener::frameEnded(evt);
}
protected:
bool mMouseDown;// 鼠标左键是否在上一帧被按下
Real mToggle;//直到下一次触发的时间间隔
Real mRotate;//滚动常量
Real mMove;//移动常量
SceneManager *mSceneMgr;//当前的场景管理器
SceneNode *mCamNode;//当前的摄像机所绑定的场景节点
private:
}; class ExampleTestApplication:public ExampleApplication
{
public:
ExampleTestApplication()
{ }
~ExampleTestApplication()
{ } protected:
void createCamera()
{
//其他参数选择默认
mCamera = mSceneMgr->createCamera("PlayerCam");
mCamera->setNearClipDistance();
}
void createScene()
{
//载入实体
mSceneMgr->setAmbientLight(ColourValue(0.25,0.25,0.25));//设置环境色
Entity *ent1 = mSceneMgr->createEntity("ninja","ninja.mesh");
SceneNode *node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("ninjaNode");
node1->attachObject(ent1); //点光源
Light *light = mSceneMgr->createLight("light1");
light->setType(Light::LT_POINT);//点光源
light->setPosition(Vector3(,,));
light->setDiffuseColour(ColourValue::White);//漫射光
light->setSpecularColour(ColourValue::White);//反射光 //第一个摄像机节点
node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1",Vector3(-,,));
node1->yaw(Degree(-));//绕y轴顺时针旋转45度
node1->attachObject(mCamera);
//第二个摄像机节点
node1 = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2",Vector3(,,));
}
void createFrameListener()
{
mFrameListener = new ExampleTestFrameListener(mWindow,mCamera,mSceneMgr);//创建一个帧监听器
mRoot->addFrameListener(mFrameListener);//注册到Root类中
mFrameListener->showDebugOverlay(true);//在层上显示帧状态
}
}; #include "windows.h" INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT ) {
ExampleTestApplication app;
app.go();
return ;
}

全部代码

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

最新文章

  1. android 时间 格式
  2. 简明 Git 命令速查表
  3. FZU 2028 BFS+vector
  4. Windows 和  Linux 下 禁止ping的方法
  5. 官网下载Spring dist
  6. 获取当前页面的url
  7. hdu1087 简单DP
  8. Android 应用程序签名
  9. Quick Cocos2dx Http通讯
  10. 【转】程序员必须知道的几个Git代码托管平台
  11. 解析java泛型(一)
  12. PPLB条码打印
  13. ISP PIPLINE (一) BLC 以及 线性化
  14. 【netcore基础】ConcurrentDictionary 使用字符串作为key给代码加锁且使用EF事物防止并发调用数据混乱的问题
  15. css 实现多行文本末尾显示省略号
  16. python 协程库gevent学习--gevent数据结构及实战(三)
  17. CRM BP SEARCH 优化
  18. sqlite c#
  19. @ModelAttribute注解详解
  20. android全屏

热门文章

  1. iOS中转义后的html标签如何还原
  2. Linux上通过MySQL命令访问MySQL数据库时常见问题汇总
  3. 数据库之游标过程-- 基于MySQL
  4. UVA 10037 Bridge (基础DP)
  5. Java变量、Java对象初始化顺序
  6. MySQL 5.7.20绿色版安装详细图文教程
  7. iOS应用架构谈part3 网络层设计方案
  8. MySQL 使用GTID进行复制
  9. 将远程分支拷贝到本地,并更新代码push到原分支
  10. 关于上传文件 非ajax提交 得到后台数据问题