找回密码
 立即注册
查看: 217|回复: 0

[简易教程] unity如何添加自定义HID设备,自己开发的手柄如何支持unity。

[复制链接]
发表于 2022-5-11 10:33 | 显示全部楼层 |阅读模式
最近在做毕业设计,涉及到HID设备接入unity。
HID设备配置

当你想用单片机自己开发一个HID手柄时,网上可以找到比较多的教程,这里有一个大佬出的stm32的教程
https://www.bilibili.com/video/BV12P4y1G7Qg?spm_id_from=333.1007.top_right_bar_window_custom_collection.content.click
我是用stm32做的,当你成功配置手柄接入windows后,控制面板可以看到你的设备


这是我的设备,我们先打开看看


这里我的报告描述符自定义手柄只有两个轴,就是x和y两个轴
轻轻滑动自定义手柄传感器可以看到两个轴的输入是正常的,到此为止我们的自定义HID手柄配置完成,下面我们看看怎样把它接入unity
unity接入

HID设备已经配置好了,按道理来说我们可以在unity里面直接使用,是的用unity自代的inputManager(old)确实可以使用我们的手柄进行输入,不需要任何配置。
但是有时候许多特定功能需要用到input system来配置我们的手柄进行操作。这时问题就出现了,unity并不认识你自定义的手柄。就像下图


这里如果你配置的手柄HID报告描述符被unity所识别那就不需要其他配置。但是如果你的HID设备不被unity所识别那就需要自己添加为HID设备。
再input system找了好久,这里是一篇添加的教程(之后我称之为官网教程):
https://docs.unity3d.com/Packages/com.unity.inputsystem@1.0/manual/HID.html
由于涉及过多偏底层的开发,这里我和一位热心网友费好大劲才把他做好。这里我尽量用做通俗的语言把过程说清楚,这里由于每个人的HID配置都不一样,所以没有现成的代码可以给。
首先我需要识别我的设备为inputsystem所支持的设备,所以我新建一个类,继承自GamePad(inputsystem里面的输入类)
[InputControlLayout(stateType = typeof(DualShock4HIDInputReport)]
public DualShock4GamepadHID : Gamepad
{
}
这里怎样识别自己的HID设备呢?类上边的这句代码为我们的设备指定了布局
官网给出了相应的模板:
[InputControlLayout(stateType = typeof(DualShock4HIDInputReport)]
#if UNITY_EDITOR
[InitializeOnLoad] // Make sure static constructor is called during startup.
#endif
public DualShock4GamepadHID : Gamepad
{
    static DualShock4GamepadHID()
    {
        // This is one way to match the Device.
        InputSystem.RegisterLayout<DualShock4GamepadHID>(
            new InputDeviceMatcher()
                .WithInterface("HID")
                .WithManufacturer("Sony.+Entertainment")
                .WithProduct("Wireless Controller"));

        // Alternatively, you can also match by PID and VID, which is generally
        // more reliable for HIDs.
        InputSystem.RegisterLayout<DualShock4GamepadHID>(
            matches: new InputDeviceMatcher()
                .WithInterface("HID")
                .WithCapability("vendorId", 0x54C) // Sony Entertainment.
                .WithCapability("productId", 0x9CC)); // Wireless controller.
    }

    // In the Player, to trigger the calling of the static constructor,
    // create an empty method annotated with RuntimeInitializeOnLoadMethod.
    [RuntimeInitializeOnLoadMethod]
    static void Init() {}
}它里面带了两种识别方式一个是产品编号和厂家               
.WithManufacturer("Sony.+Entertainment")                 
.WithProduct("Wireless Controller"));,一个是HID设备的PID和VID
        InputSystem.RegisterLayout<DualShock4GamepadHID>(            
        matches: new InputDeviceMatcher()                 
        .WithInterface("HID")                 
        .WithCapability("vendorId", 0x54C) // Sony Entertainment.               
         .WithCapability("productId", 0x9CC)); // Wireless controller.更改其中的数据为自己的手柄配置,如果觉得麻烦就只保留PID和VID的识别
把上边的东西放在自己的代码里面你已经可以成功把你的HID设备添加为inputsystem支持的设备了,对,没错,现在你已经可以用inputsystem向HID设备发送信息了,具体操作看我这篇文章
https://zhuanlan.zhihu.com/p/498254000
那么可以输入了吗??这时我们打开input debug窗口


发现不管怎么输入,这里数据依然是0。
这是因我们还没有写自己的手柄布局,简单来说就是接收报文的数据结构我们还没有告诉unity它不知道怎么显示就给你显示了gamePad类默认的手柄布局,之前的自定义的HID类上边的这行代码便是为我们指定我们手柄的布局的:
[InputControlLayout(stateType =typeof(DualShock4HIDInputReport)]//就是这一行
                                                                //DualShock4HIDInputReport是我们的自定义布局

public DualShock4GamepadHID : Gamepad {}下面让我们来写一下自己的手柄布局吧:
我以自己举例子我只有X和Y两个轴,报告描述的数据结构是:
struct HIDReport
{
    byte reportId;             // #0
    byte leftStickX;           // #1
    byte leftStickY;           // #2
}由此我的布局写为:

[StructLayout(LayoutKind.Explicit, Size = 3)]
struct DualShock4HIDInputReport : IInputStateTypeInfo
{
    public FourCC format => new FourCC('H', 'I', 'D');
    [FieldOffset(0)] public byte reportId;
    [InputControl(name = "leftStick", layout = "Stick", format = "VC2B")]
    [InputControl(name = "leftStick/x", offset = 0, format = "BYTE",
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
    [InputControl(name = "leftStick/left", offset = 0, format = "BYTE",
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0,clampMax=0.5,invert")]
    [InputControl(name = "leftStick/right", offset = 0, format = "BYTE",
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0.5,clampMax=1")]
    [InputControl(name = "leftStick/y", offset = 1, format = "BYTE",
        parameters = "invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
    [InputControl(name = "leftStick/up", offset = 1, format = "BYTE",
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0,clampMax=0.5,invert")]
    [InputControl(name = "leftStick/down", offset = 1, format = "BYTE",
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0.5,clampMax=1,invert=false")]
    [FieldOffset(1)] public byte leftStickX;
    [FieldOffset(2)] public byte leftStickY;
}要想写其他布局可以去官网教程看看哪个例子
这次再打开input Debug就可以看到我的数据可以输入进去了:


这里有可能你的布局出现的还不是自己写的。那么有可能是你自己的设备继承鱼GamePad类于是它用了GamePad的默认布局,解决方法也简单你继承自InputDevice类就可以了,这是我的全部实现
using UnityEngine;
using System.Runtime.InteropServices;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Layouts;
using UnityEditor;
//
//我自定义的类
#if UNITY_EDITOR
[InitializeOnLoad] // Make sure static constructor is called during startup.
#endif
[InputControlLayout(stateType = typeof(PingGameJoy_USB_HID_Report), isGenericTypeOfDevice = true)]
public class MyPad : InputDevice
{
    static MyPad()
    {

        InputSystem.RegisterLayout<MyPad>(
            matches: new InputDeviceMatcher()
                .WithInterface("HID")
                .WithCapability("vendorId", 1212) // Sony Entertainment.
                .WithCapability("productId", 424)); // Wireless controller.
    }
    // create an empty method annotated with RuntimeInitializeOnLoadMethod.
    [RuntimeInitializeOnLoadMethod]
    static void Init()
    { }
    public static MyPad current { get; private set; }
    public override void MakeCurrent()
    {
        base.MakeCurrent();
        current = this;
    }
}
//
自定义的布局
//
[StructLayout(LayoutKind.Explicit, Size = 3)]
public struct PingGameJoy_USB_HID_Report : IInputStateTypeInfo
{
    public FourCC format => new FourCC('H', 'I', 'D');
    [FieldOffset(0)] public byte reportId;
    [InputControl(name = "stick", format = "VC2B", layout = "Stick", displayName = "Main Stick")]
    [InputControl(name = "stick/x", defaultState = 127, format = "BYTE",
        offset = 0,
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
    [FieldOffset(1)] public byte x;
    [InputControl(name = "stick/y", defaultState = 127, format = "BYTE",
        offset = 1,
        parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
    // The stick up/down/left/right buttons automatically use the state set up for X
    // and Y but they have their own parameters. Thus we need to also sync them to
    // the parameter settings we need for our BYTE setup.
    // NOTE: This is a shortcoming in the current layout system that cannot yet correctly
    //       merge parameters. Will be fixed in a future version.

    [InputControl(name = "stick/up", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=0,clampMax=1")]
    [InputControl(name = "stick/down", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=-1,clampMax=0,invert")]
    [InputControl(name = "stick/left", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=-1,clampMax=0,invert")]
    [InputControl(name = "stick/right", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=0,clampMax=1")]
    [FieldOffset(2)] public byte y;
}
//
//再写入一个MonoBehaviour类把它挂在物体上吧
//
public class Mycontral : MonoBehaviour
{
    //public byte[] Vs;

    //InputDevice m_Gamepad= ;
    MyPad m_Gamepad;
    void Start()
    {
        m_Gamepad = MyPad.current;
    }
}这样自己的设备就是标准的输入设备啦。可以输入也可以作交互反馈,反馈功能见我另一篇文章:
https://zhuanlan.zhihu.com/p/498254000
希望我讲清楚了这个问题。若您有啥不解可以给我留言,B站吧
https://space.bilibili.com/545756492?spm_id_from=333.788.0.0

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-4-29 22:51 , Processed in 0.131852 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表