Pico为例:解决XR系统中的按键系统没有办法像Input.GetMouseDown那样判断按下、抬起和一直按下,也没有一个好的封装方法。

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;
    }
}

修改完之后调用的话也是正常调用,没有关系

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值