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

  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格式的动画上来的,可一直没找到好工具。

ogre3D学习基础10 -- 键盘控制与鼠标控制(直接控制)的更多相关文章

  1. ogre3D学习基础10 -- 键盘控制与鼠标控制(缓冲控制)

    带缓冲的鼠标.键盘操作,这里的鼠标.按键事件会被各自的监听器捕获.其中OIS中定义的两个类MouseListener,KeyListener负责对事件的处理.我们需要使用这两个类的接口. 当一个键被按 ...

  2. ogre3D学习基础13 -- 键盘控制网格动画mesh

    以上一节为蓝本,这里增加一点难度,添加了四个节点,增加键盘控制移动速度,使用bool变量控制是否移动. 第一,要增加键盘控制,那就使用OIS::KeyListener,在监听器里添加一个父类KeyLi ...

  3. ogre3D学习基础1 -- 核心对象与脚本技术

    一.核心对象介绍1.命名空间 Ogre3d使用了C++的特性--命名空间,可以防止命名混淆.使用方法也简单,using namespace Ogre;或者直接在使用时加上“Ogre::”的前缀,如Og ...

  4. ogre3D学习基础5 -- 阴影与动画

    五.阴影 阴影是渲染一个真实场景的重要组成部分,它可以给场景中的物体提供更加真实的感觉,同时还可以帮助用户更好的了解对象间的空间关系. 启用阴影: 缺省情况下,阴影是关闭的,开启方式如下: 1.建立场 ...

  5. ogre3D学习基础19 --- 材质的继承,纹理的滚动与旋转

    以上一节为基础,废话不多说. 首先新增一个节点,用于比较显示 //新增一个节点 ent = mSceneMgr->createEntity("Quad"); ent-> ...

  6. ogre3D学习基础18 -- 材质的使用与脚本的简单书写

    这一节以基础16为基础,练习材质的使用. 第一,看看框架 //material #include "ExampleApplication.h" class TutorialAppl ...

  7. ogre3D学习基础17 --- 如何手动创建ogre程序

    建立自己的Ogre程序 一直以来都是使用ExampleApplication.h来写程序,现在来看看它到底有什么神奇的地方. 首先,我们新建一个win32空项目 然后配置环境 最后新建define.c ...

  8. ogre3D学习基础3 -- 粒子与表层脚本

    9.粒子脚本 粒子脚本允许你实例化地在你的脚本代码中定义粒子系统,而不必在源代码中进行设置,使得你做任何修改都能得到快速回应.脚本里定义的粒子系统被用作模板,并且多个实际的系统可以在运行时从这里被创建 ...

  9. ogre3D学习基础11 -- 交换两个场景管理器

    这一节,练习一下前几次学习的内容,功能很简单,就是建立两个不同的场景管理器,当按下键盘上某个键时切换镜头. 基本框架不变,这个监听器继承了两个父类,一个是我们的老朋友ExampleFrameListe ...

随机推荐

  1. linux安装源文件(.tar.gz)

    安装此类文件,分为7步: 1.首先把依赖的软件给装上,如果依赖perl,先装perl,如果依赖Pathon,现装pathon 2.tar 源软件路径 -C 新软件路径(注意这里一定要-C,不然不能解压 ...

  2. springboot+shiro+cas实现单点登录之cas server搭建

    CAS是YALE大学发起的一个开源项目,旨在为web应用系统提供一种可靠的单点登录方法.它主要分为client和server端,server端负责对用户的认证工作,client端负责处理对客户端受保护 ...

  3. Azure 门户使用概览

    Azure 门户是管理 Azure 云平台的核心工具,用户可以在其中预配和管理 Azure 资源.本教程将帮助你熟悉Azure管理门户,包括一些关键功能的介绍,并演示了如何通过 Azure 门户创建虚 ...

  4. winform 配置文件增删改查

    winform 配置文件是  App.config webform   的配置文件 是web.config 其实基本操作都一样    设置个配置文件  全局文件 访问者个配置文件  对这个配置文件增删 ...

  5. Android(java)学习笔记136:利用谷歌API对数据库增删改查(推荐使用)

    接下来我们通过项目案例来介绍:这个利用谷歌API对数据库增删改查 1. 首先项目图: 2. 这里的布局文件activity_main.xml: <LinearLayout xmlns:andro ...

  6. 主成分分析法(PCA)答疑

    问:为什么要去均值? 1.我认为归一化的表述并不太准确,按统计的一般说法,叫标准化.数据的标准化过程是减去均值并除以标准差.而归一化仅包含除以标准差的意思或者类似做法.2.做标准化的原因是:减去均值等 ...

  7. Spring中使用事务搭建转账环境方法二 相对简便的注解方法 ——配置文件注入对象属性需要setter方法 注解方法,不需要生成setter方法

    XML配置文件代码如下: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=" ...

  8. iOS开发遇到的坑之六--使用cocopods管理第三方库时,编译出现Library not found for -lPods问题的解决办法

    在项目中有时候会遇到Library not found for -lPods(这里的IPods指的是你具体的第三方库)的问题 出现这个错误的原因是:xcode在编译的时候找不到这个库,从而导致项目无法 ...

  9. 使用mfc CHtmlView内存泄露解决方法

    第一步,谷歌有文章说CHtmlView部分api使用BSTR没释放: 解决方法是重写一下接口: CString GetFullName() const; CString GetFullName() c ...

  10. python特殊字符转义符号表示