Joystick
分类:Others
操作杆(包括不同厂商的操作杆、方向盘、阀门、手柄等)只要遵循主流的协议都可实现与用户应用程序的交互。其交互方式大致分为两种:
1.轮询式–DirectX读取
通过DirectX中的DirectInput识别操作杆外设,调用Windows系统自带的dinput8.lib
(Winmm.lib
亦可)库中的LPJOYINFO
实现对操作杆状态的查询。具体实现示例如下:
#include <stdio.h>
#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "Winmm.lib")
int main()
{
UINT DeviceNumbers = joyGetNumDevs();
printf("%d\n", DeviceNumbers); // always be 16 ?
while (true)
{
UINT JoystickID = 0;
LPJOYINFO pJoystickInfo = new joyinfo_tag;
joyGetPos(JoystickID, pJoystickInfo);
printf("X: %d\n", pJoystickInfo->wXpos); // value from 0 to 65535 (2^6)
printf("Z: %d\n", pJoystickInfo->wYpos);
printf("Y: %d\n", pJoystickInfo->wZpos);
printf("Buttons: %d\n", pJoystickInfo->wButtons);
sleep(1000);
}
return 0;
}
每个操作杆设备轴的变化最多支持6个维度,按键最多支持32个键,基本能够覆盖全面了。
2.响应式–映射为键盘事件
TARGET GUI
提供了图形化和脚本编写两种方式将操作杆设备的输入(可以区分Pulse、Hold、Press、Release)转换为不同的键盘事件(可以为复杂的组合键方式),随后在自己的应用程序中可以通过Qt响应键盘事件的方式获取操作杆传入信息,进而实现自己的应用场景。TARGET GUI
同时还提供了事件测试器,方便测试定义的键盘事件键值和精度是否正确。实现示例如下:
bool JoyStickInputEventManager::Filter( QObject * receiver, QEvent * event )
{
if (event->type() == QEvent::KeyPress)
{
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
bool IsThrottleEvent = false;
if (keyEvent->modifiers() == (Qt::ControlModifier | Qt::ShiftModifier))
{
IsThrottleEvent = true;
if (keyEvent->key() == Qt::Key_1)
{
emit Power_On_RDRNRM(true);
}
else
{
IsThrottleEvent = false;
}
return IsThrottleEvent;
}
if (keyEvent->modifiers() == (Qt::ControlModifier | Qt::AltModifier))
{
IsThrottleEvent = true;
if (keyEvent->key() == Qt::Key_Up)
{
emit Throttle_Control(1);
}
else if (keyEvent->key() == Qt::Key_Down)
{
emit Throttle_Control(-1);
}
else
{
IsThrottleEvent = false;
}
return IsThrottleEvent;
}
}
return false;
}
推荐DirectX读取的方式,这也是游戏引擎主流的使用方式,其中应该考虑了读取速度和不与键盘操作冲突的场景,一般映射为键盘事件是为了接入已有的系统内,通过操作杆间接控制键盘。
因为我在这里遇到了一个大坑…
该项目用的是Thrustmaster的摇杆和阀门因为需要精细化的操作识别,所以通过TARGET GUI
定义了很多组合键,其中包括Ctrl
、Alt
、Shift
和F*
(可以从F1到F30,虽然普通键盘上没有列出这些功能键)。
Qt绘制过程中会出现paintEvent
刷新不干净的bug,几经debug认为应该是操作杆定义的键盘事件过于复杂,可能和Qt保留键冲突导致Qt事件队列异常从而无法更新QWidget中绘制的图形,但是其他的绘制可以正常更新,只是会界面始终会保留一个没有刷新掉的图形。
后将TARGET GUI
中定义的组合键去去掉,程序改为DirectInput轮询式读取后搞定。注:这种方式同样需要运行TARGET GUI,但无需编辑按键脚本。