You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity Input System中如何验证HID输入CRC以阻止无效动作触发?

问题描述

我在使用Unity新Input System读取自定义HID设备的响应,现有两套逻辑:

  1. 通过各控件的ReadValue()读取HID数据并存入结构体,供游戏内其他系统读取;
  2. 用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 11:20:37