DirectInput

在看了一大篇的2D图形基础(没有写blog,因为太难用语言表达了,需要绘图)之后,感觉游戏设计中真是与数学的关系太紧密了。简直就是在摆弄数学,呈现在我们面前的画面都掌握在数学之中。现在回过神来再继续看DirectX的组成部分——DirectInput。
 
DirectInput是DirectX中对于输入设备这一块硬件的封装,提供了快速的对于输入设备的查询。要使用DirectInput来获得输入基本步骤如下(在知道了DirectDraw以后再看DirectInput就觉得DirectX各个部件总有那么点相似处):
  1. 创建IDirectInput8接口,它提供对整个DirectInput的支持。
    LPDIRECTINPUT8 lpdi;
    DirectInput8Create(main_instance, DIRECTINPUT_VERSION, IID_IDirectInput8, (LPVOID*)&lpdi, NULL);
  2. 查询设备的GUID。这一步基本上只对于JoyStick之类需要调用,特殊的如果有多个鼠标也需要调用,一般对于键盘和鼠标不需要,因为有全局定义的默认键盘鼠标的GUID:GUID_SysKeyboard和GUID_SysMouse(需要#define INITGUID, #include <OBJBASE.H>或者连上DXGUID.LIB)。
  3. 调用CreateDevice方法来创建一个设备。
    IDIRECTINPUTDEVICE8 lpdikey;
    lpdi->CreateDevice(GUID_SysKeyboard, &lpdikey, NULL);
  4. 对于你创建的设备设置协作等级。
    lpdikey->SetCooperativeLevel(main_window_handle, DISCL_BACKGROUND | DISCL_NONEXCLUSIVE);这两个是常用的属性,表示程序在后台可以使用设备并且非独占使用。如果有另外一个独占/前台运行的程序,那么这个调用就会失败。
  5. 设置数据格式。这里设置了什么数据格式,在最后查询的时候就用相应的数据结构。对于键盘,数据格式为256个元素的UCHAR的数组,对于其他设备则有相应的数据结构(DIMOUSESTATE, DIJOYSTATE, DIJOYSTATE2)
    lpdikey->SetDataFormat(&c_dfDIKeyboard);
    c_dfDIKeyboard, c_dfDIMouse, c_dfDIJoystick, c_dfDIJoystick2为预定义的DIDATAFORMAT结构。
  6. 设置设备的性能。这个步骤一般不用,即使对于复杂的JoyStick也只是需要设置其中的很少部分。因为这个调用相当可怕。
  7. 获得设备。
    lpdikey->Acquire();
  8. 轮询设备。
    lpdikey->Poll();
  9. 从设备获得数据。这是我们真正需要的。以上的一切都是为了这个。
    UCHAR keystate[256];
    lpdikey->GetDeviceState(sizeof(keystate), (LPVOID)keystate);
    然后就可以对每个数组元素进行查询,来确定按键的情况。(&0x80 != 0 表示按下)DirectInput中对于虚拟键的定义以DIK_开头。
  10. 最后,说一下释放设备的过程。
    lpdikey->Unacquire();
    lpdikey->Release();
    lpdi->Release();

以上这些步骤就是对于最简单的设备——键盘的使用。不过其他的设备和他也并没有差开多少。只是Joystick在开始的时候需要用回调函数和枚举的方法来获取GUID,然后就可以顺着下面的步骤做下来。

下面还有另外一个问题:在DirectInput工作过程中输入设备很可能发生各种各样的意外。比如你不小心拉掉了手柄线。这个时候你可以检测并且重新获取设备。利用GetDeviceState函数的返回值我们可以知道设备的情况。如下是GetDeviceState()可能返回的错误代码:

  1. DIERR_INPUTLOST           设备已经丢失。
  2. DIERR_INVALIDPARAM    函数的参数无效
  3. DIERR_NOTACQUIRED     已经完全失去设备
  4. DIERR_NOTINITIALIZED  设备没有准备好
  5. E_PENDING                       数据还未准备好

所以如果出现了DIERR_INPUTLOST的错误,我们可以尝试重新获取设备。一般来说对于键盘丢失的几率几乎为零,多数时候,你只会丢失Joystick。

下面说一下对于Mouse 和 Joystick的不同之处。

Mouse的数据输入默认的为相对模式,也就是每次读入的为相对于上一次位置数据的相对值(偏移值)。对于DIMOUSESTATE结构中的rgbButtons数组,0,1,2下标分别表示左键,右键,中键。

对于游戏杆,我真是不想说了。因为在CreateDevice前为了得到GUID实在是有太多的要说了。调用枚举函数EnumDevices(),设置回调函数,记录下回调函数中传入的设备GUID和其他一些信息。说起来真怕自己说不清楚,还是直接给出一个例子,这样好理解一些。

其实就是在CreateDevice前多做了一些事:
char joyname[256];
GUID joystickGUID;

lpdi->EnumDevices(DIDEVTYPE_JOYSTICK, DInput_Enum_Joysticks, &joystickGUID, DIEDFL_ATTACHEDONLY);  // 枚举所有Joystick,必须是接上的Joystick,回调函数为DInput_Enum_Joystick。

回调函数的格式为:BOOL CALLBACK DInput_Enum_Joystick(LPCDIDEVICEINSTANCE lpdidi, LPVOID lpParam);

在回调函数中我们可以利用其中的LPCDIDEVCEINSTANCE结构参数中的设备的信息来创建自己需要的记录(主要保存下查询到的GUID,因为有了GUID下面的步骤就都见过了)
Joystick的另外一个特殊的地方就是需要设置一下设备性能SetProperty()。这就牵涉到一个非常复杂的结构DIPROPRANGE,这一块只能参阅DirectX SDK文档了,这里是肯定说不明白的。听说加起来有10页……不过一般情况下只要设置一下轴的范围和DeadZone。

By Lu Jun

80后男,就职于软件行业。习于F*** GFW。人生48%时间陪同电子设备和互联网,美剧迷,高清视频狂热者,游戏菜鸟,长期谷粉,临时果粉,略知摄影。

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.