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

最近在做毕业设计,涉及到HID设备接入unity。

HID设备配置

当你想用单片机自己开发一个HID手柄时,网上可以找到比较多的教程,这里有一个大佬出的stm32的教程

使用 cubeMX软件配置游戏控制器_哔哩哔哩_bilibili

我是用stm32做的,当你成功配置手柄接入windows后,控制面板可以看到你的设备

编辑切换为居中

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

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

编辑

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

这里我的报告描述符自定义手柄只有两个轴,就是x和y两个轴

轻轻滑动自定义手柄传感器可以看到两个轴的输入是正常的,到此为止我们的自定义HID手柄配置完成,下面我们看看怎样把它接入unity

unity接入

HID设备已经配置好了,按道理来说我们可以在unity里面直接使用,是的用unity自代的inputManager(old)确实可以使用我们的手柄进行输入,不需要任何配置。

但是有时候许多特定功能需要用到input system来配置我们的手柄进行操作。这时问题就出现了,unity并不认识你自定义的手柄。就像下图

编辑切换为居中

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

这里如果你配置的手柄HID报告描述符被unity所识别那就不需要其他配置。但是如果你的HID设备不被unity所识别那就需要自己添加为HID设备。

再input system找了好久,这里是一篇添加的教程(之后我称之为官网教程):

HID Support | Input System | 1.0.2

由于涉及过多偏底层的开发,这里我和一位热心网友费好大劲才把他做好。这里我尽量用做通俗的语言把过程说清楚,这里由于每个人的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


        // 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.

更改其中的数据为自己的手柄配置,如果觉得麻烦就只保留PID和VID的识别

把上边的东西放在自己的代码里面你已经可以成功把你的HID设备添加为inputsystem支持的设备了,对,没错,现在你已经可以用inputsystem向HID设备发送信息了,具体操作看我这篇文章

unity与HID输入设备通讯,手柄控制器发聩功能实现 - 知乎

那么可以输入了吗??这时我们打开input debug窗口

编辑切换为居中

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

发现不管怎么输入,这里数据依然是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 = 32)]
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;

    [InputControl(name = "rightStick", layout = "Stick", format = "VC2B")]
    [InputControl(name = "rightStick/x", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
    [InputControl(name = "rightStick/left", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0,clampMax=0.5,invert")]
    [InputControl(name = "rightStick/right", offset = 0, format = "BYTE", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0.5,clampMax=1")]
    [InputControl(name = "rightStick/y", offset = 1, format = "BYTE", parameters = "invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5")]
    [InputControl(name = "rightStick/up", offset = 1, format = "BYTE", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0,clampMax=0.5,invert")]
    [InputControl(name = "rightStick/down", offset = 1, format = "BYTE", parameters = "normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0.5,clampMax=1,invert=false")]
    [FieldOffset(3)] public byte rightStickX;
    [FieldOffset(4)] public byte rightStickY;
}

要想写其他布局可以去官网教程看看哪个例子

这次再打开input Debug就可以看到我的数据可以输入进去了:

编辑切换为居中

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

这里有可能你的布局出现的还不是自己写的。那么有可能是你自己的设备继承鱼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)]
//[DefaultMember("Item")]
//[InputControlLayout(stateType = typeof(GamepadState), isGenericTypeOfDevice = true)]
//[Preserve]
public class MyPad : InputDevice
{
    static MyPad()
    {

        InputSystem.RegisterLayout<MyPad>(
            matches: new InputDeviceMatcher()
                .WithInterface("HID")
                .WithCapability("vendorId", 1126) // Sony Entertainment.
                .WithCapability("productId", 11346)); // 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;
}

这样自己的设备就是标准的输入设备啦。可以输入也可以作交互反馈,反馈功能见我另一篇文章:

unity与HID输入设备通讯,手柄控制器发聩功能实现 - 知乎

希望我讲清楚了这个问题。若您有啥不解可以给我留言,B站吧

小皮同学爱学习的个人空间_哔哩哔哩_Bilibili

  • 12
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值