Unity Input System中如何验证HID输入CRC以阻止无效动作触发?
问题描述
我在使用Unity新Input System读取自定义HID设备的响应,现有两套逻辑:
- 通过各控件的
ReadValue()读取HID数据并存入结构体,供游戏内其他系统读取; - 用
InputAction响应特定HID动作(如按钮按下)。
HID数据包含CRC校验值,必须在将值传入结构体及触发事件前完成校验。请问应在何处验证CRC值,才能阻止无效的Actions被触发?
补充代码
SensorResponse.cs(定义数据帧)
[StructLayout(LayoutKind.Explicit, Size = 0x28)] public struct SensorResponse : IInputStateTypeInfo { public FourCC format => new FourCC('H', 'I', 'D', ' ' ); [FieldOffset(0x00)] public byte zeroTmp; //0x00 [InputControl(name = "startFrame", layout = "Integer", format = "BYTE")] [FieldOffset(0x01)] public byte startFrame; //0x00 [FieldOffset(0x02)] [InputControl(name = "command", layout = "Integer", format = "BYTE", displayName = "command")] public byte command; // ... 省略其他字段 [FieldOffset(0x18)] [InputControl(name = "Switches", layout = "Integer", format = "SHRT", displayName = "Switches")] // ... 省略位绑定 [InputControl(name = "cArmM1", layout = "Button", bit = 2, displayName = "cArmM1")] [InputControl(name = "cArmM2", layout = "Button", bit = 3, displayName = "cArmM2")] // ... 省略其他按钮绑定 public Switches switches; }
ControllerDevice.cs(定义设备)
[InputControlLayout(stateType = typeof(SensorResponse))] public class ControllerDevice : InputDevice { // ... 省略其他控件定义 public ButtonControl cArmM1 { get; private set; } public ButtonControl cArmM2 { get; private set; } // ... 省略其他控件定义 protected override void FinishSetup() { base.FinishSetup(); cArmM1 = GetChildControl<ButtonControl>("cArmM1"); cArmM2 = GetChildControl<ButtonControl>("cArmM2"); // ... 省略其他控件初始化 } }
ControlScript.cs(定义动作)
public class ControlScript : MonoBehaviour { void Start() { InputAction M1Btn = new InputAction(binding: "<ControllerDevice>/cArmM1", interactions: "hold(duration=1)"); InputAction M2Btn = new InputAction(binding: "<ControllerDevice>/cArmM2", interactions: "hold(duration=1)"); M1Btn.canceled += (e) => { RecallMemory(Switches.cArmM1); }; // 兼顾点击和长按的写法 M1Btn.performed += (e) => { SetMemory(Switches.cArmM1); }; M2Btn.canceled += (e) => { RecallMemory(Switches.cArmM2); }; M2Btn.performed += (e) => { SetMemory(Switches.cArmM2); }; M1Btn.Enable(); M2Btn.Enable(); } }
读取响应数据的脚本(Actions.cs)
public class Actions : MonoBehaviour { void Update() { FillSensorResponse(); } private void FillSensorResponse() { if (ControllerDevice.current == null) return; var sensorResponse = new SensorResponse(); sensorResponse.command = (byte)ControllerDevice.current.command.ReadValue(); sensorResponse.commRev = (byte)ControllerDevice.current.commRev.ReadValue(); // ... 省略其他字段赋值 sensorResponse.switches = (Switches)ControllerDevice.current.Switches.ReadValue(); if (Crc8.Verify(sensorResponse)) { // 一些预处理 // ... // 给其他脚本提供SensorResponse数据 Simulation.SensorResponse = sensorResponse; } else Debug.Log($"CRC invalid!"); } }
解决方案
你当前的校验逻辑在Actions.cs的Update里,但这时候Input System已经更新了设备状态,绑定的InputAction已经可能触发了,无效数据已经触发动作后再校验就晚了。要从根源截断无效数据,需要在设备状态被更新到Input System的阶段做CRC校验,具体实现步骤如下:
1. 修改自定义设备的状态更新逻辑
Unity的自定义InputDevice会通过UpdateState方法接收HID原始数据,我们需要在这里先做CRC校验,只有校验通过时才更新设备状态:
[InputControlLayout(stateType = typeof(SensorResponse))] public class ControllerDevice : InputDevice { // ... 保留原有控件定义和FinishSetup逻辑 // 重写UpdateState方法,处理原始HID数据的CRC校验 public override void UpdateState(void* statePtr, void* updateBufferPtr, int updateBufferSize) { // 把原始更新数据转换为SensorResponse结构体 var incomingData = new SensorResponse(); Buffer.MemoryCopy(updateBufferPtr, &incomingData, sizeof(SensorResponse), sizeof(SensorResponse)); // 先做CRC校验 if (!Crc8.Verify(incomingData)) { Debug.LogWarning("Invalid HID data: CRC check failed"); // 校验失败,不更新设备状态,直接返回 return; } // 校验通过,正常更新设备状态到Input System base.UpdateState(statePtr, updateBufferPtr, updateBufferSize); } }
2. 简化Actions.cs的逻辑
现在设备状态只有在校验通过后才会更新,所以Actions.cs里不需要再做CRC校验,直接读取可信的设备状态即可:
public class Actions : MonoBehaviour { void Update() { FillSensorResponse(); } private void FillSensorResponse() { if (ControllerDevice.current == null) return; // 直接从设备读取已校验过的状态 var sensorResponse = ControllerDevice.current.currentStatePtr.ReadValue<SensorResponse>(); // 预处理和传递数据 // ... Simulation.SensorResponse = sensorResponse; } }
3. 为什么这能阻止无效Action触发
Input System的InputAction是基于设备状态的变化来触发的:
- 当CRC校验失败时,设备状态不会被更新,控件值不会变化,绑定的
performed/canceled事件自然不会触发; - 只有校验通过的有效数据才会更新设备状态,进而触发对应的Action事件。
额外注意点
- 如果你的HID设备是通过
HIDInputDevice扩展的,确保在接收原始报告的环节就插入校验逻辑; - 若需要处理校验失败时的降级逻辑(比如保持上一次的有效状态),可以在
UpdateState里把上一次的有效状态拷贝到当前状态指针,而不是直接返回。
内容的提问来源于stack exchange,提问作者szamil
相关产品推荐
相关产品推荐

