知乎链接: https://zhuanlan.zhihu.com/p/55437022

Github: https://github.com/Lynnvon/RawInputPluginForUE4

  • 第一章主要解决了HIDStatusBufferTooSmall的错误和增加axis与button数量的问题,这篇文章主要解决原生插件不支持多设备的问题。
  • 通过阅读原生插件源代码,发现原插件在注册HID设备时,只注册找到的第一个设备,如果你只有一个USB 设备,那没有任何问题,但当你的设备为2个甚至更多时,原插件是没有注册后边的设备的,这导致了当你的设备多于1个时,多个设备的数据相互混合了(虽然没有注册设备,但获取获取并没有区分是否注册,而是获取所有设备的数据),完全无法使用。
  • 那思路就很简单了,只需要在RawInputWindows.cpp的RegisterInputDevice方法内修改为注册全部设备,然后在ProcessMessage方法内进行DeviceName比对,就可以区分开不同设备的数据。
  • RegisterInputDevice方法内:
 1 QueryConnectedDevices();
 2         // If this doesn't already exist in our internal list add it
 3         DeviceHandle = FindRegisteredDeviceHandle(DeviceData);
 4         if (DeviceHandle == INDEX_NONE)
 5         {
 6             /*
 7             DeviceHandle = GetNextInputHandle();
 8 
 9             //只在此处设置RegisteredDeviceList的值
10             RegisteredDeviceList.Add(DeviceHandle, DeviceData);
11             // Now see if the device is connected
12 */
13             bool bWasConnected = false;
14             for (const FConnectedDeviceInfo &ConnectedDeviceInfo : ConnectedDeviceInfoList)
15             {
16 
17                 if (CompareDeviceInfo(ConnectedDeviceInfo.RIDDeviceInfo, DeviceData))
18                 {
19 
20                     DeviceHandle = GetNextInputHandle();
21 
22                     //只在此处设置RegisteredDeviceList的值
23                     RegisteredDeviceList.Add(DeviceHandle, DeviceData);
24                     // Now see if the device is connected
25                     bWasConnected = false;
26 
27                     FRawWindowsDeviceEntry &RegisteredDeviceInfo = RegisteredDeviceList[DeviceHandle];
28                     RegisteredDeviceInfo.bIsConnected = true;
29                     RegisteredDeviceInfo.DeviceData.DeviceName = ConnectedDeviceInfo.DeviceName;
30 
31                     if (DeviceData.DeviceType == RIM_TYPEHID)
32                     {
33                         RegisteredDeviceInfo.DeviceData.VendorID = ConnectedDeviceInfo.RIDDeviceInfo.hid.dwVendorId;
34                         RegisteredDeviceInfo.DeviceData.ProductID = ConnectedDeviceInfo.RIDDeviceInfo.hid.dwProductId;
35                     }
36 
37                     UE_LOG(LogRawInputWindows, Log, TEXT("VenderID:%x ProductID:%x"), RegisteredDeviceInfo.DeviceData.VendorID, RegisteredDeviceInfo.DeviceData.ProductID);
38 
39                     bWasConnected = true;
40 
41                     //break;
42                     if (bWasConnected)
43                     {
44                         SetupBindings(DeviceHandle, true);
45 
46                         UE_LOG(LogRawInputWindows, Log, TEXT("Device was registered succesfully and is connected (Usage:%d UsagePage:%d)"), DeviceData.Usage, DeviceData.UsagePage);
47                     }
48                     else
49                     {
50                         DeviceHandle = INDEX_NONE;
51                         UE_LOG(LogRawInputWindows, Warning, TEXT("Device was registered succesfully but not connected (Usage:%d UsagePage:%d)"), DeviceData.Usage, DeviceData.UsagePage);
52                     }
53                 }
54             }
55         }
View Code
  • ProcessMessage方法内通过DeviceName进行多设备的区分:
 1 if (RawInputDataBuffer->header.dwType == RIM_TYPEHID)
 2                     {
 3                         // First we need to get the pre-parsed data
 4                         uint32 BufferSize;
 5 
 6                         uint32 NameLen = 0;
 7 
 8                         //Force the use of ANSI versions of these calls
 9                         //先获取当前hid设备的名称,然后做比对,避免多个设备之间互相干扰
10                         if (GetRawInputDeviceInfoA(RawInputDataBuffer->header.hDevice, RIDI_DEVICENAME, nullptr, &NameLen) != RAW_INPUT_ERROR)
11                         {
12                             DeviceNameBuffer.SetNumUninitialized(NameLen + 1, false);
13 
14                             if (GetRawInputDeviceInfoA(RawInputDataBuffer->header.hDevice, RIDI_DEVICENAME, DeviceNameBuffer.GetData(), &NameLen) != RAW_INPUT_ERROR)
15                             {
16                                 DeviceNameBuffer[NameLen] = 0;
17                                 FString DeviceName = ANSI_TO_TCHAR(DeviceNameBuffer.GetData());
18                                 DeviceName.ReplaceInline(TEXT("#"), TEXT("\\"), ESearchCase::CaseSensitive);
19 
20                                 if (::GetRawInputDeviceInfo(RawInputDataBuffer->header.hDevice, RIDI_PREPARSEDDATA, nullptr, &BufferSize) != RAW_INPUT_ERROR)
21                                 {
22                                     PreParsedData.SetNumUninitialized(BufferSize + 1, false);
23 
24                                     if (::GetRawInputDeviceInfo(RawInputDataBuffer->header.hDevice, RIDI_PREPARSEDDATA, PreParsedData.GetData(), &BufferSize) != RAW_INPUT_ERROR)
25                                     {
26                                         HIDP_CAPS Caps;
27 
28                                         // now that we have the PP data we need to get the caps, check those and see if this is a device we registered and if it is store it so we can send it
29                                         if (DLLPointers.HidP_GetCaps((PHIDP_PREPARSED_DATA)PreParsedData.GetData(), &Caps) == HIDP_STATUS_SUCCESS)
30                                         {
31 
32                                             FRawInputRegisteredDevice DeviceData(RawInputDataBuffer->header.dwType, Caps.Usage, Caps.UsagePage);
33 
34                                             // Win32 doesn't correctly report the device ID, so at least for now just trust it is from the device we want
35                                             //if (DeviceData == EachEntry.DeviceData)
36                                             //很重要,需要先比对设备,再解析数据,否则多个hid设备时会数据混乱
37                                             if(DeviceName == EachEntry.DeviceData.DeviceName)
38                                             {
39                                                 bIsRegisteredDevice = true;
40                                                 ParseInputData(DeviceEntryPair.Key, RawInputDataBuffer, (PHIDP_PREPARSED_DATA)PreParsedData.GetData(), Caps);
41                                             }
42                                         }
43                                     }
44                                 }
45                             }
46                         }
47 
48                     }
View Code
  • 其他细节请参考github上的代码(应该没什么了)
  • 因为要支持两个摇杆,所有我将aixs增加至了16个,button增加至了40个,后期如果需要,也可以修改为动态映射和动态识别设备,目前识别设备只能在启动ue4时识别。
  • 以上就是所有内容,祝大家工作愉快,Have Fun!!!