1.首先我们需要把所有需要的按键存入一个枚举当中
public enum XRButton
{
MenuButton,
TriggerButton,
GripButton,
PrimaryButton,
SecondaryButton
}
如果需要其他的键位也可以添加进去
2.定义俩个字典,用来存储每个按键的按下和松开的状态
private static Dictionary<XRButton, bool> buttonPressedStates = new Dictionary<XRButton, bool>();
private static Dictionary<XRButton, bool> buttonReleasedStates = new Dictionary<XRButton, bool>();
3.写一个初始化俩个枚举中的状态
private static void Initialize()
{
foreach (XRButton button in System.Enum.GetValues(typeof(XRButton)))
{
if (!buttonPressedStates.ContainsKey(button))
{
buttonPressedStates[button] = false;
}
if (!buttonReleasedStates.ContainsKey(button))
{
buttonReleasedStates[button] = true; // 初始状态为已经松开
}
}
}
4.这个时候我们要开始获取XR中是哪个手柄控制器,以便后续使用,封装一个方法
private static InputDevice GetDevice(XRNode hand)
{
return InputDevices.GetDeviceAtXRNode(hand);
}
这样后续要使用哪个手柄就传入对应的手柄控制器就行
5.接下来我们把XRButton这个枚举里的我们想要使用的按键给返回对应的按键
private static InputFeatureUsage<bool> GetFeatureUsage(XRButton button)
{
switch (button)
{
case XRButton.MenuButton:
return CommonUsages.menuButton;
case XRButton.TriggerButton:
return CommonUsages.triggerButton;
case XRButton.GripButton:
return CommonUsages.gripButton;
case XRButton.PrimaryButton:
return CommonUsages.primaryButton;
case XRButton.SecondaryButton:
return CommonUsages.secondaryButton;
default:
return CommonUsages.triggerButton;
//用到什么再去添加
}
}
当然,如果使用其他的设备,可以再添加对应的,比如摇杆按键的值,因为笔者这里没有用到所以就没有去添加
6.最后就可以去封装写Down、Up和一直触发的方法了,这里逻辑也比较简单,大概逻辑就是先通过前面封装的手柄控制器方法和按键的方法,然后传入进去之后去判断字典中存储的状态去对应,
device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue)这里的意思就是判断是否按下的,然后去和字典的状态一起去判断对应状态,都是二者必须相反。捋一下也比较容易看懂就不做具体介绍了。
/// <summary>
/// 按下方法
/// </summary>
/// <param name="hand">手柄控制器</param>
/// <param name="button">按键</param>
/// <returns></returns>
public static bool GetButtonDown(XRNode hand, XRButton button)
{
Initialize();
InputDevice device = GetDevice(hand);
bool buttonValue = false;
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
// 检测按下且之前为松开状态
if (buttonValue && buttonReleasedStates[button])
{
buttonPressedStates[button] = true;
buttonReleasedStates[button] = false; // 标记为按下
return true;
}
}
// 按键松开时,标记为松开
if (!buttonValue)
{
buttonReleasedStates[button] = true;
}
return false;
}
/// <summary>
/// 抬起方法
/// </summary>
/// <param name="hand">手柄控制器</param>
/// <param name="button">按键</param>
/// <returns></returns>
public static bool GetButtonUp(XRNode hand, XRButton button)
{
Initialize();
InputDevice device = GetDevice(hand);
bool buttonValue = false;
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
if (!buttonValue && buttonPressedStates[button])
{
buttonPressedStates[button] = false;
return true;
}
}
return false;
}
/// <summary>
/// 一直触发
/// </summary>
/// <param name="hand">手柄控制器</param>
/// <param name="button">按键</param>
/// <returns></returns>
public static bool GetButton(XRNode hand, XRButton button)
{
Initialize();
InputDevice device = GetDevice(hand);
bool buttonValue = false;
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
return buttonValue;
}
return false;
}
7.最后我们就可以去使用了,使用方法:
void Update()
{
if (XRInputController.GetButtonDown(XRNode.RightHand,XRButton.TriggerButton))
{
Debug.LogError("扳机键按下了");
}
if (XRInputController.GetButtonDown(XRNode.RightHand,XRButton.GripButton))
{
Debug.LogError("握手键按下了");
}
if (XRInputController.GetButtonUp(XRNode.RightHand,XRButton.GripButton))
{
Debug.LogError("握手键抬起了");
}
if (XRInputController.GetButton(XRNode.RightHand,XRButton.GripButton))
{
Debug.LogError("握手键一直在触发");
}
}
8.最后把完整的代码写出来
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public static class XRInputController
{
private static Dictionary<XRButton, bool> buttonPressedStates = new Dictionary<XRButton, bool>();
private static Dictionary<XRButton, bool> buttonReleasedStates = new Dictionary<XRButton, bool>();
private static void Initialize()
{
foreach (XRButton button in System.Enum.GetValues(typeof(XRButton)))
{
if (!buttonPressedStates.ContainsKey(button))
{
buttonPressedStates[button] = false;
}
if (!buttonReleasedStates.ContainsKey(button))
{
buttonReleasedStates[button] = true; // 初始状态为已经松开
}
}
}
private static InputDevice GetDevice(XRNode hand)
{
return InputDevices.GetDeviceAtXRNode(hand);
}
private static InputFeatureUsage<bool> GetFeatureUsage(XRButton button)
{
switch (button)
{
case XRButton.MenuButton:
return CommonUsages.menuButton;
case XRButton.TriggerButton:
return CommonUsages.triggerButton;
case XRButton.GripButton:
return CommonUsages.gripButton;
case XRButton.PrimaryButton:
return CommonUsages.primaryButton;
case XRButton.SecondaryButton:
return CommonUsages.secondaryButton;
default:
return CommonUsages.triggerButton;
}
}
/// <summary>
/// 按下方法
/// </summary>
/// <param name="hand">手柄控制器</param>
/// <param name="button">按键</param>
/// <returns></returns>
public static bool GetButtonDown(XRNode hand, XRButton button)
{
Initialize();
InputDevice device = GetDevice(hand);
bool buttonValue = false;
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
// 检测按下且之前为松开状态
if (buttonValue && buttonReleasedStates[button])
{
buttonPressedStates[button] = true;
buttonReleasedStates[button] = false; // 标记为按下
return true;
}
}
// 按键松开时,标记为松开
if (!buttonValue)
{
buttonReleasedStates[button] = true;
}
return false;
}
/// <summary>
/// 抬起方法
/// </summary>
/// <param name="hand">手柄控制器</param>
/// <param name="button">按键</param>
/// <returns></returns>
public static bool GetButtonUp(XRNode hand, XRButton button)
{
Initialize();
InputDevice device = GetDevice(hand);
bool buttonValue = false;
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
if (!buttonValue && buttonPressedStates[button])
{
buttonPressedStates[button] = false;
return true;
}
}
return false;
}
/// <summary>
/// 一直触发
/// </summary>
/// <param name="hand">手柄控制器</param>
/// <param name="button">按键</param>
/// <returns></returns>
public static bool GetButton(XRNode hand, XRButton button)
{
Initialize();
InputDevice device = GetDevice(hand);
bool buttonValue = false;
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
return buttonValue;
}
return false;
}
}
End:个人学习,有不足指教
几天后....
实际投入到项目使用发现有几个问题:同时多调用同样的按键触发方式会只执行第一个的触发,同时调用不同手柄的同样的的按键,依旧会出现持续按下的状态,所以修改如下:
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
// 定义XRInput静态类,用于管理XR手柄按键状态
public static class XRInputController
{
// 存储按键当前按下状态、释放状态、刚按下状态、刚抬起状态的字典
private static Dictionary<(XRNode, XRButton), bool> buttonPressedStates = new Dictionary<(XRNode, XRButton), bool>();
private static Dictionary<(XRNode, XRButton), bool> buttonReleasedStates = new Dictionary<(XRNode, XRButton), bool>();
private static Dictionary<(XRNode, XRButton), bool> buttonCurrentDownStates = new Dictionary<(XRNode, XRButton), bool>();
private static Dictionary<(XRNode, XRButton), bool> buttonCurrentUpStates = new Dictionary<(XRNode, XRButton), bool>();
// 内部辅助类,用于每帧更新按键状态
private class XRInputUpdater : MonoBehaviour
{
private void Update()
{
// 遍历左右手和所有按键类型,更新按键状态
foreach (XRNode hand in new[] { XRNode.LeftHand, XRNode.RightHand })
{
foreach (XRButton button in (XRButton[])System.Enum.GetValues(typeof(XRButton)))
{
UpdateButtonState(hand, button); // 更新当前按键状态
}
}
}
}
// 确保Updater在场景中激活,保证初始化只调用一次
[RuntimeInitializeOnLoadMethod]
private static void Initialize()
{
// 如果场景中没有XRInputUpdater对象,创建一个并添加到场景中
if (Object.FindObjectOfType<XRInputUpdater>() == null)
{
var updater = new GameObject("XRInputUpdater").AddComponent<XRInputUpdater>();
Object.DontDestroyOnLoad(updater.gameObject); // 设置为场景切换不销毁
}
}
// 初始化按键状态,将按键状态加入字典
private static void InitializeButtonState(XRNode hand, XRButton button)
{
var key = (hand, button);
// 如果按键尚未记录状态,则初始化所有状态
if (!buttonPressedStates.ContainsKey(key))
{
buttonPressedStates[key] = false; // 按下状态
buttonReleasedStates[key] = true; // 释放状态
buttonCurrentDownStates[key] = false; // 刚按下状态
buttonCurrentUpStates[key] = false; // 刚抬起状态
}
}
// 根据按键类型返回对应的FeatureUsage,映射XR按键类型
private static InputFeatureUsage<bool> GetFeatureUsage(XRButton button)
{
return button switch
{
XRButton.MenuButton => CommonUsages.menuButton,
XRButton.TriggerButton => CommonUsages.triggerButton,
XRButton.GripButton => CommonUsages.gripButton,
XRButton.PrimaryButton => CommonUsages.primaryButton,
XRButton.SecondaryButton => CommonUsages.secondaryButton,
_ => CommonUsages.triggerButton // 默认使用TriggerButton
};
}
// 更新按键的当前状态
private static void UpdateButtonState(XRNode hand, XRButton button)
{
InitializeButtonState(hand, button); // 初始化按键状态(若未初始化)
InputDevice device = InputDevices.GetDeviceAtXRNode(hand); // 获取指定手柄设备
bool buttonValue = false;
var key = (hand, button);
// 尝试获取当前按键状态
if (device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue))
{
// 检测按键是否刚刚按下
if (buttonValue && buttonReleasedStates[key])
{
buttonCurrentDownStates[key] = true; // 标记刚按下
buttonPressedStates[key] = true; // 标记为按下状态
buttonReleasedStates[key] = false; // 标记不在释放状态
}
else
{
buttonCurrentDownStates[key] = false; // 重置刚按下状态
}
// 检测按键是否刚刚释放
if (!buttonValue && buttonPressedStates[key])
{
buttonCurrentUpStates[key] = true; // 标记刚抬起
buttonPressedStates[key] = false; // 重置按下状态
buttonReleasedStates[key] = true; // 标记为释放状态
}
else
{
buttonCurrentUpStates[key] = false; // 重置刚抬起状态
}
}
}
// 获取按键是否刚刚按下
public static bool GetButtonDown(XRNode hand, XRButton button)
{
var key = (hand, button);
return buttonCurrentDownStates.ContainsKey(key) && buttonCurrentDownStates[key];
}
// 获取按键是否刚刚释放
public static bool GetButtonUp(XRNode hand, XRButton button)
{
var key = (hand, button);
return buttonCurrentUpStates.ContainsKey(key) && buttonCurrentUpStates[key];
}
// 获取按键是否持续按下
public static bool GetButton(XRNode hand, XRButton button)
{
InputDevice device = InputDevices.GetDeviceAtXRNode(hand);
bool buttonValue = false;
return device.TryGetFeatureValue(GetFeatureUsage(button), out buttonValue) && buttonValue;
}
}
修改完之后调用的话也是正常调用,没有关系