DIEDFL_ATTACHEDONLY Only attached and installed devices. DIEDFL_FORCEFEEDBACK Only devices that support force feedback. DIEDFL_INCLUDEALIASES Include devices that are aliases for other devices. DIEDFL_INCLUDEHIDDEN Include hidden devices. DIEDFL_INCLUDEPHANTOMS Include phantom (placeholder) devices. 下面是回调函数的声明: BOOL CALLBACK DIEnumDevicesProc( LPDIDEVICEINSTANCE lpddi, // 设备结构 LPVOID pvRef); lpddi是一个指向DIDEVICEINSTANCE结构的指针,它包含了当前所找到设备一些信息.下面是它的详细定义: typedef struct { DWORD dwSize; // Size of this structure GUID guidInstance; // device GUID GUID guidProduct; // OEM supplied GUID of device DWORD dwDevType; // Device type TCHAR tszInstanceName[MAX_PATH]; //Name of device TCHAR tszProductName[MAX_PATH]; //Name of product GUID guidFFDriver; // GUID of force-feedback driver WORD wUsagePage; // Usage page if an HID device WORD wUsage; // Usage code if an HID device } DIDEVICEINSTANCE; 下面就让我们来看一个具体的例子,它的功能是枚举系统中的所有输入设备,当找到一个后就弹出一对话框,根据我们的选择来决定是继续枚举还是停止运行: IDirectInput8 *g_pDI; BOOL InitDIAndEnumAllDevices(HWND hWnd,HINSTANCE hInst) { if(FAILED(DirectInput8Create(hInst, DIRECTINPUT_VERSION,IID_IDirectInput8, (void**)&g_pDI, NULL))) return FALSE; g_pDI->EnumDevices(DI8DEVCLASS_ALL, EnumDevices,(LPVOID)hWnd, DIEDFL_ALLDEVICES); return TRUE; } BOOL CALLBACK EnumDevices(LPCDIDEVICEINSTANCE pdInst, LPVOID pvRef) { int Result; // Display a message box with name of device found Result = MessageBox((HWND)pvRef, pdInst->tszInstanceName, "Device Found", MB_OKCANCEL); // Tell it to continue enumeration if OK pressed if(Result == IDOK) return DIENUM_CONTINUE; // Stop enumeration return DIENUM_STOP; } 然后把InitDIAndEnumAllDevices()插入到程序的相应位置就可以了.
设备对象的创建: 现在我们已经有了GUID,接下来就是要创建具体的设备接口对象了,这个工作是有下面这个函数来完成的: HRESULT IDirectInput8::CreateDevice( REFGUID rguid, // GUID of device to create, predefined or from enumeration LPDIRECTINPUTDEVICE *lplpDirectInputDevice, // pointer to the object you’re creating LPUNKNOWN pUnkOuter); // NULL - not used 它的参数都很明了,这里我们就不再多说,直接来看一个例子: IDirectInputDevice8 *pDIDevice; HRESULT hr = g_pDI->CreateDevice(DeviceGUID, &pDIDevice, NULL); 或许感觉这个例子还是不够具体,我们就来看看如何使用键盘: IDirectInputDevice8 *pDIDevice; HRESULT hr = pDI->CreateDevice(GUID_SysKeyboard,&pDIDevice, NULL); 设置数据格式: 各种输入设备发送的信息都是不同的,所以我们无法以一种固定的格式来接收所有的输入信息,所以我们需要为每个输入设备都设置一种数据格式以便来正确的接受来自设备的数据,设置工作由下面这个函数来完成: HRESULT IDirectInputDevice8::SetDataFormat(LPCDIDATAFORMAT lpdf); 该函数只有一个参数,一个指向DIDATAFORMAT结构的指针,下面我们来这个函数的具体定义: typedef struct { DWORD dwSize; // Size of this structure DWORD dwObjSize; // Size of DIOBJECTDATAFORMAT structure DWORD dwFlags; // Flags determining if device works in absolute mode (DIDF_ABSAXIS) or relative (DIDF_RELAXIS) DWORD dwDataSize; // Size of data packets received from device (in multiples of 4) DWORD dwNumObjs; // Number of objects in the rgodf array LPDIOBJECTDATAFORMAT rgodf; // Address to an array of DIOBJECTDATAFORMAT structures. } DIDATAFORMAT, *LPDIDATAFORMAT; 又是一个讨厌的数据结构,尽管它不是很复杂,但是我们见到的类似的数据结构实在是太多了,好在大多数情况下不用我们自己来创建其实例,因为DirectInput已经预定义好了一些: Device Data Structure Example Keyboard c_dfDIKeyboard pDIDevice->SetDataFormat(&c_dfDIKeyboard); Mouse c_dfDIMouse pDIDevice->SetDataFormat(&c_dfDIMouse); Joystick c_dfJoystick pDIDevice->SetDataFormat(&c_dfDIJoystick); 我们又一次站在了巨人的肩膀上,尽情享受着前人的果实,感觉着实舒服.如果你对他们的工作感到不屑,或者想自己开发这些,我不太赞同,不要重复发明轮子! 设置设备的共享等级: 游戏中往往使用多个输入设备,鼠标,键盘,游戏杆,甚至更多.但这里面有一个我们不得不考虑的问题:当我们使用这些输入设备的时候是否允许其它的应用程序同时使用.我们可以很霸道地独占这些设备,但这并不是最好的选择.让我们来看看如何设置吧: HRESULT IDirectInputDevice8::SetCooperativeLevel( HWND hWnd, // handle to the parent window DWORD dwFlags);// flags determining how to share Access hWnd是窗口句柄,dwFlags可以从以下值中选择:
等级 详细描述 DISCL_NONEXCLUSIVE 允许其他程序使用,并且不会干扰其他应用程序的使用 DISCL_EXCLUSIVE 独占模式,其它应用都不能使用 DISCL_FOREGROUND 前台模式,也就是说使用它的程序必须处于激活状态,如果它失去焦点就会自动失去设备,再它重新获得焦点的时候必须重新获得设备使用权 DISCL_BACKGROUND 后台模式,无论是否激活都能使用 DISCL_NOWINKEY This disables the Windows logo key. 当我们设置这些标志时,或者DISCL_EXCLUSIVE 或者 DISCL_NONEXCLUSIVE,并且要跟DISCL_FOREGROUND 或者 DISCL_BACKGROUND 合起来使用.我们建议大家这样来组合: pDIDevice->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE); 设置特殊属性: 除了前面我们所设置的属性外,我们还可以设置一些更为高级的属性.比如是使用相对坐标还是绝对坐标,相对坐标是相对上一次移动了的坐标,而绝对坐标是以一点为原点来算的.我们还可以来设置数据缓冲,我们可以来设置缓冲区的大小从而以我们喜欢的节奏来处理数据,所有的设置都是通过下面的代码来实现的: HRESULT IDirectInputDevice8::SetProperty( REFGUID rguidProp, // GUID of property LPCDIPROPHEADER pdiph); // DIPROPHEADER containing data about the property being set 下面是DIPROPRHEADER的定义: typedef struct { DWORD dwSize; // Size of the enclosing structure DWORD dwHeaderSize; // Size of this structure DWORD dwObj; // What value we’re setting DWORD dwHow; // How you’re setting the value } DIPROPHEADER, *LPDIPROPHEADER; 可以参阅DirectX SDK了解具体如何来使用上述代码来设置相应的属性. 获得设备: 在设备能被使用之前首先要得到它,这样才能使得我们的程序能接触到设备,不管设备是共享还是独占的.这里有一点需要注意:其它应用程序也是可以获得设备,所以必要的时候我们还要重新获得设备. 那我们怎么知道什么时候该获得设备呢?第一次通常是创建设备对象时,使用设备之前.其它时候就是其它程序夺取了使用权之后.下面的代码用来获得设备: HRESULT IDirectInputDevice8::Acquire(); 我们还可以释放: HRESULT IDirectInputDevice8::Unacquire();
为了避免在运行期间出现错误,接下来应该调用下面这句: HRESULT IDirectInputDevice8::Poll(); 这个函数的调用能够保证数据的正确性. 数据的读入: 终于,我们能够从输入设备中读入数据了,这个过程是由IDirectInputDevice8::GetDeviceState()来完成的.下面是它的原型: HRESULT IDirectInputDevice8::GetDeviceState( DWORD cbData, // 数据缓冲区的大小 LPVOID lpvData); // 数据缓冲区 第二个参数是需要的数据缓冲区,对于各种不同的数据设备数据缓冲区是不同的. 下面是一段读入数据的代码: BOOL ReadDevice(IDirectInputDevice8 *pDIDevice, void *DataBuffer, long BufferSize) { HRESULT hr; while(1) { // Poll device g_pDIDevice->Poll(); // Read in state if(SUCCEEDED(hr = g_pDIDevice->GetDeviceState(BufferSize,(LPVOID)DataBuffer))) break; // Return on an unknown error if(hr != DIERR_INPUTLOST && hr != DIERR_NOTACQUIRED) return FALSE; // Reacquire and try again if(FAILED(g_pDIDevice->Acquire())) return FALSE; } // Return a success return TRUE; } 下面我们来看看具体的处理键盘和鼠标的例子. 键盘的处理: IDirectInputDevice8*InitKeyboard(HWND hWnd, IDirectInput8 *pDI) { IDirectInputDevice8 *pDIDevice; // Create the device object if(FAILED(pDI->CreateDevice(GUID_SysKeyboard, &pDIDevice, NULL))) return NULL; // Set the data format if(FAILED(pDIDevice->SetDataFormat(&c_dfDIKeyboard))) { pDIDevice->Release(); return NULL; } // Set the cooperative mode if(FAILED(pDIDevice->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE))) { pDIDevice->Release(); return NULL; } // Acquire the device for use if(FAILED(pDIDevice->Acquire())) { pDIDevice->Release(); return NULL; } // Everything was a success, return the pointer return pDIDevice; } 上面的代码并不难理解,因为都是按照我们前面的讲述来的,所以这里就不再重复.这里只是做好了初始化的工作,在开始读数据之前我们首先要理解键盘的数据是如何保存的.我们必须提供一个256字节的数组,每个字节保存一个键的信息.所以我们一共可以处理256个键.每个键有两个状态:按下或者释放.为了查看键的状态通过查看相应字节的最高位(位7),如果是1则被按下,否则是处于释放状态. char KeyStateBuffer[256]; if((pDIDKeyboard = InitKeyboard(g_hWnd, g_pDI)) != NULL) { // read in the data ReadData(pDIDKeyboard, (void*)KeyStateBuffer, 256); } #define KeyState(x) ((KeyStateBuffer[x] & 0x80) ? TRUE : FALSE) if(KeyState(VK_LEFT) == TRUE) { // Left arrow is being pressed } 鼠标的处理: 鼠标的初始化跟键盘的一样,只不过是将数据格式由键盘改成了鼠标.这里就不再重复那些代码了. 处理鼠标时需要调用DirectInputDevice8::GetDeviceState()函数,该函数填充了一个DIMOUSESTATE结构体,它里面包含了关于鼠标的信息: typedef struct { LONG lX; // Relative change in X coordinate LONG lY; // Relative change in Y coordinate LONG lZ; // Relative change in Z coordinate BYTE rgbButtons[4]; // Button pressed flags } DIMOUSESTATE, *LPDIMOUSESTATE; 注意,这里的坐标值是相对的,而我们要想得到绝对位置就必须维护两个全局变量来保存绝对位置: IDirectInputDevice8 *pDIDMouse; // The mouse coordinates long g_MouseXPos = 0, g_MouseYPos = 0; // The data buffer to store the mouse state DIMOUSESTATE MouseState; if((pDIDMouse = InitMouse(g_hWnd, g_pDI)) != NULL) { // read in the data ReadData(pDIDMouse, (void*)MouseState, sizeof(DIMOUSESTATE)); // update the absolute coordinates g_MouseXPos += MouseState.lX; g_MouseYPos += MouseState.lY; } #define MouseButtonState(x) ((MouseState.rgbButtons[x] & \ 0x80) ? TRUE : FALSE)
小结:在这段时间里我们学习了如何更快的处理输入,为以后做好游戏打好坚实的基础.
上一页 [1] [2]
 【责编:Luzi】
|