开发平台:VC6.0
基于MFC
玩游戏的时候,经常遇到想用手柄但游戏本身不支持的事儿。这样的情况下,我们就要使用软件,将手柄映射为键盘,以达到目的。网上有几款比较流行的优秀的此类软件,比如JoyToKey,Xpr等。这些软件方便实用,都很成熟。不过它们都有一点小瑕疵,就是无法将一键映射为多键。听这句话可能有些朋友迷糊了,JoyToKey不就能吗,你怎么说不能吗。如果您这样想,那么您肯定是误会我的意思了。JoyToKey提供的一键映多键功能是按下手柄的一个键,相当于同时按下了键盘上的多个键,在这里,我所说的映多键,是指在手柄上设定一个辅助键。按住和松开辅助键时,按其他键会分别映射成键盘上的不同键。类似于键盘的shift的效果,但注意,不是直接映射为shift,对大多数游戏而言,字母大小写识别为一样。
前述结束,那么我们就开始说正题。
一个手柄/键盘映射程序,无外乎就四部分:一、界面;二、接收;三、处理;四、输出。
界面就不多说了。
接收,就是接收手柄的输出。这部分有多种方法,比如windows API和DirectX,这里我们选前者。
处理,就是将接收到的数据映射为输出数据。
输出,就是向操作系统发送假的键盘事件,从而完成映射过程。
接收部分:
那么,我们先来进行知识的准备。为了完成接收部分,我们需要了解和手柄相关的windows API。其中常用的较重要的函数如下:
joyGetDevCaps 查询指定的游戏杆设备以确定其性能
joyGetNumDevs 返回系统支持的游戏杆设备的数量
joyGetPos 查询指定的游戏杆设备的位置和活动性
joyGetPosEx 查询一个游戏杆设备的位置和它的按扭状态
joyGetThreshold 查询指定的游戏杆设备的当前移动阈值
joyReleaseCapture 释放由JoySetCapture函数设置的在指定游戏杆设备上的捕获
joySetCapture 发送一个游戏杆消息到指定的窗口
joySetThreshold 设置指定的游戏杆设备的移动阈值
要使用这几个API,需要连接winmm.lib,包含mmsystem.h头文件。如下图所示:
如果仅制作基本的映射功能,那么我们并不需要用到全部的函数。主要使用的是这个:
MMRESULT joyGetPosEx(UINT uJoyID, LPJOYINFOEX pji)
这个函数可以主动取得游戏杆信息。
参数uJoyID是指手柄的ID,对于单手柄编程,填写JOYSTICKID1即可。
参数pji是一个指向JOYINFOEX型结构体的指针。JOYINFOEX的定义如下:
typedef struct joyinfoex_tag {
DWORD dwSize; /* size of structure */
DWORD dwFlags; /* flags to indicate what to return */
DWORD dwXpos; /* x position */
DWORD dwYpos; /* y position */
DWORD dwZpos; /* z position */
DWORD dwRpos; /* rudder/4th axis position */
DWORD dwUpos; /* 5th axis position */
DWORD dwVpos; /* 6th axis position */
DWORD dwButtons; /* button states */
DWORD dwButtonNumber; /* current button number pressed */
DWORD dwPOV; /* point of view state */
DWORD dwReserved1;
DWORD dwReserved2;
} JOYINFOEX, *PJOYINFOEX, NEAR *NPJOYINFOEX, FAR *LPJOYINFOEX;
它包含了指定手柄当前的状态信息。我们主要用到的是其中的dwFlags,dwXpos,dwYpos以及dwButtons。这四个成员依次表示:获取状态,十字键X轴当前状态,十字键Y轴当前状态,功能键当前状态。
我们在使用joyGetPosEx获得手柄状态前,先要把dwFlags设置为JOY_RETURNALL,即返回全部按键状态,这样才能同时获得十字键和功能键的信息。
dwXpos和dwYpos的值分别代表了X轴和Y轴当前的状态。对于使用windows默认的自带手柄驱动的,按键情况和对应值如下:
值 | 上下 | 左右 |
0x0000 | 上 | 左 |
0x7eff | 不按 | 不按 |
0xffff | 下 | 右 |
0x7fff | 同时 | 同时 |
注意:如果安装了手柄驱动盘,值会随驱动不同而改变,请自行测定
而dwButtons的每一位对应手柄的一个功能键状态,0表示抬起状态,1表示按下状态。(注意,是状态,不是动作)对应关系是从低位到高位对应功能键1至手柄功能键最高数。
joyGetPosEx函数的返回值可能是以下几种:
/* joystick error return values */
#define JOYERR_NOERROR (0) /* no error */
#define JOYERR_PARMS (JOYERR_BASE+5) /* bad parameters */
#define JOYERR_NOCANDO (JOYERR_BASE+6) /*request not completed */
#define JOYERR_UNPLUGGED (JOYERR_BASE+7) /*joystick is unplugged */
根据它,我们可以判断电脑当前否有手柄连接。
输出部分:
键盘模拟部们,我们只需要使用一个很简单的函数keyevent
void keybd_event(BYTE bVk,BYTE bScan,DWORD dwFlags,DWORD dwExtralnfo);
参数:
bVk:定义一个虚拟键码。键码值必须在1~254之间。
bScan:定义该键的硬件扫描码。
dwFlags:定义函数操作的名个方面的一个标志位集。应用程序可使用如下一些预定义常数的组合设置标志位。
KEYEVENTF_EXETENDEDKEY:若指定该值,则扫描码前一个值为OXEO(224)的前缀字节。
KEYEVENTF_KEYUP:若指定该值,该键将被释放;若未指定该值,该键交被接下。
(其实就是一个是0一个是2,0表示键按下,1表示键弹起)
dwExtralnfo:定义与击键相关的附加的32位值。
表面上看着好像很点乱,其实没那么复杂。
给一个使用实例:
#define KEY 要模拟的键的ASCII码
keybd_event(KEY,MapVirtualKey(KEY, 0),0,0);
注意,这里使用MapVirtualKey()函数是有必要的。对于很多游戏,单纯的使用keybd_event(KEY,0,0,0)的模式是不能被正确识别的,换句话说,游戏程序会把那些伪造的键盘信息过滤掉或根本不接受。但使用MapVirtualKey()后,大部分游戏就会识别到我们发送的键盘信息了。
未完待续
下篇内容:编程实战