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

  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. I/O操做总结(二)

    文件的操作 这一节我们来讨论关于文件自身的操作 不浪费唾沫了,用代码说话…… 实例1:创建文件对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...

  2. git从无到有建立一个仓库并上传文件

    第一步,创建仓库 登录自己的码云  第二步,本地操作 1.到你所要上传的文件夹中右键 选择git bash here 2.初始化项目 git init 3.连接远程仓库 刚才我们建立的时候的远程地址就 ...

  3. Flash图表控件FusionCharts如何高亮显示数据

    使用Flash图表控件FusionCharts时,通过改变alpha值是高亮显示数据的最简单方式. XML代码如下: <chart> <set label='John' value= ...

  4. windows网络和共享中心“查看基本网络信息并设置连接”为“未知”的解决方案

    存在问题“查看基本网络信息并设置连接”为“未知”.如图所示: 解决步骤 运行services.msc 启动Network List Service 若无法启动,打开其属性,选择“登录”选项卡,将启动类 ...

  5. Unity中实现全局管理类的几种方式

    (搬运自我在SegmentFault的博客) 如何在Unity中实现全局管理类?由于Unity脚本的运行机制和面向组件编程(COP)的思想,实现起来和普通的方式略有差别. 第一种方式是使用静态类.适合 ...

  6. UVA 10382 Watering Grass (区间覆盖,贪心)

    问题可以转化为草坪的边界被完全覆盖.这样一个圆形就换成一条线段. 贪心,从中选尽量少的线段把区间覆盖,按照把线段按左端点排序,记录一个当前已经覆盖区间的位置cur, 从左端点小于等于cur选一个右端点 ...

  7. groupadd - 建 立 新 群 组

    总览 SYNOPSIS groupadd [-g gid [-o]] [-r] [-f] group 描述 DESCRIPTION groupadd 可 指 定 群 组 名 称 来 建 立 新 的 群 ...

  8. appium---命令行启动appium

    在客户端的appium长时间运行的时候,出产生一些数据.日志有可能会对appium的内存有所增长,严重的会使appium产生崩溃,这个时候就推荐使用通过cmd进行运行appium, 安装前提需要安装N ...

  9. Django ORM models操作

    title: Django ORM models操作 tags: Django --- Django ORM models操作 Django ORM基本操作 一.数据库的创建及增删改查 1 使用类创建 ...

  10. java基础—泛型

    一.体验泛型 JDK1.5之前的集合类中存在的问题——可以往集合中加入任意类型的对象,例如下面代码: 1 package cn.gacl.generic.summary; 2 3 import jav ...