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

  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. SQL必知必会-笔记

    一.数据库/数据表 数据库(DATABASE):存储有组织的数据的容器; 数据库管理系统(DBMS):数据库软件.开发者通过 DBMS 操纵 DATABASE 表(TABLE):表是一种结构化的文件, ...

  2. 【Android】ContentProvider

    转载地址:http://www.cnblogs.com/lqminn/archive/2012/10/16/2725624.html 一.ContentProvider的概念 ContentProvi ...

  3. SlickEdit 18.0 版本发布 同时更新破解文件

    18.0版本没有太大的惊喜 多了如下功能 Multiple Document Group Interface Repository Log Browser History Diff Support f ...

  4. Centos离线安装Docker并加入到Swarm管理节点

    以root用户登录 加入Swarm前需要在Swarm上生成Token,所以需要提前将Swarm集群搭建完成后,再运行以下命令将各虚机加入到swarm节点 下载docker离线安装包,并拷贝到/root ...

  5. 电话号码 马赛克*号 string类扩展

    /// <summary> /// 字符串马赛克 /// </summary> /// <param name="source"></pa ...

  6. jacvaSe-LinkedList

    package com.java.chap08.sec02; import java.util.LinkedList; public class TestLinkedList { private st ...

  7. 随记:UWP开发中怎么使当前页面拓展到标题栏

    public sealed partial class MainPage : Page { public MainPage() { this.InitializeComponent(); CoreAp ...

  8. This implementation is not part of the Windows Platform FIPS validated cryptographic algorithms while caching

    今天运行自己的网站时报了这样一个错误,很是纳闷,这个网站运行了这么久,怎么报这个错呢,原来是做缓存的时候用到了基于windows平台的加密算法.解决方法如下: 删除注册表下的这个节点即可.删除HKEY ...

  9. Cause: java.lang.UnsupportedOperationException

    运行web项目的时候出现以下错误: ### Cause: java.lang.UnsupportedOperationException    at org.mybatis.spring.MyBati ...

  10. Ubuntu下安装XAMPP

    来源:http://www.ido321.com/1265.html 最近,我也玩起了Linux了,瞬间觉得自己逼格又上去了,所以,就给笔记本安装了Ubuntu+Win7双系统.当然在Ubuntu下必 ...