转载:OGRE一起学(十五) 键盘控制

第十五章 键盘控制

这一节我们学习最基本的键盘控制。下面是截图和代码,你可以使用 [I] 、[K] 、[J] 、[L] 控制角色的前后左右移动。

OGRE 使用 InputReader 类来接收系统输入信息,包括键盘输入和鼠标输入。 Ogre::InputReader 类在头文件 OgreInput.h 中定义,对于键盘控制,我们最常用的一个方法是 isKeyDown : 
bool Ogre::InputReader:: isKeyDown ( KeyCode kc ) const [virtual] 
当 KeyCode kc 指定的键被按下时返回真。

其中, KeyCode 的枚举也在头文件 OgreInput.h 中定义:    

enum KeyCode     {         KC_ESCAPE          =0x01,         KC_1                   =0x02,         KC_2                   =0x03,         KC_3                   =0x04,         KC_4                   =0x05,         KC_5                   =0x06,         KC_6                   =0x07,         KC_7                   =0x08,         KC_8                   =0x09,         KC_9                   =0x0A,         KC_0                   =0x0B,         KC_MINUS            =0x0C,    /* - on main keyboard */         KC_EQUALS         =0x0D,         KC_BACK             =0x0E,    /* backspace */         KC_TAB              =0x0F,         KC_Q                  =0x10,         KC_W                 =0x11,         KC_E                  =0x12,         KC_R                  =0x13,         KC_T                 =0x14,         KC_Y                 =0x15,         KC_U                 =0x16,         KC_I                  =0x17,         KC_O                 =0x18,         KC_P                 =0x19,         KC_LBRACKET     =0x1A,         KC_RBRACKET     =0x1B,         KC_RETURN        =0x1C,    /* Enter on main keyboard */         KC_LCONTROL    =0x1D,         KC_A                 =0x1E,         KC_S                 =0x1F,         KC_D                 =0x20,         KC_F                 =0x21,         KC_G                 =0x22,         KC_H                 =0x23,         KC_J                 =0x24,         KC_K                 =0x25,         KC_L                 =0x26,         KC_SEMICOLON         =0x27,         KC_APOSTROPHE      =0x28,         KC_GRAVE                =0x29,    /* accent grave */         KC_LSHIFT                =0x2A,         KC_BACKSLASH         =0x2B,         KC_Z               =0x2C,         KC_X               =0x2D,         KC_C               =0x2E,         KC_V               =0x2F,         KC_B               =0x30,         KC_N               =0x31,         KC_M               =0x32,         KC_COMMA           =0x33,         KC_PERIOD           =0x34,    /* . on main keyboard */         KC_SLASH             =0x35,    /* ''/'' on main keyboard */         KC_RSHIFT            =0x36,         KC_MULTIPLY        =0x37,    /* * on numeric keypad */         KC_LMENU             =0x38,    /* left Alt */         KC_SPACE             =0x39,         KC_CAPITAL          =0x3A,         KC_F1              =0x3B,         KC_F2              =0x3C,         KC_F3              =0x3D,         KC_F4              =0x3E,         KC_F5              =0x3F,         KC_F6              =0x40,         KC_F7              =0x41,         KC_F8              =0x42,         KC_F9              =0x43,         KC_F10            =0x44,         KC_NUMLOCK         =0x45,         KC_SCROLL            =0x46,    /* Scroll Lock */         KC_NUMPAD7         =0x47,         KC_NUMPAD8         =0x48,         KC_NUMPAD9         =0x49,         KC_SUBTRACT        =0x4A,    /* - on numeric keypad */         KC_NUMPAD4         =0x4B,         KC_NUMPAD5         =0x4C,         KC_NUMPAD6         =0x4D,         KC_ADD                 =0x4E,    /* + on numeric keypad */         KC_NUMPAD1         =0x4F,         KC_NUMPAD2         =0x50,         KC_NUMPAD3         =0x51,         KC_NUMPAD0         =0x52,         KC_DECIMAL          =0x53,    /* . on numeric keypad */         KC_OEM_102         =0x56,    /* < > | on UK/Germany keyboards */         KC_F11             =0x57,         KC_F12             =0x58,         KC_F13             =0x64,    /*                     (NEC PC98) */         KC_F14             =0x65,    /*                     (NEC PC98) */         KC_F15             =0x66,    /*                     (NEC PC98) */         KC_KANA                  =0x70,    /* (Japanese keyboard)            */         KC_ABNT_C1             =0x73,    /* / ? on Portugese (Brazilian) keyboards */         KC_CONVERT             =0x79,    /* (Japanese keyboard)            */         KC_NOCONVERT        =0x7B,    /* (Japanese keyboard)            */         KC_YEN                    =0x7D,    /* (Japanese keyboard)            */         KC_ABNT_C2             =0x7E,    /* Numpad . on Portugese (Brazilian) keyboards */         KC_NUMPADEQUALS   =0x8D,    /* = on numeric keypad (NEC PC98) */         KC_PREVTRACK          =0x90,    /* Previous Track (KC_CIRCUMFLEX on Japanese keyboard) */         KC_AT                      =0x91,    /*                     (NEC PC98) */         KC_COLON                =0x92,    /*                     (NEC PC98) */         KC_UNDERLINE          =0x93,    /*                     (NEC PC98) */         KC_KANJI                  =0x94,    /* (Japanese keyboard)            */         KC_STOP                  =0x95,    /*                     (NEC PC98) */         KC_AX                      =0x96,    /*                     (Japan AX) */         KC_UNLABELED          =0x97,    /*                        (J3100) */         KC_NEXTTRACK         =0x99,    /* Next Track */         KC_NUMPADENTER     =0x9C,    /* Enter on numeric keypad */         KC_RCONTROL           =0x9D,         KC_MUTE                  =0xA0,    /* Mute */         KC_CALCULATOR        =0xA1,    /* Calculator */         KC_PLAYPAUSE          =0xA2,    /* Play / Pause */         KC_MEDIASTOP         =0xA4,    /* Media Stop */         KC_VOLUMEDOWN      =0xAE,    /* Volume - */         KC_VOLUMEUP           =0xB0,    /* Volume + */         KC_WEBHOME            =0xB2,    /* Web home */         KC_NUMPADCOMMA     =0xB3,    /* , on numeric keypad (NEC PC98) */         KC_DIVIDE           =0xB5,    /* / on numeric keypad */         KC_SYSRQ           =0xB7,         KC_RMENU           =0xB8,    /* right Alt */         KC_PAUSE           =0xC5,    /* Pause */         KC_HOME            =0xC7,    /* Home on arrow keypad */         KC_UP                =0xC8,    /* UpArrow on arrow keypad */         KC_PGUP             =0xC9,    /* PgUp on arrow keypad */         KC_LEFT             =0xCB,    /* LeftArrow on arrow keypad */         KC_RIGHT           =0xCD,    /* RightArrow on arrow keypad */         KC_END              =0xCF,    /* End on arrow keypad */         KC_DOWN          =0xD0,    /* DownArrow on arrow keypad */         KC_PGDOWN       =0xD1,    /* PgDn on arrow keypad */         KC_INSERT          =0xD2,    /* Insert on arrow keypad */         KC_DELETE         =0xD3,    /* Delete on arrow keypad */         KC_LWIN            =0xDB,    /* Left Windows key */         KC_RWIN            =0xDC,    /* Right Windows key */         KC_APPS            =0xDD,    /* AppMenu key */         KC_POWER         =0xDE,    /* System Power */         KC_SLEEP           =0xDF,    /* System Sleep */         KC_WAKE           =0xE3,    /* System Wake */         KC_WEBSEARCH         =0xE5,    /* Web Search */         KC_WEBFAVORITES    =0xE6,    /* Web Favorites */         KC_WEBREFRESH        =0xE7,    /* Web Refresh */         KC_WEBSTOP             =0xE8,    /* Web Stop */         KC_WEBFORWARD      =0xE9,    /* Web Forward */         KC_WEBBACK             =0xEA,    /* Web Back */         KC_MYCOMPUTER       =0xEB,    /* My Computer */         KC_MAIL                    =0xEC,    /* Mail */         KC_MEDIASELECT       =0xED     /* Media Select */     };  

下面分析源代码。

我们需要控制的角色节点,在方法 
void KeyboardApplication::createScene( void ) 
中建立:
    // 创建模型实体     Entity* entObject = mSceneMgr->createEntity( "object", "ninja.mesh" );     entObject->setCastShadows( true );      SceneNode* objectNode = rootNode->createChildSceneNode( "objectNode" );      objectNode->attachObject( entObject );      objectNode->setPosition( Vector3( 0, -27, 0 ) );  

注意其中这一句: 
SceneNode* objectNode = rootNode->createChildSceneNode( "objectNode" ); 
我们在创建这个场景节点的时候,在括号里顺手为它起了一个名字叫作 "objectNode" ,这是有用处的,以后我们就可以根据这个名字,使用 
SceneNode * Ogre::SceneManager:: getSceneNode ( const String & name ) const [virtual] 
通过提供场景节点的名字来获取这个场景节点。

因为 SceneManager* mSceneMgr 是应用程序类 Ogre:: ExampleApplication 的保护成员,而我们要控制角色节点的移动,必须在帧监听器类 Ogre:: ExampleFrameListener 中逐帧控制,所以我们必须从 ExampleApplication 向 ExampleFrameListener 传递 mSceneMgr 实例参数。总体说来我们需要这样做:

(1) 在 
void KeyboardApplication::createScene( void ) 
方法中,为角色实体建立并绑定场景到一个节点,并赋予这个场景节点一个唯一的名字;

(2) 在 
void KeyboardApplication::createFrameListener( void ) 
方法中,新建帧监听器实例并向其传递 mSceneMgr :

    // 新建帧监听器实例     mFrameListener= new KeyboardFrameListener( mWindow, mCamera, mSceneMgr );     mRoot->addFrameListener( mFrameListener );                  // 添加帧监听器到根节点 

(3) 重载 KeyboardFrameListener 的构造函数并接收上一步传递过来的 mSceneMgr 实例参数:

KeyboardFrameListener::KeyboardFrameListener( RenderWindow* win, Camera* cam, SceneManager* sm )     : ExampleFrameListener( win, cam ), mSceneMgr( sm ) { } 

(4) 使用 
SceneNode * Ogre::SceneManager:: getSceneNode ( const String & name ) const [virtual] 
方法,使用角色场景节点的名字来获取这个场景节点:

mSceneNode = mSceneMgr->getSceneNode( "objectNode" ); // 获取角色对象的场景节点

然后嘛,我们就可以对这个场景节点为所欲为了。

我们要在每帧实时控制场景管理器或场景节点的属性,都可以使用这个通用方法。只要我们在帧监听器类中获取了场景节点的实例 mSceneMgr ,我们就可以使用 Ogre::SceneManager 中的大量 get 方法,例如:

virtual Camera *  getCamera (const String &name)                          // 获取摄像机 virtual Light *  getLight (const String &name)                            // 获取光源 virtual Material *  getMaterial (const String &name)                      // 获取材质 virtual SceneNode *  getRootSceneNode (void) const                        // 获取场景根节点 virtual SceneNode *  getSceneNode (const String &name) const              // 获取场景节点 virtual Entity *  getEntity (const String &name)                          // 获取实体 virtual BillboardSet *  getBillboardSet (const String &name)              // 获取公告板集 virtual Animation *  getAnimation (const String &name) const              // 获取动画 virtual AnimationState *  getAnimationState (const String &animName)      // 获取动画状态 virtual Overlay *  getOverlay (const String &name)                        // 获取盖层(界面) 

来获取场景中的任意实例。条件就是你在创建这些实例的时候务必要顺手给它起个名字,方便以后按名字查找。

最后是 static Real timeDelay (时间延迟) 这个静态变量的作用,用来控制接收键盘输入的时间间隔。因为如果我们不去控制每隔多长时间移动一次的话,角色就每帧移动一次,这样,在帧数 FPS 很高的时候它就移动得很快,在 FPS 低的时候它就移动得很慢,我们无法预料客户机的 FPS ,所以如果不使用这个 timeDelay ,我们无法控制角色的实际移动速度。

时间延迟的每帧刷新使用下面的代码:
    if (timeDelay >= 0)          timeDelay -= evt.timeSinceLastFrame;         // 时间延迟 -= 从上次调用时到现在所经历的时间 

然后每接收一次键盘输入就使时间延迟重新倒数:
if (mInputDevice->isKeyDown( KC_I ) && timeDelay <= 0)     {         mSceneNode->translate( Vector3( 0, 0, -walkSpeed ) );         timeDelay = 0.02;     } 

timeSinceLastFrame 是 Ogre:: FrameEvent 的数据成员,在头文件 OgreFrameListener.h 中定义,单位是秒: 
Real Ogre::FrameEvent::timeSinceLastFrame 
这个数据成员记录的是:上一次访问 FrameEvent::timeSinceLastFrame 属性到现在的时间差(秒)。

posted @ 2009-06-08 13:21  回忆1919  阅读(1030)  评论(0编辑  收藏  举报