[转载请保证内容的完整性和正确性]
建立一个简单的框架——响应键盘事件
上一节我们在场景中添加了一个机器人,这一节我们将建立一个FrameListener类(顾名思义,这是一个侦听类),来控制模型进行移动。
创建FrameListener类
FrameListener类主要负责事件的侦听,如帧结束、键盘输入等事件。它继承自ExampleFrameListener类,如果需要的话还可以继承KeyListener(键盘事件)、MouseListener(鼠标事件)和JoyStickListener(游戏杆事件)。这里只有键盘事件。
1 //DemoFrameListener.h 2 class DemoFrameListener : public ExampleFrameListener, public OIS::KeyListener { 3 public: 4 DemoFrameListener(RenderWindow* _win, Camera* _cam, bool bufferKeys, 5 bool bufferMouse, bool bufferJoy); 6 bool keyPressed(const OIS::KeyEvent& arg); 7 bool keyReleased(const OIS::KeyEvent& arg); 8 bool frameEnded(const FrameEvent& evt); 9 };
1 //DemoFrameListener.cpp 2 DemoFrameListener::DemoFrameListener(RenderWindow* _win, Camera* _cam, bool bufferKeys, 3 bool bufferMouse, bool bufferJoy) 4 : ExampleFrameListener(_win, _cam, bufferKeys, bufferMouse, bufferJoy) { 5 6 } 7 8 bool DemoFrameListener::keyPressed(const OIS::KeyEvent& arg) { 9 return true; 10 } 11 12 bool DemoFrameListener::keyReleased(const OIS::KeyEvent& arg) { 13 return true; 14 } 15 16 bool DemoFrameListener::frameEnded(const FrameEvent& evt) { 17 return true; 18 }
在Application类中添加新的方法。
1 //DemoApplication.cpp 2 void DemoApplication::createFrameListener(void) { 3 mFrameListener = new DemoFrameListener(mWindow, mCamera, true, true, true); 4 mRoot->addFrameListener(mFrameListener); 5 }
这时运行代码,还是能看到和上一节一样的机器人,但是鼠标、键盘都不能移动摄像机了。接下来我们将添加自己的控制方法。
创建Manager类
在添加控制方法之前,我们要先优化一下代码结构。上一节中我们把添加模型的代码写到了Application类中,这次我们要创建一个Manager类,来统一管理这些模型资源。
1 //DemoManager.h 2 class DemoManager 3 { 4 public: 5 DemoManager(Ogre::SceneManager* _sm); 6 ~DemoManager(void); 7 void Update(); 8 void SetDeltaT(float _dt); 9 private: 10 Ogre::SceneManager* sm; 11 float deltaT; 12 Ogre::SceneNode* rootsn; 13 Ogre::SceneNode* node; 14 };
添加构造函数中的代码
1 //DemoManager.cpp 2 DemoManager::DemoManager(Ogre::SceneManager* _sm) : sm(_sm) { 3 Ogre::Entity* ent = sm->createEntity("Robot", "robot.mesh"); 4 rootsn = sm->getRootSceneNode(); 5 node = rootsn->createChildSceneNode("RobotNode"); 6 node->attachObject(ent); 7 }
其他函数中也略作修改。
1 //DemoApplication.h 2 //添加变量 3 private: 4 DemoManager* demoManager;
1 //DemoApplication.cpp 2 //修改两个函数 3 void DemoApplication::createScene(void) { 4 mSceneMgr->setAmbientLight(ColourValue(0.5, 0.5, 0.5)); 5 Light* l = mSceneMgr->createLight("MainLight"); 6 l->setPosition(20, 80, 50); 7 8 demoManager = new DemoManager(mSceneMgr); 9 } 10 11 void DemoApplication::createFrameListener(void) { 12 mFrameListener = new DemoFrameListener(mWindow, mCamera, true, true, true, demoManager); 13 mRoot->addFrameListener(mFrameListener); 14 }
1 //DemoFrameListener.h 2 //修改构造函数声明 3 DemoFrameListener(RenderWindow* _win, Camera* _cam, bool bufferKeys, 4 bool bufferMouse, bool bufferJoy, DemoManager* _dm);
1 //DemoFrameListener.cpp 2 //修改构造函数 3 DemoFrameListener::DemoFrameListener(RenderWindow* _win, Camera* _cam, bool bufferKeys, 4 bool bufferMouse, bool bufferJoy, DemoManager* _dm) 5 : ExampleFrameListener(_win, _cam, bufferKeys, bufferMouse, bufferJoy), demoManager(_dm) { 6 7 }
这是运行代码可以看到和之前一样的界面,但是我们的代码结构已经得到了优化,分为了三部分:Application(管理结构)、FrameListener(处理事件)和Manager(管理资源)。
添加键盘事件
我们将添加6个按键,WASD进行平移,QE进行水平旋转。
首先,为了能够让移动不受帧率的影响,我们让移动随时间变化。我们要传给Manager类每一帧间隔的时间。
1 //DemoManager.cpp 2 void DemoManager::SetDeltaT(float _dt) { 3 deltaT = _dt; 4 }
然后定义键盘事件:
1 //DemoFrameListener.cpp 2 bool DemoFrameListener::keyPressed(const OIS::KeyEvent& arg) { 3 Ogre::Vector3 movement(0, 0, 0); 4 float yaw(0); 5 6 switch (arg.key) { 7 case OIS::KC_W: 8 movement.z -= 1000; 9 break; 10 case OIS::KC_S: 11 movement.z += 1000; 12 break; 13 case OIS::KC_A: 14 movement.x -= 1000; 15 break; 16 case OIS::KC_D: 17 movement.x += 1000; 18 break; 19 case OIS::KC_Q: 20 yaw += 5; 21 break; 22 case OIS::KC_E: 23 yaw -= 5; 24 break; 25 } 26 27 demoManager->SetMovement(movement); 28 demoManager->SetRotation(yaw); 29 return true; 30 }
为了方便,我们再定义一个退出程序的按键。
1 //DemoFrameListener.cpp 2 bool DemoFrameListener::keyReleased(const OIS::KeyEvent& arg) { 3 if (arg.key == OIS::KC_ESCAPE) { 4 _exit = true; 5 } 6 return true; 7 } 8 9 bool DemoFrameListener::frameEnded(const FrameEvent& evt) { 10 ExampleFrameListener::frameEnded(evt); 11 if (demoManager) { 12 demoManager->SetDeltaT(evt.timeSinceLastFrame); 13 demoManager->Update(); 14 } 15 16 return (!_exit); 17 }
然后在Manager中接收按键事件:
1 //DemoManager.cpp 2 void DemoManager::Update() { 3 node->translate(movement * deltaT); 4 node->yaw(Ogre::Radian(yaw * deltaT)); 5 6 movement = Ogre::Vector3(0, 0, 0); 7 yaw = 0; 8 } 9 10 void DemoManager::SetMovement(Ogre::Vector3 _movement) { 11 movement = Ogre::Vector3(_movement); 12 } 13 14 void DemoManager::SetRotation(float _yaw) { 15 yaw = _yaw; 16 }
这里可以看出,我们对3D模型做的变换其实就是对场景节点做的变换。
运行这段代码,我们虽然可以控制模型机器人,但是需要一下一下地按键。要改善这个问题就要修改FrameListener类,其中有keyPressed和keyReleased两个方法,分别是按下键盘和释放键盘,略作修改就能实现长按事件。