ogre3D学习基础10 -- 键盘控制与鼠标控制(直接控制)
要实现键盘,鼠标对场景的控制,首先要帧监听,就是在每一帧的渲染前后对它进行操作。这里的操作没有用到缓冲区,只是简单的直接获取。
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格式的动画上来的,可一直没找到好工具。
最新文章
- android 时间 格式
- 简明 Git 命令速查表
- FZU 2028 BFS+vector
- Windows 和 Linux 下 禁止ping的方法
- 官网下载Spring dist
- 获取当前页面的url
- hdu1087 简单DP
- Android 应用程序签名
- Quick Cocos2dx Http通讯
- 【转】程序员必须知道的几个Git代码托管平台
- 解析java泛型(一)
- PPLB条码打印
- ISP PIPLINE (一) BLC 以及 线性化
- 【netcore基础】ConcurrentDictionary 使用字符串作为key给代码加锁且使用EF事物防止并发调用数据混乱的问题
- css 实现多行文本末尾显示省略号
- python 协程库gevent学习--gevent数据结构及实战(三)
- CRM BP SEARCH 优化
- sqlite c#
- @ModelAttribute注解详解
- android全屏