首页 | 互联网 | IT动态 | 网络设备 | 服务器 | IDC | 安全 | Cisco | Windows | Linux | Java | .Net | Oracle | CIW | 华为 | 专题
IT技术 | 网页设计 | 平面设计 | 电子书下载 | 教学视频 | 方案 | 数字网校 | 直播室 | 虚拟考场 | 面授培训 | 搜索 | 博客 | 沙龙 | 论坛
 您现在的位置: 中国IT实验室 >> 游戏开发 >> 游戏策划 >> 文章正文
游戏中输入的处理
来源:中国IT实验室收集整理 作者:佚名 时间:2007-9-30


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】

中国IT教育热线咨询

相关文章
游戏设计时需要问自己的三问题
浅谈游戏企划-新手入门篇
暴雪称霸游戏业界的六大秘密绝招
游戏中打怪行为的人类学分析
独家专访网易游戏策划是怎样炼成的
推荐文章
中国IT教育热线咨询

 精彩友情推荐
·神州数码交换机
·神州数码交换机价格
·神州数码网络交换机
·netgear交换机
·网件交换机
·IDC资讯大全
·机房品质万里行
·IDC托管必备知识
·全国IDC报价
·网站推广优化
 基础入门  开发文档
 最新推荐
  多数的Windows程序都需要Windows.h和Windowsx.h这两个头文件,要确保使用它们。当然,你还需要其它......
游戏引擎演化史
在Windows上安装OGRE的方法
关于滤镜遮罩概念,Sobel 遮罩
游戏开发新手入门之Windows编程
游戏开发新手入门之位图化图形
教你实现卡通渲染的另类勾边方法
游戏设计大师谈如何成为一名游戏设
Visual C#编写 3D游戏框架示例
真正的 Java 学习从入门到精通
游戏开发经验——游戏开发的基本常
  针对于移动新出台的政策,需要尽快地把我们公司的游戏对应到不同的手机平台,这是针对市场策略的有利调整............
载入位图文件到DirectDraw
Archer Game Suite 是什么?
浅谈游戏企划-新手入门篇
暴雪称霸游戏业界的六大秘密绝招
骨骼动画及示例Skinned Mesh的解析
游戏五要素模型假设
Quake-III代码里神奇的浮点开方函数
基于Dialogue的MFC程序调用DirectX
关于Kjava手机平台移植可行性报告
网络游戏的数据传输处理和防火墙穿
  培训中心