最近做毕业设计,需要用unity实现场景内的反馈到手柄的功能,授之以渔不如授之以鱼,耐心看完这片文章相信你可以diy自己的通用接口手柄。由于usb协议太过于复杂,C#也没有好用的HID设备类,找遍了内网外网的资料,没多少人经行研究。C#的实现方法倒是有例如CyUSB,USBlib.NET等等资料和接口我全部都找了并且试了个遍,要么用不了,要么运行闪退并且用不了。有的代码比较简洁的接口却不能用于Win10,都不能在unity里用,心态崩了。

最后想一下还得用unity里自带的接口

之前看到有一个UP主写HID通讯的文章——Unity中向HID设备发送数据 - 哔哩哔哩

这兄弟是我查阅上千资料中唯一一个实现了该功能的大佬,他还把研究成果做成插件上传到了unity的asset store,但是由于他的文章教程不够详尽,而且每个人DIY的HID手柄不一样,我想想吧还是的自己研究(不是因为穷买不起大佬的插件)然后就开始了input system的研究。根据文章主题吧,我先说unity的部分:

unity部分

在开始之前先导入input system包。

根据大佬的文章和input system的api手册(手册链接::Input System | Input System | 1.2.0(想了解更多输入系统的可以去学习))

我知道了发送数据用的函数是ExecuteCommand<TCommand>(ref TCommand),

所以这两句代码便是用于HID数据传递的:

var c = MyControlCommand.Create(0x04, 0x00);

var res= HID.all[3].ExecuteCommand(ref c);

print("res:" + res);

其中先说说MyControlCommand,查了很多资料,最后我借鉴了B站这个大佬的

//HID设备配置
    [StructLayout(LayoutKind.Explicit, Size = kSize)]
    internal struct MyControlCommand : IInputDeviceCommandInfo
    {
        public static FourCC Type { get { return new FourCC('H', 'I', 'D', 'O'); } }
        internal const int id = 0;
        internal const int kSize = InputDeviceCommand.BaseCommandSize + 2;
        [FieldOffset(0)]
        public InputDeviceCommand baseCommand;
        [FieldOffset(InputDeviceCommand.BaseCommandSize)]
        public byte reportId;
        [FieldOffset(InputDeviceCommand.BaseCommandSize + 1)]
        public byte DataType;
        [FieldOffset(InputDeviceCommand.BaseCommandSize + 2)]
        public byte Data;
        //Add data here
        public FourCC typeStatic
        {
            get { return Type; }
        }
        public static MyControlCommand Create(byte dataType, byte data)
        {
            return new MyControlCommand
            {
                baseCommand = new InputDeviceCommand(Type, kSize),
                reportId = id,
                DataType = dataType,
                Data = data
            };
        }
    }



/* 可以复制过去粘贴在自己创建的Mono派生类里面去用, 按提示引入命名空间即可,但是其中你要注意第六行;这里的2要HID与接收字节相对应, 我只接收一个字节,加上ID是2;这里单片机部分还会详细说,记 住这个字节数大了小了都发送不了一定要注意。

internal const int kSize = InputDeviceCommand.BaseCommandSize + 2; */

然后再说说比较重要的一点,也是我一直踩坑的,就是这里的HID.all[3];

其实就是我的手柄啦,其实它与Gamepad,Joystick一样是一个InputDevice的派生类,你用前两个类也是可以的,分别写为




Gamepad.current 或者 Joystick.current

这里的主要问题是怎么知道current就是我的手柄了呢?

这边打开这个分析工具

android 手柄 sendReport 安卓hid手柄_c#


编辑切换为居中

添加图片注释,不超过 140 字(可选)

你看看自己的手柄是否出现在这里被支持的设备了

android 手柄 sendReport 安卓hid手柄_单片机_02


编辑切换为居中

添加图片注释,不超过 140 字(可选)

如果不支持,那很遗憾,这里就通讯不了了。没错我的就是不支持,我尝试用input system里的AddDevice()方法去添加我的HID设备但是无法,它一直找不到这个名字的HID设备。要添加组件的HID设备看我的这篇文章吧:

unity如何添加自定义HID设备,自己开发的手柄如何支持unity。 - 知乎

那我们怎样才能让我的设备支持Input system呢?下面让我们来看看单片机部分:

单片机部分

我们要把单片机作为通用的可收发数据的HID设备这里有一篇比较好的博客参照:STM32 使用Cubemx 建一个USB(HID)设备下位机,实现数据收发 - 白菜没我白 

其中因为我们要做的是HID手柄就需要配置手柄的报告描述符,这是官网的教程:HID报告描述符教程 手把手教你编写HID报告描述符 - USB中文网

描述符配置不按unity的标准unity就不识别,就是前面说的它显示不支持此设备。那么unity支持的标准是什么呢?我也不知道,自己配了几百遍unity就是不认这个小三,显示它是不支持的设备。。。

然后我没办法用了USB的官方例程描述符

android 手柄 sendReport 安卓hid手柄_单片机_03


编辑切换为居中

添加图片注释,不超过 140 字(可选)

unity瞬间就识别了,挺好的,但是问题来了报告描述符里只有输入,没有输出,也就是不下发,这个简单,我在遥感中间加了一句输出的代码:

android 手柄 sendReport 安卓hid手柄_描述符_04


编辑切换为居中

添加图片注释,不超过 140 字(可选)

然后就即识别也可接收数据了。。。。。

下位机关键代码更改部分(配置部分上边给了博客的):

描述符

android 手柄 sendReport 安卓hid手柄_stm32_05


编辑切换为居中

添加图片注释,不超过 140 字(可选)

android 手柄 sendReport 安卓hid手柄_unity_06


编辑切换为居中

Hid的部分配置代码

差不多就是这样了,查资料的过程中真的是累死了,本生自己又菜,感觉很无助,还花钱下了一些资料,结果没啥用要的还不少,那时就在想要是我实现了这个功能一定把它免费分享到网上,,,如果你也在做类似的功能希望这篇文章能对你有所帮助。有什么问题的话可以留言啊咱可以一起讨论。