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
GetAll() { List deviceList = new List (); //实际为传入list引用获取的InputDevice全部放在list中 InputDevices.GetDevices(deviceList); return deviceList; } -
根据XRNode获取设备
//XRNode为枚举变量 //常用的有 Head LeftHand RightHand //根据这些枚举可以轻松获得指定的头盔,左手柄,右手柄 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; //省略device的获取 使用前要先获取 public void Test() { bool isDown; //记录是否按下 if(device.TryGetFeatureValue(CommonUsages.triggerButton,out isDown) && isDown) { //xxxxx 处理逻辑 } } -
判断是否移动了摇杆
InputDevice device; public void Test() { Vector2 axis; if (device.TryGetFeatureValue(usage, out axis) && !axis.Equals(Vector2.zero)) { //xxxxx相关逻辑 } }
对比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{ / //解决1:使用泛型创建实例 解决2:使用按需加载(即有其它脚本调用时在get中加载) private static T instance; //创建私有对象记录取值,可只赋值一次避免多次赋值 public static T Instance { //实现按需加载 get { //当已经赋值,则直接返回即可 if (instance != null) return instance; instance = FindObjectOfType (); //为了防止脚本还未挂到物体上,找不到的异常情况,可以自行创建空物体挂上去 if (instance == null) { //如果创建对象,则会在创建时调用其身上脚本的Awake即调用T的Awake(T的Awake实际上是继承的父类的) //所以此时无需为instance赋值,其会在Awake中赋值,自然也会初始化所以无需init() new GameObject("Singleton of " + typeof(T)).AddComponent (); } else instance.Init(); //保证Init只执行一次 return instance; } } private void Awake() { //若无其它脚本在Awake中调用此实例,则可在Awake中自行初始化instance instance = this as T; //初始化 Init(); } //子类对成员进行初始化如果放在Awake里仍会出现Null问题所以自行制作一个init函数解决(可用可不用) protected virtual void Init() { } } }



