Unity XR Input 获取Quest和Pico各个按键状态,按下、抬起、按下中
测试使用的是Unity2021.3.8和Quest2,只需绑定事件即可获取Quest各个按键状态,按下,抬起,按下中··
参考文档:
https://docs.unity3d.com/cn/current/Manual/xr_input.html
https://blog.csdn.net/Q540670228/article/details/123139150
使用以上代码时碰到些小问题(调试时获取不到设备,按钮冲突),部分内容做了修改
实现效果
全部代码
MonoSingleton.cs
using UnityEngine;
namespace Common
{
///<summary>
///脚本单例类,负责为唯一脚本创建实例
///<summary>
public class MonoSingleton<T> : MonoBehaviour where T : MonoSingleton<T> //注意此约束为T必须为其本身或子类
{
/*
相较于直接在需要唯一创建的脚本中创建实例,Awake初始化的过程需要解决的问题
1.代码重复
2.在Awake里面初始化,其它脚本在Awake中调用其可能会为Null的异常情况
*/
//解决1:使用泛型创建实例 解决2:使用按需加载(即有其它脚本调用时在get中加载)
private static T instance; //创建私有对象记录取值,可只赋值一次避免多次赋值
public static T Instance
{
//实现按需加载
get
{
//当已经赋值,则直接返回即可
if (instance != null) return instance;
instance = FindObjectOfType<T>();
//为了防止脚本还未挂到物体上,找不到的异常情况,可以自行创建空物体挂上去
if (instance == null)
{
//如果创建对象,则会在创建时调用其身上脚本的Awake即调用T的Awake(T的Awake实际上是继承的父类的)
//所以此时无需为instance赋值,其会在Awake中赋值,自然也会初始化所以无需init()
/*instance = */
new GameObject("Singleton of " + typeof(T)).AddComponent<T>();
}
return instance;
}
}
private void Awake()
{
//若无其它脚本在Awake中调用此实例,则可在Awake中自行初始化instance
instance = this as T;
//初始化
//Init();
}
}
}
InputEvent.cs
//示例中此代码放到了XR Origin上面
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
/// <summary>
/// 提供各种输入事件
/// </summary>
public class InputEvent : Common.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
//提供状态字典独立记录各个feature的状态
Dictionary<string, bool> stateDic;
private void Init()
{
if (!leftHandController.isValid)
{
leftHandController = InputDevices.GetDeviceAtXRNode(XRNode.LeftHand);
}
if (!rightHandController.isValid)
{
rightHandController = InputDevices.GetDeviceAtXRNode(XRNode.RightHand);
}
if (!headController.isValid)
{
headController = InputDevices.GetDeviceAtXRNode(XRNode.Head);
}
stateDic = new Dictionary<string, bool>();
}
//*******************事件源的触发**************************
/// <summary>
/// 按钮事件源触发模板
/// </summary>
/// <param name="device">设备</param>
/// <param name="usage">功能特征</param>
/// <param name="btnEnter">开始按下按钮事件</param>
/// <param name="btnDown">按下按钮事件</param>
/// <param name="btnUp">抬起按钮事件</param>
private void ButtonDispatchModel(InputDevice device, InputFeatureUsage<bool> usage, Action btnEnter, Action btnDown, Action btnUp)
{
//Debug.Log("usage:" + usage.name);
//为首次执行的feature添加bool状态 -- 用以判断Enter和Up状态
string featureKey = device.characteristics + usage.name;
if (!stateDic.ContainsKey(featureKey))
{
stateDic.Add(featureKey, false);
}
bool isDown;
if (device.TryGetFeatureValue(usage, out isDown) && isDown)
{
//Debug.Log("device:" + device.characteristics + "usage:" + usage.name);
if (!stateDic[featureKey])
{
stateDic[featureKey] = true;
btnEnter?.Invoke();
}
btnDown?.Invoke();
}
else
{
if (stateDic[featureKey])
{
btnUp?.Invoke();
stateDic[featureKey] = false;
}
}
}
/// <summary>
/// 摇杆事件源触发模板
/// </summary>
/// <param name="device">设备</param>
/// <param name="usage">功能特征</param>
/// <param name="joyStickMove">移动摇杆事件</param>
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()
{
if (leftHandController.isValid)
{
ButtonDispatchModel(leftHandController, CommonUsages.triggerButton, onLeftTriggerEnter, onLeftTriggerDown, onLeftTriggerUp);
ButtonDispatchModel(leftHandController, CommonUsages.gripButton, onLeftGripEnter, onLeftGripDown, onLeftGripUp);
ButtonDispatchModel(leftHandController, CommonUsages.primaryButton, onLeftAXButtonEnter, onLeftAXButtonDown, onLeftAXButtonUp);
ButtonDispatchModel(leftHandController, CommonUsages.secondaryButton, onLeftBYButtonEnter, onLeftBYButtonDown, onLeftBYButonUp);
ButtonDispatchModel(leftHandController, CommonUsages.primary2DAxisClick, onLeftJoyStickEnter, onLeftJoyStickDown, onLeftJoyStickUp);
ButtonDispatchModel(leftHandController, CommonUsages.menuButton, onLeftAppButtonEnter, onLeftAppButtonDown, onLeftAppButtonUp);
//JoyStickDispatchModel(leftHandController, CommonUsages.primary2DAxis, onLeftJoyStickMove);
}
else
{
Init();
}
if (rightHandController.isValid)
{
ButtonDispatchModel(rightHandController, CommonUsages.triggerButton, onRightTriggerEnter, onRightTriggerDown, onRightTriggerUp);
ButtonDispatchModel(rightHandController, CommonUsages.gripButton, onRightGripEnter, onRightGripDown, onRightGripUp);
ButtonDispatchModel(rightHandController, CommonUsages.primaryButton, onRightAXButtonEnter, onRightAXButtonDown, onRightAXButtonUp);
ButtonDispatchModel(rightHandController, CommonUsages.secondaryButton, onRightBYButtonEnter, onRightBYButtonDown, onRightBYButtonUp);
ButtonDispatchModel(rightHandController, CommonUsages.primary2DAxisClick, onRightJoyStickEnter, onRightJoyStickDown, onRightJoyStickUp);
ButtonDispatchModel(rightHandController, CommonUsages.menuButton, onRightAppButtonEnter, onRightAppButtonDown, onRightAppButtonUp);
//JoyStickDispatchModel(rightHandController, CommonUsages.primary2DAxis, onRightJoyStickMove);
}
else
{
Init();
}
}
}
使用示例
LeftHandButton.cs
//示例中此代码放到了LeftHand Controller上面
using UnityEngine;
public class LeftHandButton : MonoBehaviour
{
private void Awake()
{
InputEvent.Instance.onLeftAXButtonUp += LeftAXButtonUp;
InputEvent.Instance.onLeftAXButtonEnter += LeftAXButtonEnter;
InputEvent.Instance.onLeftAXButtonDown += LeftAXButtonDown;
}
void LeftAXButtonDown()
{
print("左手柄A键按下中···");
}
void LeftAXButtonEnter()
{
print("按下左手柄A键");
}
void LeftAXButtonUp()
{
print("抬起左手柄A键");
}
}