zoukankan      html  css  js  c++  java
  • [OGRE]基础教程来七发:来谈一谈缓冲绑定

    上一章我们处理监听的方案是,每一帧只处理一次。

    这一次,当鼠标键盘的事件发生时,我们会立即处理它。

    这里只是对缓冲输入的一个简单介绍,而不是完整的如何使用OIS的教程。

    若想了解更多内容,请查阅相关的OIS使用教程。


    初始的项目源码如下:

     

    #include "ExampleApplication.h"
    
     
    
    class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
    
    {
    
    public:
    
        TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr)
    
            : ExampleFrameListener(win, cam, true, true)
    
        {
    
        }
    
     
    
        bool frameStarted(const FrameEvent &evt)
    
        { 
    
            if(mMouse)
    
                mMouse->capture();
    
            if(mKeyboard) 
    
                mKeyboard->capture();
    
            return mContinue;
    
        }
    
     
    
        // MouseListener
    
        bool mouseMoved(const OIS::MouseEvent &e) { return true; }
    
        bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
    
        bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
    
     
    
        // KeyListener
    
        bool keyPressed(const OIS::KeyEvent &e) { return true; }
    
        bool keyReleased(const OIS::KeyEvent &e) { return true; }
    
    protected:
    
        Real mRotate;          // The rotate constant
    
        Real mMove;            // The movement constant
    
       
    
        SceneManager *mSceneMgr;   // The current SceneManager
    
        SceneNode *mCamNode;   // The SceneNode the camera is currently attached to
    
     
    
        bool mContinue;        // Whether to continue rendering or not
    
        Vector3 mDirection;     // Value to move in the correct direction
    
    };
    
         
    
    class TutorialApplication : public ExampleApplication
    
    {
    
    public:
    
        void createCamera(void)
    
        { 
    
            // create camera, but leave at default position
    
            mCamera = mSceneMgr->createCamera("PlayerCam"); 
    
            mCamera->setNearClipDistance(5);
    
        }
    
     
    
        void createScene(void)
    
        {
    
            mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));
    
     
    
            // add the ninja
    
            Entity *ent = mSceneMgr->createEntity("Ninja", "ninja.mesh");
    
            SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode");
    
            node->attachObject(ent);  
    
     
    
            // create the light
    
            Light *light = mSceneMgr->createLight("Light1");
    
            light->setType(Light::LT_POINT);
    
            light->setPosition(Vector3(250, 150, 250));
    
            light->setDiffuseColour(ColourValue::White);
    
            light->setSpecularColour(ColourValue::White); 
    
     
    
            // Create the scene node
    
            node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1", Vector3(-400, 200, 400));
    
     
    
            // Make it look towards the ninja
    
            node->yaw(Degree(-45));
    
     
    
            // Create the pitch node
    
            node = node->createChildSceneNode("PitchNode1");
    
            node->attachObject(mCamera); 
    
     
    
            // create the second camera node/pitch node
    
            node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2", Vector3(0, 200, 400));
    
            node = node->createChildSceneNode("PitchNode2");
    
        }
    
     
    
        void createFrameListener(void)
    
        {
    
            // Create the FrameListener
    
            mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);
    
            mRoot->addFrameListener(mFrameListener); 
    
     
    
            // Show the frame stats overlay
    
            mFrameListener->showDebugOverlay(true);
    
        }
    
    };
    
     
    
    #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    
    #define WIN32_LEAN_AND_MEAN
    
    #include "windows.h"
    
     
    
    INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
    
    #else
    
    int main(int argc, char **argv)
    
    #endif
    
    {
    
        // Create application object
    
        TutorialApplication app;
    
     
    
        try {
    
            app.go();
    
        } catch(Exception& e) {
    
    #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    
            MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
    
    #else
    
            fprintf(stderr, "An exception has occurred: %s
    ",
    
                e.getFullDescription().c_str());
    
    #endif
    
        }
    
     
    
        return 0;
    
    }
    
    

    上一次我们使用的是无缓冲的输入,也就是说,在每一帧里我们查询OIS::KeyboardOIS::Mouse实例的状态,以判断它们是否被按下。

    而缓冲输入使用了一个listener接口,以便在事件发生时通知你的程序。

    比如,当一个键被按下时,会触发一个 KeyListener::keyPressed 事件,

    而当这个键被释放(不再按下)时,KeyListener::keyReleased 事件被触发给所有已注册的KeyListener类。

    这些能用在追踪按键的时间,或判断按键在上一帧中是否没有被按下。


     

    关于OIS的监听系统有一点要注意的是,对于每一个KeyboardMouseJoystick对象只能有一个监听器,这样是为了简单(也为了速度)。

    多次调用setEventCallback函数的结果是只有最后一次注册的监听器才得到事件消息。

    如果你有多个对象需要获得KeyMouse事件,你只有自己写一个消息分发。


    还有,千万记得在frameStarted方法里调用Keyboard::captureMouse::capture

    OIS不会使用线程来确定键盘鼠标的状态,所以你必须指明什么时候去获取输入。


    OISKeyListener接口提供了两个纯虚函数。

    第一个是keyPressed函数,每次按下某个键时调用它,

    还一个是keyReleased,每次离开某个键时调用它,

    传入这些函数的参数是一个KeyEvent,它包含被按下/释放的按键的键码。


     

    鼠标监听界面MouseListener接口比KeyListener接口要稍微复杂一些。

    它包含查看何时鼠标键被按下/释放的函数: MouseListener::mousePressed MouseListener::mouseReleased。

    它还包含一个mouseMoved函数,当鼠标移动时调用它。

    这些函数都接收一个MouseEvent对象,在state变量里保存着当前鼠标的状态。

     

    需要注意的是,MouseState对象即包含了鼠标移动的相对XY坐标(即,从上一次调用MouseListener::mouseMoved开始,它所移动的距离),

    还包含了绝对XY坐标(即,屏幕上的准确位置)。

     

    在我们开始修改TutorialFrameListener之前,请注意先对TutorialFrameListener类做两处大的改变:

     

    class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener



    我们继承了OISMouseListenerKeyListener类,这样我们才能从它们那里接收事件。


     

    同样,调用ExampleFrameListener构造器也有变化:

     TutorialFrameListener( RenderWindow* win, Camera* cam, SceneManager *sceneMgr ):ExampleFrameListener(win, cam, true, true)


    将后面两个参数改为true 指明了我们将要使用带缓冲的键盘鼠标输入。

    再对类中的变量稍作修改:

     

    protected:
    	Real mRotate;          // 旋转常量
    	Real mMove;            // 运动常量
    	SceneManager *mSceneMgr;   // 当前的场景管理器
    	SceneNode *mCamNode;   // 当前摄像机附着的场景节点
    	bool mContinue;        // 是否要继续渲染
    	Vector3 mDirection;     // 指向正确的移动方向


    mContinue变量是frameStarted方法的返回值。

    mContinuefalse的时候,程序退出。

    mDirection变量指定了在每一个帧里我们如何移动摄像机节点。

    在构造器里,我们像在上次那样初始化一些变量,并把mContinue设成true。添加如下代码到TutorialFrameListener的构造器里:

     

    	TutorialFrameListener( RenderWindow* win, Camera* cam, SceneManager *sceneMgr )
    		:ExampleFrameListener(win, cam, true, true)
    	{
    		// Populate the camera and scene manager containers
    		mCamNode = cam->getParentSceneNode();
    		mSceneMgr = sceneMgr;
    		// 设置旋转和移动速度
    		mRotate = 0.13;
    		mMove = 250;
    		// 继续渲染
    		mContinue = true;
    	}

    ExampleFrameListener的构造器里已经取得了OISmMousemKeyboard对象。

    我们调用这些输入对象的setEventCallback方法,把TutorialFrameListener注册成一个监听器。

    		//注册监听器
    		mMouse->setEventCallback(this);
    		mKeyboard->setEventCallback(this);


     

    最后,我们还要把mDirection初始化成零向量(因为我们最开始不需要它动):

    	TutorialFrameListener( RenderWindow* win, Camera* cam, SceneManager *sceneMgr )
    		:ExampleFrameListener(win, cam, true, true)
    	{
    		// Populate the camera and scene manager containers
    		mCamNode = cam->getParentSceneNode();
    		mSceneMgr = sceneMgr;
    		// 设置旋转和移动速度
    		mRotate = 0.13;
    		mMove = 250;
    		// 继续渲染
    		mContinue = true;
    		//注册监听器
    		mMouse->setEventCallback(this);
    		mKeyboard->setEventCallback(this);
    		//零向量
    		mDirection = Vector3::ZERO;
    	}


    在我们深入之前,我们应该设置 Escape 键用来退出程序。

    找到TutorialFrameListener::keyPressed方法,每当键盘上一个键被按下时,都会调用这个方法并传入一个KeyEvent对象。

    我们能够通过这个对象的key变量来获取按键的键码(KC_*)。基于这个值,我们构造一个switch,为绑定所有程序里用到的按钮。


     

    我们需要在switch语句里为其它按钮做绑定。首先我们要让用户按12键进行视口的切换。

    代码基本上与上次相同:

     

          case OIS::KC_1:
    
              mCamera->getParentSceneNode()->detachObject(mCamera);
    
              mCamNode = mSceneMgr->getSceneNode("CamNode1");
    
              mCamNode->attachObject(mCamera);
    
              break;
    
     
    
          case OIS::KC_2:
    
              mCamera->getParentSceneNode()->detachObject(mCamera);
    
              mCamNode = mSceneMgr->getSceneNode("CamNode2");
    
              mCamNode->attachObject(mCamera);
    
              break;
    

    接下来我们要添加键盘移动。每次用户按下移动按键,我们都要朝正确的方向加上或者减去mMove

          case OIS::KC_UP:
    
          case OIS::KC_W:
    
              mDirection.z -= mMove;
    
              break;
    
     
    
          case OIS::KC_DOWN:
    
          case OIS::KC_S:
    
              mDirection.z += mMove;
    
              break;
    
     
    
          case OIS::KC_LEFT:
    
          case OIS::KC_A:
    
              mDirection.x -= mMove;
    
              break;
    
     
    
          case OIS::KC_RIGHT:
    
          case OIS::KC_D:
    
              mDirection.x += mMove;
    
              break;
    
     
    
          case OIS::KC_PGDOWN:
    
          case OIS::KC_E:
    
              mDirection.y -= mMove;
    
              break;
    
     
    
          case OIS::KC_PGUP:
    
          case OIS::KC_Q:
    
              mDirection.y += mMove;
    
              break;
    
    

    当按键被释放时,我们要立即取消mDirection向量上的移动。找到keyReleased方法,添加如下代码:

          switch (e.key)
    
          {
    
          case OIS::KC_UP:
    
          case OIS::KC_W:
    
              mDirection.z += mMove;
    
              break;
    
     
    
          case OIS::KC_DOWN:
    
          case OIS::KC_S:
    
              mDirection.z -= mMove;
    
              break;
    
     
    
          case OIS::KC_LEFT:
    
          case OIS::KC_A:
    
              mDirection.x += mMove;
    
              break;
    
     
    
          case OIS::KC_RIGHT:
    
          case OIS::KC_D:
    
              mDirection.x -= mMove;
    
              break;
    
     
    
          case OIS::KC_PGDOWN:
    
          case OIS::KC_E:
    
              mDirection.y += mMove;
    
              break;
    
     
    
          case OIS::KC_PGUP:
    
          case OIS::KC_Q:
    
              mDirection.y -= mMove;
    
              break;
    
          } // switch
    
          return true;
    
    

    好了,我们能根据按键输入对mDirection进行更新了。下面的代码与上次是一样的,添加到frameStarted函数里:

           mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);
    
    


    接下来轮到鼠标了。我们从点击鼠标左键来控制灯的开关开始。

    找到mousePressed函数并看看它的参数。用OIS,我们可以访问MouseEventMouseButtonID

    我们用MouseButtonID作为switch条件,来确定按下的是哪个按钮。用下面的代码替换掉mousePressed函数里的:

     

    Light *light = mSceneMgr->getLight("Light1");
    
          switch (id)
    
          {
    
          case OIS::MB_Left:
    
              light->setVisible(! light->isVisible());
    
              break;
    
          }
    
          return true;
    
    


    剩下来的事情就是绑定鼠标右键来进入鼠标观察模式。

    每当鼠标移动时我们都检查右键是否按下。如果是,我们基于相对运动来转动摄像机。

    通过传入函数的MouseEvent对象,我们能获取相对运动。它包含一个switch变量,里面有鼠标的状态(是关于鼠标的详细信息)。

    MouseState::buttonDown告诉我们是否一个特定的按钮被按下,而“X”“Y”变量告诉我们鼠标的相对运动。找到mouseMoved方法,用以下代码替换掉原来的:

     

          if (e.state.buttonDown(OIS::MB_Right))
    
          {
    
              mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD);
    
              mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL);
    
          }
    
          return true;
    


    至此,完整的代码如下:

     

    #include "ExampleApplication.h"
    
    class TutorialFrameListener : public ExampleFrameListener, public OIS::MouseListener, public OIS::KeyListener
    {
    public:
        TutorialFrameListener(RenderWindow* win, Camera* cam, SceneManager *sceneMgr)
            : ExampleFrameListener(win, cam, true, true)
        {
            // Populate the camera and scene manager containers
            mCamNode = cam->getParentSceneNode();
            mSceneMgr = sceneMgr;
    
            // set the rotation and move speed
            mRotate = 0.13;
            mMove = 250;
    
            // continue rendering
            mContinue = true;
    
            mMouse->setEventCallback(this);
            mKeyboard->setEventCallback(this);
    
            mDirection = Vector3::ZERO;
        }
    
        bool frameStarted(const FrameEvent &evt)
        {
            if(mMouse)
                mMouse->capture();
            if(mKeyboard) 
                mKeyboard->capture();
    
            mCamNode->translate(mDirection * evt.timeSinceLastFrame, Node::TS_LOCAL);
            return mContinue;
        }
    
        // MouseListener
        bool mouseMoved(const OIS::MouseEvent &e)
        {
            if (e.state.buttonDown(OIS::MB_Right))
            {
                mCamNode->yaw(Degree(-mRotate * e.state.X.rel), Node::TS_WORLD);
                mCamNode->pitch(Degree(-mRotate * e.state.Y.rel), Node::TS_LOCAL);
            }
            return true;
        }
    
        bool mousePressed(const OIS::MouseEvent &e, OIS::MouseButtonID id)
        {
            Light *light = mSceneMgr->getLight("Light1");
            switch (id)
            {
            case OIS::MB_Left:
                light->setVisible(! light->isVisible());
                break;
            }
            return true;
        }
    
        bool mouseReleased(const OIS::MouseEvent &e, OIS::MouseButtonID id) { return true; }
    
        // KeyListener
        bool keyPressed(const OIS::KeyEvent &e)
        {
            switch (e.key)
            {
            case OIS::KC_ESCAPE: 
                mContinue = false;
                break;
    
            case OIS::KC_1:
                mCamera->getParentSceneNode()->detachObject(mCamera);
                mCamNode = mSceneMgr->getSceneNode("CamNode1");
                mCamNode->attachObject(mCamera);
                break;
    
            case OIS::KC_2:
                mCamera->getParentSceneNode()->detachObject(mCamera);
                mCamNode = mSceneMgr->getSceneNode("CamNode2");
                mCamNode->attachObject(mCamera);
                break;
    
            case OIS::KC_UP:
            case OIS::KC_W:
                mDirection.z -= mMove;
                break;
    
            case OIS::KC_DOWN:
            case OIS::KC_S:
                mDirection.z += mMove;
                break;
    
            case OIS::KC_LEFT:
            case OIS::KC_A:
                mDirection.x -= mMove;
                break;
    
            case OIS::KC_RIGHT:
            case OIS::KC_D:
                mDirection.x += mMove;
                break;
    
            case OIS::KC_PGDOWN:
            case OIS::KC_E:
                mDirection.y -= mMove;
                break;
    
            case OIS::KC_PGUP:
            case OIS::KC_Q:
                mDirection.y += mMove;
                break;
            }
            return true;
        }
    
        bool keyReleased(const OIS::KeyEvent &e)
        {
            switch (e.key)
            {
            case OIS::KC_UP:
            case OIS::KC_W:
                mDirection.z += mMove;
                break;
    
            case OIS::KC_DOWN:
            case OIS::KC_S:
                mDirection.z -= mMove;
                break;
    
            case OIS::KC_LEFT:
            case OIS::KC_A:
                mDirection.x += mMove;
                break;
    
            case OIS::KC_RIGHT:
            case OIS::KC_D:
                mDirection.x -= mMove;
                break;
    
            case OIS::KC_PGDOWN:
            case OIS::KC_E:
                mDirection.y += mMove;
                break;
    
            case OIS::KC_PGUP:
            case OIS::KC_Q:
                mDirection.y -= mMove;
                break;
            } // switch
            return true;
        }
    protected:
        Real mRotate;          // The rotate constant
        Real mMove;            // The movement constant
    
        SceneManager *mSceneMgr;   // The current SceneManager
        SceneNode *mCamNode;   // The SceneNode the camera is currently attached to
    
        bool mContinue;        // Whether to continue rendering or not
        Vector3 mDirection;     // Value to move in the correct direction
    };
    
    class TutorialApplication : public ExampleApplication
    {
    public:
        void createCamera(void)
        {
            // create camera, but leave at default position
            mCamera = mSceneMgr->createCamera("PlayerCam"); 
            mCamera->setNearClipDistance(5);
        }
    
        void createScene(void)
        {
            mSceneMgr->setAmbientLight(ColourValue(0.25, 0.25, 0.25));
    
            // add the ninja
            Entity *ent = mSceneMgr->createEntity("Ninja", "ninja.mesh");
            SceneNode *node = mSceneMgr->getRootSceneNode()->createChildSceneNode("NinjaNode");
            node->attachObject(ent);
    
            // create the light
            Light *light = mSceneMgr->createLight("Light1");
            light->setType(Light::LT_POINT);
            light->setPosition(Vector3(250, 150, 250));
            light->setDiffuseColour(ColourValue::White);
            light->setSpecularColour(ColourValue::White);
    
            // Create the scene node
            node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode1", Vector3(-400, 200, 400));
            node->yaw(Degree(-45));
            node->attachObject(mCamera);
    
            // create the second camera node/pitch node
            node = mSceneMgr->getRootSceneNode()->createChildSceneNode("CamNode2", Vector3(0, 200, 400));
        }
    
        void createFrameListener(void)
        {
            // Create the FrameListener
            mFrameListener = new TutorialFrameListener(mWindow, mCamera, mSceneMgr);
            mRoot->addFrameListener(mFrameListener);
    
            // Show the frame stats overlay
            mFrameListener->showDebugOverlay(true);
        }
    };
    
    #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
    #define WIN32_LEAN_AND_MEAN
    #include "windows.h"
    
    INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT)
    #else
    int main(int argc, char **argv)
    #endif
    {
        // Create application object
        TutorialApplication app;
    
        try {
            app.go();
        } catch(Exception& e) {
    #if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32
            MessageBox(NULL, e.getFullDescription().c_str(), "An exception has occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);
    #else
            fprintf(stderr, "An exception has occurred: %s
    ",
                e.getFullDescription().c_str());
    #endif
        }
    
        return 0;
    }
    


    运行效果如图:






  • 相关阅读:
    jQuery 语法
    jQuery 简介
    把数据存储到 XML 文件
    XML 注意事项
    XML DOM (Document Object Model) 定义了访问和操作 XML 文档的标准方法。
    通过 PHP 生成 XML
    XML 命名空间(XML Namespaces)
    XML to HTML
    XMLHttpRequest 对象
    使用 XSLT 显示 XML
  • 原文地址:https://www.cnblogs.com/pangblog/p/3364647.html
Copyright © 2011-2022 走看看