DirectX 9.0高级游戏编程(第3章)--使用DirectInput进行通信 收藏 概述 得到用户的输入可能是任何计算机游戏最重要的部分。如果没有用户输入,那么无论多时髦的图形或者多伟大的音效,你都会觉得这只不过是在看电影,而不是游戏。一个游戏需要从键盘,鼠标或者摇杆上取得用户输入的信息,这样它才能够同用户进行交互。过去,对这些不同的设备进行编程需要付出很大的代价,而且对摇杆还要进行一些独特的处理,为了解决这个问题,微软创建了DirectInput. DirectInput的创建为同用户系统上的输入设备进行通信提供了一条直接的方式。它可以列举连接在机器上的所有的设备,甚至可以列举出特殊设备的性能。你可以使用任何公司的输入设备,只要它有DirectInput驱动程序,你的应用程序就可以同它交谈。事实上,今天的每一个输入设备都有DirectInput驱动程序。 DirectInput有许多极好的特性,比如强制反馈,不过,我不想在这里过多地讨论它。我们的DirectInput议题将被限制在鼠标和键盘的用法。然而,一旦你懂得了使DirectInput工作的基本概念,那么再理解它的一些复杂事物将不再是难事。 Win32 API有一套完全的窗口消息,它可以通知你什么时候键被按下了,什么时候鼠标移动了等等。它也能够支持摇杆。那么使用DirectInput比使用标准的API调用有什么优势呢?这里有以下几个原因: 1. Win32 API不是为游戏或者速度而设计的 2. Win32最多只能支持那种简单的摇杆,如果是复杂的摇杆,比如那种带几个轴的,8到10个按钮的等等,Win32就支持不了了。 3. 对鼠标的支持被限制在3个按钮,2个轴,一个滚轮。但是今天的市场上,很多鼠标有4个,5个按钮,甚至更多。 4. Win32对键盘的支持,主要是被设计用来支持键盘输入应用程序的。它有很多可以自动处理重复的键,可以把键码转换成ASCII字符等的功能,但是这些功能,游戏都不需要,而且,这会导致更宝贵的处理器循环的浪费。 5. Win32键盘处理代码虽然可以为你捕获一些键(比如Alt),但是这需要特殊的消息处理,才能够得到正确的操作。 6. 消息处理不是这个世界上最快的事情。应用程序得到了很多所需的鼠标消息,但是你必须等到这些消息队列空了以后才能够渲染桢,这将导致整个应用程序变慢。 设备 所谓DirectInput设备,即一个可以向计算机提供输入的物理对象。键盘,鼠标,各种摇杆都属于DirectInput设备。你可以通过一个COM接口同这些设备进行交谈,比如用Direct3D。这个接口的名字,在DirectX 9.0中叫做IDirectInputDevice8. 注意:你可能会感到疑惑,为什么在DirectX 9.0中的设备会被叫做IDirectInputDevice8.这是因为这个特殊的接口自从最近一个版本(8.0版)以后就没有再被更新过,所以这个接口是属于DirectX 8.0的。唯一的不同就是在后台兼容性上的变化。 设备由一些对象组成,每个对象都定义了一些按钮,轴,POV hat等。使用IDirect-InputDevice8::EnumObjects,设备可以列举出它所包含的所有对象。不过这些只是对摇杆有用,因为键盘和鼠标都有一套标准的对象。 对象通过一个叫做DIDEVICEOBJECT- INSTANCE的结构被描述出来。DirectInput功能集可以使你不需要知道这些结构的工作方式。为区别于其他事物,结构使用GUID来描述对象的类型。表3.1显示了当前对象类型的GUID集合,其他更多的将在以后当用户创建更新,更好的对象类型的时候公布。 表3.1 当前对象类型的GUID集合 GUID_XAxis 表示在x轴上移动的轴(比如,鼠标左右移动) GUID_YAxis 表示在y轴上移动的轴(比如,鼠标上下移动) GUID_Zaxis 表示在z轴上移动的轴(比如,新型的鼠标滚轮) GUID_RxAxis 表示相对于x轴旋转的轴 GUID_RyAxis 表示相对于y轴旋转的轴 GUID_RzAxis 表示相对于z轴旋转的轴 GUID_Slider 滑动轴(比如,在一些摇杆上有的throttle滑块) GUID_Button 按钮(鼠标或者摇杆上) GUID_Key 键(键盘上) GUID_POV 在一些摇杆上有的POV hat GUID_Unknown 未知的设备类型 当应用程序需要知道设备当前的状态的时候,这些信息经常需要通过一定的方式才能被传输过来。有时候我们可能只是需要得到一个字节列表,那么就不需要设备提供很多额外的信息;另一方面,如果我们强制应用程序使用标准的通信方法来获得这些信息,也并不能很好的解决市场上各种类型的设备的问题。因为这些原因,所以DirectInput让应用程序直接跟设备交流,告诉这些设备它希望从设备中接收什么样的数据。如果你只是希望得到摇杆上1到2个按钮的数据,那么你就不需要向摇杆请求所有的数据,因为这些数据可能包含一打的按钮。另外,应用程序还要能够判定设备上的轴是绝对的(以原点为中心,就像摇杆的轴)还是相对的(自由的移动,就像鼠标轴)。当一个设备被创建以后,你就必须调用IDirectInputDevice8::SetDataFormat。 HRESULT IDirectInputDevice8::SetDataFormat( LPCDIDATAFORMAT lpdf ); lpdf 一个指向DIDATAFORMAT结构的指针,该结构定义了从设备中接收到的数据的格式。 你可以使用以下几个已经定义的常量: 1) c_dfDIKeyboard 标准的键盘结构。256个字符组成的数组,每个字符对应每个键。 2) c_dfDIMouse 标准的鼠标结构。带有3个轴和4个按钮,符合DIMOUSESTATE结构的那种。 3) c_dfDIMouse2 鼠标结构扩展。带有3个轴和8个按钮,符合DIMOUSESTATE2结构的那种 4) c_dfDIJoystick 标准的摇杆。带有3个定位轴,3个旋转轴,2个滑块,1个POV hat,32个按钮,符合DIJOYSTATE结构的那种。 5) c_dfDIJoystick2 扩展了性能的摇杆。引用SDK文档中的集群数据格式定义,符合DIJOYSTATE2结构。 接收设备的状态 可以有两种方式去接收来自设备的数据:立即数据访问和缓冲数据访问。一般代码只使用立即数据访问,但这并不是说缓冲数据访问就没有优点了。缓冲数据访问主要用于当你需要完全地获得所有所发生的输入事件的时候。如果一个键在立即设备状态请求中快速地被按下并释放,你将看不到它,因为状态的改变并没有被排队。如果应用程序在任何合理的桢频下运行,这当然不会成为一个问题。立即数据访问被用来查找设备在某个时刻某些点的当前状态。当你发出请求的时候,如果按钮被按下或者释放,你就不能看到他们。你可以使用IDirectInputDevice8::GetDeviceState: HRESULT IDirectInputDevice8::GetDeviceState( DWORD cbData, LPVOID lpvData ); cbData 尺寸,用字节表示,和lpvData一起被传递进来的数据结构。 LpvData 指向缓冲的指针,该缓冲用来填充设备状态的。数据的格式取决于你使用SetDataFormat所定义的格式 。 对鼠标设备来说,如果你把数据格式设置成c_dfDIMouse,则GetDeviceData的参数应该为sizeof(DIMOUSESTATE)和有效的DIMOUSESTATE结构的地址。在函数完成之后,如果它成功了,结构将被来自鼠标的数据充满。 typedef struct DIMOUSESTATE { LONG lX; LONG lY; LONG lZ; BYTE rgbButtons[4]; } DIMOUSESTATE, *LPDIMOUSESTATE; lX 移动的X轴。相对移动;如果从你上次使用以来一直没有移动过,它将被置为0。 lY 移动的Y轴。相对移动;如果从你上次使用以来一直没有移动过,它将被置为0。 lZ 移动的Z轴(鼠标滚轮)。相对移动;如果从你上次使用以来一直没有移动过,它将被置为0。 rgbButtons 一组字节,有四个按钮的鼠标中的其中一个按钮。如果要支持具有更多按钮的鼠标,就要使用DIMOUSESTATE2结构。 至于键盘数据,你全部所作的都被传递进一个256个元素组成的字符数组。每个字符都代表一定的键。你可以使用DirectInput 键常量,通过数组的索引找到那个键。这里列出了键盘上所有可能键的常量。表3.2是一张公共列表,一些非常模糊的键,像日本键盘和web键盘上的一些键,没有包括在内。要得到完整列表,请查看SDK文档(DirectX 9.0 C++ Documentation/DirectInput/ DirectInput C++ Reference/Device Constants/Keyboard Device)。表3.2 DirectInput公共键盘常量 DIK_A … DIK_Z A到Z键 DIK_0 … DIK_9 0到9键 DIK_F1 … DIK_F15 F1到F15键,如果这些间存在的话。 DIK_NUMPAD0 … DIK_NUMPAD9 数字键盘上的键。不管Num Lock键有没有打开,这些键所对应的常量都是不变的。 DIK_ESCAPE Esc键 DIK_MINUS 键盘顶部一行中的-键。 DIK_EQUALS 键盘顶部一行中的=键。 DIK_BACK Backspace键 DIK_TAB Tab键 DIK_LBRACKET [(左括号)键 DIK_RBRACKET ](右括号)键 DIK_RETURN Return键 DIK_LCONTROL 左边Ctrl键 DIK_SEMICOLON ;(分号)键 DIK_APOSTROPHE '(单引号)键 DIK_GRAVE ‘ (重音) 键;跟 ~ 键为同一个键 DIK_LSHIFT 左边Shift键 DIK_BACKSLASH /反斜杠键 DIK_COMMA ,(逗号)键 DIK_PERIOD . (句点) 键 DIK_SLASH / (斜线) 键 DIK_RSHIFT 右边Shift 键 DIK_MULTIPLY 数字面板上的*键 DIK_LMENU 左边 Alt 键 DIK_SPACE 空格键 DIK_CAPITAL Caps Lock 键 DIK_NUMLOCK Num Lock键 DIK_SCROLL Scroll Lock键 DIK_SUBTRACT 数字键区的-键 DIK_ADD 数字键区的+键 DIK_DECIMAL 数字键区的. 键 DIK_NUMPADENTER 数字键区的Enter键 DIK_RCONTROL 右边的Ctrl键 DIK_DIVIDE 数字键区的 / 键 DIK_SYSRQ SysRq (跟 PrtScrn同一个)键 DIK_RMENU 右边的Alt键 DIK_PAUSE Pause 键 DIK_HOME Home 键 DIK_UP 向上箭头 DIK_PRIOR PgUp键 DIK_LEFT 向左箭头 DIK_RIGHT 向右箭头 DIK_END End 键 DIK_DOWN 向下箭头 DIK_NEXT PgDn键 DIK_INSERT Insert 键 DIK_DELETE Delete键 DIK_LWIN 左边 Windows 键 DIK_RWIN 右边Windows键 DIK_APPS 应用程序键协调级别 因为DirectInput设备要被使用系统的所有应用程序共享,所以有一个协调级别的概念。在你成功创建了一个IDirectInputDevice8接口的基础上,你要做的第一件事情就是设置协调级别。设置协调级别的调用是: HRESULT IDirectInputDevice8::SetCooperativeLevel( HWND hwnd, DWORD dwFlags ); hwnd 创建对象的应用程序的窗口句柄。 DwFlags 一组描述协调级别的标志。可以由以下组合: 1)DISCL_BACKGROUND 当这个标志被设置的时候,应用程序可以在任何时候获得设备,即使它当前不是活动应用程序。 2)DISCL_EXCLUSIVE 应用程序需要独占访问输入设备。 这会阻止其他应用程序同时使用设备(例如,Windows本身)。如果鼠标设备被设置成了独占模式,Windows就停止发送鼠标消息并且光标消失。 3)DISCL_FOREGROUND 当这个标志被设置的时候,如果窗口移到后台,设备不会自动被获得。只有在应用程序移到前台的时候,设备才能被重新获得。 4)DISCL_NONEXCLUSIVE 应用程序需要非独占访问输入设备。这个方式它不会妨碍其他应用程序同时使用设备(例如,Windows本身)。 5)DISCL_NOWINKEY 禁止使用Windows的键。这可以防止用户通过意外地按Windows键而妨碍独占的应用程序。 所有的设备必须设置为DISCL_FOREGROUND或者DISCL_BACKGROUND中的一种(两者不能同时都设置),并且必须设置为DISCL_EXCLUSIVE或者DISCL_NONEXCLUSIVE中的一种(两者不能同时设置)。 应用程序焦点和设备 如果你以前没有从设备那里得到过它的状态,那么已经失去了有权使用的机会。例如,当应用程序没有获得焦点的时候,你就不能取得键盘的状态。当应用程序类失去焦点的时候,它就自动检测,并且从轮流检测设备中停止输入代码,直到重新获得焦点。当你得到了焦点以后,在你开始请求它的状态前,你需要重新获得设备。这可以通过使用一个零参数的函数IDirectInputDevice*::Acquire()来完成。你将可以看到Acquire遍及键盘和鼠标的输入代码部分。 DirectInput对象 DirectInput对象(具有IDirectInputDevice8接口的)并没有像Direct3D设备对象一样,明显地绑定到物理设备。但是,它是非常有用的,你需要用它来枚举有用的设备并且创建这些设备。为了创建DirectInput对象,你需要用到一个全局函数DirectInput8Create,它集合了所有所需的COM的操作。 HRESULT WINAPI DirectInput8Create( HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID* ppvOut, LPUNKNOWN punkOuter ); inst 用来创建DirectInput的应用程序的实例句柄。 dwVersion 你想创建的DirectInput对象的版本号。这个参数你要指定为DIRECTINPUT_VERSION。 riidltf 你要创建的接口的标识符。只有为这个参数指定IID_IDirectInput8才不会出错。 ppvOut 指针的地址,它接收新创建的接口的地址。 punkOuter 被COM集合所使用 - 只需要指定为NULL就可以了。 一旦你拥有了DirectInput接口,你就可以使用它来枚举和创建设备。设备的创建是使用IDirectInput8::CreateDevice. 用cInputLayer来实现DirectInput 由于我给你看的那些具有全部的DirectInput功能的子集的存在,因此处理DirectInput的代码变得非常简单。虽然增加对简易的摇杆的支持将不再很难,但是实现一个可以枚举设备对象,并且赋予他们任务的健壮的系统则需要考虑很多的工作,在这里我们就不做讨论了。 代码工作的方式就是构建输入层,并且连接到鼠标对象和键盘对象(分别使用cMouse和cKeyboard)。鼠标和键盘都要有监听者或者有当事件发生的时候可以被通知的类。为了让一个类成为一个监听者,你必须做好2件事情。第一,这个类必须实现iKeyboardReceiver接口(对键盘来讲)和(或)iMouseReceiver接口(对鼠标设备来讲).第二,它必须让键盘或者鼠标成为一个接收者,这个可以通过调用cKeyboard::SetReceiver()或者cMouse::SetReceiver()来实现。这个做法其实只是接受希望成为接收者的类的地址而已。以下为这几个接口: 列表 3.1: Input communication interfaces /** * Any object that implements this interface can receive input * from the keyboard. */ struct iKeyboardReceiver { virtual void KeyUp( int key) = 0; virtual void KeyDown( int key) = 0; }; /** * Any object that implements this interface can receive input * from the mouse. */ struct iMouseReceiver { virtual void MouseMoved( int dx, int dy) = 0; virtual void MouseButtonUp( int button) = 0; virtual void MouseButtonDown( int button) = 0; }; 输入层是另一种系统对象,和别的对象一样它只能有一个实例。这个条件在构造函数中就被验证了。输入层显示在列表3.2(头文件)和3.3(源文件): 列表 3.2: InputLayer.h /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Desc: Sample application for Direct3D * * copyright (c) 2002 by Peter A Walsh and Adrian Perez * See license.txt for modification and distribution information ******************************************************************/ #ifndef _INPUTLAYER_H #define _INPUTLAYER_H #include #include "Keyboard.h" #include "Mouse.h" class cInputLayer { cKeyboard* m_pKeyboard; cMouse* m_pMouse; // The DI8 object LPDIRECTINPUT8 m_pDI; static cInputLayer* m_pGlobalILayer; cInputLayer( HINSTANCE hInst, HWND hWnd, bool bExclusive, bool bUseKeyboard = true, bool bUseMouse = true ); public: virtual ~cInputLayer(); cKeyboard* GetKeyboard() { return m_pKeyboard; } cMouse* GetMouse() { return m_pMouse; } void UpdateDevices(); static cInputLayer* GetInput() { return m_pGlobalILayer; } LPDIRECTINPUT8 GetDInput() { return m_pDI; } void SetFocus(); // called when the app gains focus void KillFocus(); // called when the app must release focus static void Create( HINSTANCE hInst, HWND hWnd, bool bExclusive, bool bUseKeyboard = true, bool bUseMouse = true ) { //everything is taken care of in the constructor new cInputLayer( hInst, hWnd, bExclusive, bUseKeyboard, bUseMouse ); } }; inline cInputLayer* Input() { return cInputLayer::GetInput(); } #endif //_INPUTLAYER_H 列表 3.3: InputLayer.cpp /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Desc: Sample application for Direct3D * * copyright (c) 2002 by Peter A Walsh and Adrian Perez * See license.txt for modification and distribution information ******************************************************************/ #include "stdafx.h" #include "InputLayer.h" #include "Keyboard.h" #include "Mouse.h" #include "Application.h" #include "Window.h" cInputLayer* cInputLayer::m_pGlobalILayer = NULL; cInputLayer::cInputLayer( HINSTANCE hInst, HWND hWnd, bool bExclusive, bool bUseKeyboard, bool bUseMouse ) { m_pKeyboard = NULL; m_pMouse = NULL; if( m_pGlobalILayer ) { throw cGameError("cInputLayer already initialized!/n"); } m_pGlobalILayer = this; HRESULT hr; /** * Create the DI8 object */ hr = DirectInput8Create( hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_pDI, NULL ); if( FAILED(hr) ) { throw cGameError("DirectInput8 object could not be created/n"); } try { if( bUseKeyboard ) { m_pKeyboard = new cKeyboard( hWnd ); } if( bUseMouse ) { m_pMouse = new cMouse( hWnd, bExclusive ); } } catch( ... ) { SafeRelease( m_pDI ); throw; } } cInputLayer::~cInputLayer() { if( m_pDI ) { if( m_pKeyboard ) { delete m_pKeyboard; // this does all the de-init. } if( m_pMouse ) { delete m_pMouse; // this does all the de-init. } SafeRelease( m_pDI ); } m_pGlobalILayer = NULL; } void cInputLayer::UpdateDevices() { if( m_pKeyboard ) { m_pKeyboard->Update(); } if( m_pMouse ) { m_pMouse->Update(); } } void cInputLayer::SetFocus() { if( m_pKeyboard ) { m_pKeyboard->ClearTable(); } if( m_pMouse ) { m_pMouse->Acquire(); } } void cInputLayer::KillFocus() { if( m_pKeyboard ) { m_pKeyboard->ClearTable(); } if( m_pMouse ) { m_pMouse->UnAcquire(); } } 当用一种很容易的方式为类监听按下的键提供监听接口的时候,键盘对象多数都是围绕IDirectInputDevice8接口的。如果你不想使用监听者,只需要在最后的检查时间在键盘对象上调用Poll方法查找一定的键的状态就可以了。 列表 3.4: Keyboard.h /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Desc: Sample application for Direct3D * * copyright (c) 2002 by Peter A Walsh and Adrian Perez * See license.txt for modification and distribution information ******************************************************************/ #ifndef _KEYBOARD_H #define _KEYBOARD_H #include #include class cInputLayer; /** * Any object that implements this interface can receive input * from the keyboard. */ struct iKeyboardReceiver { virtual void KeyUp( int key ){}; virtual void KeyDown( int key ){}; }; class cKeyboard { // The DInput device used to encapsulate the keyboard LPDIRECTINPUTDEVICE8 m_pDevice; char m_keyState[256]; iKeyboardReceiver* m_pTarget; public: void ClearTable() { memset( m_keyState, 0, sizeof(char)*256 ); } cKeyboard( HWND hWnd ); ~cKeyboard(); // Poll to see if a certain key is down bool Poll( int key ); // Use this to establish a KeyboardReceiver as the current input focus void SetReceiver( iKeyboardReceiver* pTarget ); eResult Update(); }; #endif //_KEYBOARD_H 列表 3.5: Keyboard.cpp /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Desc: Sample application for Direct3D * * copyright (c) 2002 by Peter A Walsh and Adrian Perez * See license.txt for modification and distribution information ******************************************************************/ #include "stdafx.h" #include "InputLayer.h" #include "window.h" #include using namespace std; #include "Keyboard.h" cKeyboard::cKeyboard( HWND hWnd ) { m_pTarget = NULL; HRESULT hr; /** * Get the DInput interface pointer */ LPDIRECTINPUT8 pDI = Input()->GetDInput(); /** * Create the keyboard device * */ hr = Input()->GetDInput()->CreateDevice( GUID_SysKeyboard, &m_pDevice, NULL ); if( FAILED(hr) ) { throw cGameError("Keyboard could not be created/n"); } /** * Set the keyboard data format */ hr = m_pDevice->SetDataFormat(&c_dfDIKeyboard); if( FAILED(hr) ) { SafeRelease( m_pDevice ); throw cGameError("Keyboard could not be created/n"); } /** * Set the cooperative level */ hr = m_pDevice->SetCooperativeLevel( hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); if( FAILED(hr) ) { SafeRelease( m_pDevice ); throw cGameError("Keyboard coop level could not be changed/n"); } memset( m_keyState, 0, 256*sizeof(bool) ); } cKeyboard::~cKeyboard() { if( m_pDevice ) { m_pDevice->Unacquire(); SafeRelease( m_pDevice ); } } void cKeyboard::SetReceiver( iKeyboardReceiver* pTarget ) { // Set the new target. m_pTarget = pTarget; } bool cKeyboard::Poll( int key ) { // stuff goes in here. if( m_keyState[key] & 0 x 80 ) return true; return false; } eResult cKeyboard::Update() { char newState[256]; HRESULT hr; hr = m_pDevice->Poll(); hr = m_pDevice->GetDeviceState(sizeof(newState),(LPVOID)&newState); if( FAILED(hr) ) { hr = m_pDevice->Acquire(); if( FAILED( hr ) ) { return resFailed; } hr = m_pDevice->Poll(); hr = m_pDevice->GetDeviceState(sizeof(newState),(LPVOID)&newState); if( FAILED( hr ) ) { return resFailed; } } if( m_pTarget ) { int i; for( i=0; i< 256; i++ ) { if( m_keyState[i] != newState[i] ) { // Something happened to this key since last checked if( !(newState[i] & 0 x 80) ) { // It was Released m_pTarget->KeyUp( i ); } else { // Do nothing; it was just pressed, it'll get a keydown // in a bit, and we don't want to send the signal to // the input target twice } } // copy the state over (we could do a memcpy at the end, but this // will have better cache performance) m_keyState[i] = newState[i]; if( Poll(i)) { // It was pressed m_pTarget->KeyDown( i ); } } } else { // copy the new states over. memcpy( m_keyState, newState, 256 ); } return resAllGood; } 鼠标对象的函数跟键盘对象几乎是一样的。鼠标的幕后代码显示在列表3.6(头文件)和列表3.7(源文件)。列表 3.6: Mouse.h /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Desc: Sample application for Direct3D * * copyright (c) 2002 by Peter A Walsh and Adrian Perez * See license.txt for modification and distribution information ******************************************************************/ #ifndef _MOUSE_H #define _MOUSE_H #include /** * Any object that implements this interface can receive input * from the mouse. */ struct iMouseReceiver { virtual void MouseMoved( int dx, int dy ){}; virtual void MouseButtonUp( int button ){}; virtual void MouseButtonDown( int button ){}; }; class cMouse { LPDIRECTINPUTDEVICE8 m_pDevice; DIMOUSESTATE m_lastState; iMouseReceiver* m_pTarget; public: cMouse( HWND hWnd, bool bExclusive ); ~cMouse(); /** * Use this to establish a MouseReceiver as the current * input focus */ void SetReceiver( iMouseReceiver* pTarget ); eResult Update(); eResult Acquire(); void UnAcquire(); }; #endif // _MOUSE_H 列表 3.7: Mouse.cpp /******************************************************************* * Advanced 3D Game Programming using DirectX 9.0 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Desc: Sample application for Direct3D * * copyright (c) 2002 by Peter A Walsh and Adrian Perez * See license.txt for modification and distribution information ******************************************************************/ #include "stdafx.h" #include "InputLayer.h" #include "Window.h" #include "Mouse.h" cMouse::cMouse( HWND hWnd, bool bExclusive ) { m_pTarget = NULL; HRESULT hr; /** * Create the device * */ hr = Input()->GetDInput()->CreateDevice( GUID_SysMouse, &m_pDevice, NULL ); if( FAILED( hr )) { throw cGameError("[cMouse::Init]: Couldn't create the device!/n"); } /** * Set the data format */ hr = m_pDevice->SetDataFormat(&c_dfDIMouse); if( FAILED( hr )) { SafeRelease( m_pDevice ); throw cGameError("[cMouse::Init]: SetDataFormat failed/n"); } /** * Set the cooperative level */ if( bExclusive ) { hr = m_pDevice->SetCooperativeLevel( hWnd, DISCL_EXCLUSIVE | DISCL_NOWINKEY | DISCL_FOREGROUND ); } else { hr = m_pDevice->SetCooperativeLevel( hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND); } if( FAILED( hr )) { SafeRelease( m_pDevice ); throw cGameError("[cMouse::Init]: SetCooperativeLevel failed/n"); } m_lastState.lX = 0; m_lastState.lY = 0; m_lastState.lZ = 0; m_lastState.rgbButtons[0] = 0; m_lastState.rgbButtons[1] = 0; m_lastState.rgbButtons[2] = 0; m_lastState.rgbButtons[3] = 0; } cMouse::~cMouse() { if( m_pDevice ) { m_pDevice->Unacquire(); SafeRelease( m_pDevice ); } } void cMouse::SetReceiver( iMouseReceiver* pTarget ) { m_pTarget = pTarget; } eResult cMouse::Update() { DIMOUSESTATE currState; HRESULT hr; hr = m_pDevice->Poll(); hr = m_pDevice->GetDeviceState( sizeof(DIMOUSESTATE), (void*)&currState ); if( FAILED(hr) ) { hr = m_pDevice->Acquire(); if( FAILED( hr ) ) { return resFailed; } hr = m_pDevice->Poll(); hr = m_pDevice->GetDeviceState( sizeof(DIMOUSESTATE), (void*)&currState ); if( FAILED( hr ) ) { return resFailed; } } if( m_pTarget ) { int dx = currState.lX; int dy = currState.lY; if( dx || dy ) { m_pTarget->MouseMoved( dx, dy ); } if( currState.rgbButtons[0] & 0x80 ) { // the button got pressed. m_pTarget->MouseButtonDown( 0 ); } if( currState.rgbButtons[1] & 0x80 ) { // the button got pressed. m_pTarget->MouseButtonDown( 1 ); } if( currState.rgbButtons[2] & 0x80 ) { // the button got pressed. m_pTarget->MouseButtonDown( 2 ); } if( !(currState.rgbButtons[0] & 0x80) && (m_lastState.rgbButtons[0] & 0x80) ) { // the button got released. m_pTarget->MouseButtonUp( 0 ); } if( !(currState.rgbButtons[1] & 0x80) && (m_lastState.rgbButtons[1] & 0x80) ) { // the button got released. m_pTarget->MouseButtonUp( 1 ); } if( !(currState.rgbButtons[2] & 0x80) && (m_lastState.rgbButtons[2] & 0x80) ) { // the button got released. m_pTarget->MouseButtonUp( 2 ); } } m_lastState = currState; return resAllGood; } eResult cMouse::Acquire() { HRESULT hr = m_pDevice->Acquire(); if( FAILED(hr) ) { return resFailed; } return resAllGood; } void cMouse::UnAcquire() { m_pDevice->Unacquire(); } cApplication的题外话 cApplication的唯一题外话就是InitInput调用,它可以初始化键盘和鼠标。如果该方法所带的动作并不是你想要的,那么你可以重载它。代码如下: cInputLayer::Create( AppInstance(), MainWindow()->GetHWnd(), NULL, true, true ); 你可能也注意到了,本章没有一个例子程序。别担心,我决定把本章的例子和下一章的例子整合起来,因此你大概可以在下一章节中可以看到它。 DirectInput是一个比较棘手的东西,但是当你必须处理类似时间信号,BIOS,鼠标驱动程序的所有者的时候,相比较那些DOS时代所存在的东西,它确实是一个非常好的接口。 一旦你已经创建了DirectInput代码,你可以保存它,以后无需太多修改就可以给大多数项目使用。因此,如果将来你需要在你的项目中使用代码来进行输入处理,你只需要剪切和拷贝本章学习过的代码到你的代码中就行了。 现在,是时候学习DirectX更深的知识了,下一章,我们将学习DirectSound 本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/t1p2/archive/2004/09/21/112091.aspx
DirectX 9.0高级游戏编程(第3章)--使用DirectInput进行通信 收藏
最新推荐文章于 2021-10-24 16:16:22 发布