|
最近在做毕业设计,涉及到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(&#34;HID&#34;)
.WithManufacturer(&#34;Sony.+Entertainment&#34;)
.WithProduct(&#34;Wireless Controller&#34;));
// Alternatively, you can also match by PID and VID, which is generally
// more reliable for HIDs.
InputSystem.RegisterLayout<DualShock4GamepadHID>(
matches: new InputDeviceMatcher()
.WithInterface(&#34;HID&#34;)
.WithCapability(&#34;vendorId&#34;, 0x54C) // Sony Entertainment.
.WithCapability(&#34;productId&#34;, 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(&#34;Sony.+Entertainment&#34;)
.WithProduct(&#34;Wireless Controller&#34;));,一个是HID设备的PID和VID
InputSystem.RegisterLayout<DualShock4GamepadHID>(
matches: new InputDeviceMatcher()
.WithInterface(&#34;HID&#34;)
.WithCapability(&#34;vendorId&#34;, 0x54C) // Sony Entertainment.
.WithCapability(&#34;productId&#34;, 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(&#39;H&#39;, &#39;I&#39;, &#39;D&#39;);
[FieldOffset(0)] public byte reportId;
[InputControl(name = &#34;leftStick&#34;, layout = &#34;Stick&#34;, format = &#34;VC2B&#34;)]
[InputControl(name = &#34;leftStick/x&#34;, offset = 0, format = &#34;BYTE&#34;,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5&#34;)]
[InputControl(name = &#34;leftStick/left&#34;, offset = 0, format = &#34;BYTE&#34;,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0,clampMax=0.5,invert&#34;)]
[InputControl(name = &#34;leftStick/right&#34;, offset = 0, format = &#34;BYTE&#34;,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0.5,clampMax=1&#34;)]
[InputControl(name = &#34;leftStick/y&#34;, offset = 1, format = &#34;BYTE&#34;,
parameters = &#34;invert,normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5&#34;)]
[InputControl(name = &#34;leftStick/up&#34;, offset = 1, format = &#34;BYTE&#34;,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0,clampMax=0.5,invert&#34;)]
[InputControl(name = &#34;leftStick/down&#34;, offset = 1, format = &#34;BYTE&#34;,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp,clampMin=0.5,clampMax=1,invert=false&#34;)]
[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(&#34;HID&#34;)
.WithCapability(&#34;vendorId&#34;, 1212) // Sony Entertainment.
.WithCapability(&#34;productId&#34;, 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(&#39;H&#39;, &#39;I&#39;, &#39;D&#39;);
[FieldOffset(0)] public byte reportId;
[InputControl(name = &#34;stick&#34;, format = &#34;VC2B&#34;, layout = &#34;Stick&#34;, displayName = &#34;Main Stick&#34;)]
[InputControl(name = &#34;stick/x&#34;, defaultState = 127, format = &#34;BYTE&#34;,
offset = 0,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5&#34;)]
[FieldOffset(1)] public byte x;
[InputControl(name = &#34;stick/y&#34;, defaultState = 127, format = &#34;BYTE&#34;,
offset = 1,
parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5&#34;)]
// 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 = &#34;stick/up&#34;, parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=0,clampMax=1&#34;)]
[InputControl(name = &#34;stick/down&#34;, parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=-1,clampMax=0,invert&#34;)]
[InputControl(name = &#34;stick/left&#34;, parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=-1,clampMax=0,invert&#34;)]
[InputControl(name = &#34;stick/right&#34;, parameters = &#34;normalize,normalizeMin=0,normalizeMax=1,normalizeZero=0.5,clamp=2,clampMin=0,clampMax=1&#34;)]
[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 |
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|