PicoXR中的输入事件
前言
? picoXR为Unity提供的Unity XR SDK是基于Unity XR实现的各个功能,针对手柄和头盔的各个按键摇杆事件的获取,均是采用的Unity XR提供的方法。目前UnityXR只提供了if判断的方式每帧监听的方式,还未提供事件接口等形式。
简介
为方便读者理解后续API,先粗略介绍一下Unity XR监听按键摇杆的步骤。首先是获取需要监听的设备,然后判断此设备的某种行为是否发生,UnityXR是无差别对待各种品牌的设备的,每种品牌的设备的按键等也都不尽相同。自然设备的行为也是多种多样的,UnityXR提供了一组普遍的行为特征(CommonsUsages),但要具体使用需要根据使用的设备查阅设备具体按键和UnityXR提供的普遍特征的对应关系。本文主要介绍Pico产品,这是pico官网提供的映射关系文档链接https://sdk.picovr.com/docs/XRPlatformSDK/Unity/cn/chapter_five.html#id5
设备的获取
? 关于设备的获取和事件的监听UnityXR的官方文档有很好的解释和示例,更推荐大家去官网学习https://docs.unity3d.com/Manual/xr_input.html
首先需要引入UnityEngine.XR的命名空间,设备的获取主要用到的API为InputDevice和InputDevices
接下来介绍两种设备获取的方式
-
获取全部的设备 using UnityEngine.XR;
public List<InputDevice> GetAll()
{
List<InputDevice> deviceList = new List<InputDevice>();
InputDevices.GetDevices(deviceList);
return deviceList;
}
-
根据XRNode获取设备
InputDevice headController = InputDevices.GetDeviceAtXRNode(XRNode.Head);
InputDevice leftHandController = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
InputDevice rightHandController = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
输入事件
? 前面提到过,光获取到设备还需要知道设备上各个按键或摇杆的行为特征,注意行为特征一定要去官网查看其和设备各个按键的映射关系才能正确的开发。
这里拿Pico neo3的手柄映射关系举例
主要用到的API是InputDevice.TryGetFeatureValue(InputFeatureUsage usage,T value);
返回值为bool — 是否获取到行为特征值
参数1 — 行为特征(根据不同行为有不同的参数,例如按键型就是bool value代表是否按下,摇杆型就是Vector2,value代表偏移量axis)
-
判断是否按下了Trigger键 InputDevice device;
public void Test()
{
bool isDown;
if(device.TryGetFeatureValue(CommonUsages.triggerButton,out isDown) && isDown)
{
}
}
-
判断是否移动了摇杆 InputDevice device;
public void Test()
{
Vector2 axis;
if (device.TryGetFeatureValue(usage, out axis) && !axis.Equals(Vector2.zero))
{
}
}
优化
? 对比Unity的其它组件提供的事件响应机制,大多靠event,委托和接口实现,而UnityXR提供的这个输入事件使用起来一般都是放在Update里每帧轮询检测,不足是显而易见的。
- 输入事件和处理逻辑耦合在一起
- 只有按下事件,没有提供开始,抬起等事件
? 所以需要自行实现一组公开event事件,实现相应事件源,供其他观察者进行注册,即可解决上面提到的一些不足。
笔者自行实现了一个简易的InputEvent单例类,提供了按键类的Enter,Down,Up事件和摇杆类的Move事件。
InputEvent.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
using Common;
public class InputEvent:MonoSingleton<InputEvent>
{
InputDevice leftHandController;
InputDevice rightHandController;
InputDevice headController;
#region public event
public Action onLeftTriggerEnter;
public Action onLeftTriggerDown;
public Action onLeftTriggerUp;
public Action onRightTriggerEnter;
public Action onRightTriggerDown;
public Action onRightTriggerUp;
public Action onLeftGripEnter;
public Action onLeftGripDown;
public Action onLeftGripUp;
public Action onRightGripEnter;
public Action onRightGripDown;
public Action onRightGripUp;
public Action onLeftAppButtonEnter;
public Action onLeftAppButtonDown;
public Action onLeftAppButtonUp;
public Action onRightAppButtonEnter;
public Action onRightAppButtonDown;
public Action onRightAppButtonUp;
public Action onLeftJoyStickEnter;
public Action onLeftJoyStickDown;
public Action onLeftJoyStickUp;
public Action onRightJoyStickEnter;
public Action onRightJoyStickDown;
public Action onRightJoyStickUp;
public Action<Vector2> onLeftJoyStickMove;
public Action<Vector2> onRightJoyStickMove;
public Action onLeftAXButtonEnter;
public Action onLeftAXButtonDown;
public Action onLeftAXButtonUp;
public Action onLeftBYButtonEnter;
public Action onLeftBYButtonDown;
public Action onLeftBYButonUp;
public Action onRightAXButtonEnter;
public Action onRightAXButtonDown;
public Action onRightAXButtonUp;
public Action onRightBYButtonEnter;
public Action onRightBYButtonDown;
public Action onRightBYButtonUp;
#endregion
Dictionary<string, bool> stateDic;
protected override void Init()
{
base.Init();
leftHandController = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
rightHandController = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
headController = InputDevices.GetDeviceAtXRNode(XRNode.Head);
stateDic = new Dictionary<string, bool>();
}
private void ButtonDispatchModel(InputDevice device,InputFeatureUsage<bool> usage,Action btnEnter,Action btnDown,Action btnUp)
{
Debug.Log("usage:" + usage.name);
string featureKey = device.name + usage.name;
if(!stateDic.ContainsKey(featureKey))
{
stateDic.Add(featureKey, false);
}
bool isDown;
if(device.TryGetFeatureValue(usage,out isDown) && isDown)
{
if(!stateDic[featureKey])
{
stateDic[featureKey] = true;
if(btnEnter != null)
btnEnter();
}
if(btnDown!=null)
btnDown();
}
else
{
if(stateDic[featureKey])
{
if(btnUp!=null)
btnUp();
stateDic[featureKey] = false;
}
}
}
private void JoyStickDispatchModel(InputDevice device,InputFeatureUsage<Vector2> usage,Action<Vector2> joyStickMove)
{
Vector2 axis;
if (device.TryGetFeatureValue(usage, out axis) && !axis.Equals(Vector2.zero))
{
if(joyStickMove!=null)
joyStickMove(axis);
}
}
private void Update()
{
ButtonDispatchModel(leftHandController, CommonUsages.triggerButton, onLeftTriggerEnter, onLeftTriggerDown, onLeftTriggerUp);
ButtonDispatchModel(rightHandController, CommonUsages.triggerButton, onRightTriggerEnter, onRightTriggerDown, onRightTriggerUp);
ButtonDispatchModel(leftHandController, CommonUsages.gripButton, onLeftGripEnter, onLeftGripDown, onLeftGripUp);
ButtonDispatchModel(rightHandController, CommonUsages.gripButton, onRightGripEnter, onRightGripDown, onRightGripUp);
ButtonDispatchModel(leftHandController, CommonUsages.primaryButton, onLeftAXButtonEnter, onLeftAXButtonDown, onLeftAXButtonUp);
ButtonDispatchModel(rightHandController, CommonUsages.primaryButton, onRightAXButtonEnter, onRightAXButtonDown, onRightAXButtonUp);
ButtonDispatchModel(leftHandController, CommonUsages.secondaryButton, onLeftBYButtonEnter, onLeftBYButtonDown, onLeftBYButonUp);
ButtonDispatchModel(rightHandController, CommonUsages.secondaryButton, onRightBYButtonEnter, onRightBYButtonDown, onRightBYButtonUp);
ButtonDispatchModel(leftHandController, CommonUsages.primary2DAxisClick, onLeftJoyStickEnter, onLeftJoyStickDown, onLeftJoyStickUp);
ButtonDispatchModel(rightHandController, CommonUsages.primary2DAxisClick, onRightJoyStickEnter, onRightJoyStickDown, onRightJoyStickUp);
ButtonDispatchModel(leftHandController, CommonUsages.menuButton, onLeftAppButtonEnter, onLeftAppButtonDown, onLeftAppButtonUp);
ButtonDispatchModel(rightHandController, CommonUsages.menuButton, onRightAppButtonEnter, onRightAppButtonDown,onRightAppButtonUp);
JoyStickDispatchModel(leftHandController, CommonUsages.primary2DAxis, onLeftJoyStickMove);
JoyStickDispatchModel(rightHandController, CommonUsages.primary2DAxis, onRightJoyStickMove);
}
}
单例模式采用的是脚本单例,更多具体的单例模式有兴趣的读者可以自行搜索一下。
MonoSingleton.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Common
{
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T>
{
private static T instance;
public static T Instance
{
get
{
if (instance != null) return instance;
instance = FindObjectOfType<T>();
if (instance == null)
{
new GameObject("Singleton of " + typeof(T)).AddComponent<T>();
}
else instance.Init();
return instance;
}
}
private void Awake()
{
instance = this as T;
Init();
}
protected virtual void Init()
{
}
}
}
|