Unity Pico 设备按键

博客介绍了XR Interaction Toolkit 2.5.2,提及Pico系统键盘支持旧输入并发布显示,还涉及Pico设备的按键及震动相关内容,属于游戏开发领域信息技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这里插入图片描述

XR Interaction Toolkit 2.5.2

Pico 系统键盘 支持旧输入 (发布显示)

Pico 设备按键、震动

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
using CommonUsages = UnityEngine.XR.CommonUsages;
using InputDevice = UnityEngine.XR.InputDevice;
using UnityEngine.XR.Interaction.Toolkit;
using UnityEngine.EventSystems;

public class InputEvent : MonoBehaviour
{
    private InputDeviceEvent leftEvent;
    private InputDeviceEvent rightEvent;
    //public InputDeviceEvent headEvent;

    [SerializeField]
    private XRRayInteractor leftHandRay;
    [SerializeField]
    private XRRayInteractor rightHandRay;
  
    void Awake()
    {
        leftEvent = new InputDeviceEvent(XRNode.LeftHand);
        rightEvent = new InputDeviceEvent(XRNode.RightHand);
        //headEvent = new InputDeviceEvent(XRNode.Head); 
        //Test();
    }

    private void FixedUpdate()
    {
        leftEvent.Update();
        rightEvent.Update();
    }

    #region Core

    /// <summary>
    /// 清空事件 
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    public void RmoveActionAll(int id = 0)
    {
        switch (id)
        {
            case 0:
                leftEvent.RmoveActionAll();
                rightEvent.RmoveActionAll(); break;
            case 1:
                leftEvent.RmoveActionAll(); break;
            case 2:
                rightEvent.RmoveActionAll(); break;
            default:
                break;
        }
    }

    /// <summary>
    /// 添加 移除事件
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="isAdd"></param>
    /// <param name="usagesType"></param>
    /// <param name="btnEnter"></param>
    /// <param name="btnStay"></param>
    /// <param name="btnExit"></param>
    public void AddOrRmoveAction(int id, bool isAdd, CommonUsagesType usagesType, Action<Vector2> btnEnter = null, Action<Vector2> btnStay = null, Action<Vector2> btnExit = null)
    {
        switch (id)
        {
            case 0:
                leftEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit);
                rightEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit); break;
            case 1:
                leftEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit); break;
            case 2:
                rightEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit); break;
            default:
                break;
        }
    }

    /// <summary>
    /// 添加 移除事件
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="isAdd"></param>
    /// <param name="usagesType"></param>
    /// <param name="btnEnter"></param>
    /// <param name="btnStay"></param>
    /// <param name="btnExit"></param>
    public void AddOrRmoveAction(int id, bool isAdd, CommonUsagesType usagesType, Action btnEnter = null, Action btnStay = null, Action btnExit = null)
    {
        switch (id)
        {
            case 0:
                leftEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit);
                rightEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit); break;
            case 1:
                leftEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit); break;
            case 2:
                rightEvent.AddOrRmoveAction(isAdd, usagesType, btnEnter, btnStay, btnExit); break;
            default:
                break;
        }
    }

    /// <summary>
    /// 震动
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="amplitude">振幅 0 - 1</param>
    /// <param name="duration">时长 秒</param>
    public void HapticAction(int id, float amplitude = 0.5f, float duration = 1) //  秒 
    {
        switch (id)
        {
            case 0:
                leftEvent.Haptic(amplitude, duration);
                rightEvent.Haptic(amplitude, duration); break;
            case 1:
                leftEvent.Haptic(amplitude, duration); break;
            case 2:
                rightEvent.Haptic(amplitude, duration); break;
            default:
                break;
        }
    }

    #endregion

    #region 扩展
    /// <summary>
    /// 添加事件
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="usagesType"></param>
    /// <param name="btnEnter"></param>
    /// <param name="btnStay"></param>
    /// <param name="btnExit"></param>
    public void AddAction(int id, CommonUsagesType usagesType, Action btnEnter = null, Action btnStay = null, Action btnExit = null)
    {
        AddOrRmoveAction(id, true, usagesType, btnEnter, btnStay, btnExit);
    }

    /// <summary>
    /// 添加事件
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="usagesType"></param>
    /// <param name="btnEnter"></param>
    /// <param name="btnStay"></param>
    /// <param name="btnExit"></param>
    public void AddAction(int id, CommonUsagesType usagesType, Action<Vector2> btnEnter = null, Action<Vector2> btnStay = null, Action<Vector2> btnExit = null)
    {
        AddOrRmoveAction(id, true, usagesType, btnEnter, btnStay, btnExit);
    }

    /// <summary>
    /// 移除事件
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="usagesType"></param>
    /// <param name="btnEnter"></param>
    /// <param name="btnStay"></param>
    /// <param name="btnExit"></param>
    public void RmoveAction(int id, CommonUsagesType usagesType, Action btnEnter = null, Action btnStay = null, Action btnExit = null)
    {
        AddOrRmoveAction(id, false, usagesType, btnEnter, btnStay, btnExit);
    }

    /// <summary>
    /// 移除事件
    /// </summary>
    /// <param name="id">0:双手 1:左手 2:右手</param>
    /// <param name="usagesType"></param>
    /// <param name="btnEnter"></param>
    /// <param name="btnStay"></param>
    /// <param name="btnExit"></param>
    public void RmoveAction(int id, CommonUsagesType usagesType, Action<Vector2> btnEnter = null, Action<Vector2> btnStay = null, Action<Vector2> btnExit = null)
    {
        AddOrRmoveAction(id, false, usagesType, btnEnter, btnStay, btnExit);
    }
    #endregion

    #region 测试

    void Test()
    {
        AddAction(2, CommonUsagesType.AXButton, null, TestActionStay, TestActionExit);
        AddAction(2, CommonUsagesType.AXButton, TestActionEnter2, TestActionStay2, null);
        AddAction(2, CommonUsagesType.Joy2DAxisMove, TestJoyActionEnter, TestJoyActionStay, TestJoyActionExit);

        Invoke("Test2", 50);
    }
    void Test2()
    {
        Debug.Log("Clear");
        RmoveAction(2, CommonUsagesType.AXButton, null, TestActionStay, TestActionExit);
        RmoveAction(2, CommonUsagesType.AXButton, TestActionEnter2, TestActionStay2, null);
        RmoveAction(2, CommonUsagesType.Joy2DAxisMove, TestJoyActionEnter, TestJoyActionStay, TestJoyActionExit);
    }

    void TestActionEnter2()
    {
        Debug.Log("TestActionEnter2:");
    }

    void TestActionStay2()
    {
        Debug.Log("TestActionStay2:");
    }
    void TestActionExit2()
    {
        Debug.Log("TestActionExit2:");
    }


    void TestActionEnter()
    {
        Debug.Log("TestActionEnter:");
    }

    void TestActionStay()
    {
        Debug.Log("TestActionStay:");
    }

    void TestActionExit()
    {
        Debug.Log("TestActionExit:");
    }

    void TestJoyActionEnter(Vector2 aix)
    {
        Debug.Log("TestJoyActionEnter:" + aix);
    }

    void TestJoyActionStay(Vector2 aix)
    {
        Debug.Log("TestJoyActionStay:" + aix);
    }

    void TestJoyActionExit(Vector2 aix)
    {
        Debug.Log("TestJoyActionExit:" + aix);
    }

    #endregion

    #region  XRRayInteractor

    public RaycastResult GetUIRaycast(int id = 0)
    {
        RaycastResult raycastResult;
        switch (id)
        {
            case 1: leftHandRay.TryGetCurrentUIRaycastResult(out raycastResult); break;
            case 2: rightHandRay.TryGetCurrentUIRaycastResult(out raycastResult); break;
            default:
                leftHandRay.TryGetCurrentUIRaycastResult(out raycastResult);
                if (raycastResult.gameObject != null)
                {

                }
                else
                {
                    rightHandRay.TryGetCurrentUIRaycastResult(out raycastResult);
                    if (raycastResult.gameObject != null)
                    {

                    }
                }
                break;
        }

        return raycastResult;
    }

    public Transform GetRaycast(int id = 0)
    {
        switch (id)
        {
            case 1: return leftHandRay.rayEndTransform; ;
            case 2: return rightHandRay.rayEndTransform;
            default:
                if (leftHandRay.rayEndTransform != null)
                {
                    return leftHandRay.rayEndTransform;
                }
                else if (rightHandRay.rayEndTransform != null)
                {
                    return rightHandRay.rayEndTransform;
                }
                break;
        }

        return null;
    }

    public bool GetRaycast(out Vector3 pos, int mask, int id = 0)
    {
        bool state = false;
        switch (id)
        {
            case 1: state = GetRaycast(leftHandRay.transform, out pos, mask); break;
            case 2: state = GetRaycast(rightHandRay.transform, out pos, mask); break;
            default:
                state = GetRaycast(leftHandRay.transform, out pos, mask);
                if (!state)
                    state = GetRaycast(rightHandRay.transform, out pos, mask); 
                break;
        }
        return state;
    }

    private bool GetRaycast(Transform rayTrans, out Vector3 pos, int mask)
    {
        pos = Vector3.zero;

        if (Physics.Raycast(rayTrans.transform.position, rayTrans.transform.forward, out RaycastHit hit, 1000, mask))
        {
            pos = hit.point;
            return true;
        }
        return false;
    }


    #endregion
}


[Serializable]
public class InputDeviceEvent
{
    #region public event

    public Action onTriggerEnter;
    public Action onTriggerStay;
    public Action onTriggerExit;

    public Action onGripEnter;
    public Action onGripStay;
    public Action onGripUp;

    public Action onAXButtonEnter;
    public Action onAXButtonStay;
    public Action onAXButtonExit;

    public Action onBYButtonEnter;
    public Action onBYButtonStay;
    public Action onBYButonExit;

    public Action onMenuButtonEnter;
    public Action onMenuButtonStay;
    public Action onMenuButtonExit;

    public Action onJoy2DAxisClickEnter;
    public Action onJoy2DAxisClickStay;
    public Action onJoy2DAxisClickExit;

    public Action<Vector2> onJoy2DAxisMoveEnter;
    public Action<Vector2> onJoy2DAxisMoveExit;
    public Action<Vector2> onJoy2DAxisMoveStay;
     
    #endregion

    //提供状态字典独立记录各个feature的状态
    private Dictionary<string, bool> stateDic = new Dictionary<string, bool>();
    private XRNode xRNode;
    private InputDevice inputDevice;

    public InputDeviceEvent()
    {

    }

    public InputDeviceEvent(XRNode xRNode)
    {
        this.xRNode = xRNode;

        Init();
    }

    private void Init()
    {
        if (inputDevice == null || !inputDevice.isValid)
        {
            inputDevice = InputDevices.GetDeviceAtXRNode(xRNode);
        }
    }

    public void Update() //******************每帧轮询监听事件***********************
    {
        if (inputDevice != null && inputDevice.isValid)
        {
            ButtonDispatchModel(inputDevice, CommonUsages.triggerButton, onTriggerEnter, onTriggerStay, onTriggerExit);//扳机
            ButtonDispatchModel(inputDevice, CommonUsages.gripButton, onGripEnter, onGripStay, onGripUp);//抓取
            ButtonDispatchModel(inputDevice, CommonUsages.primaryButton, onAXButtonEnter, onAXButtonStay, onAXButtonExit);//AX
            ButtonDispatchModel(inputDevice, CommonUsages.secondaryButton, onBYButtonEnter, onBYButtonStay, onBYButonExit);//BY

            ButtonDispatchModel(inputDevice, CommonUsages.menuButton, onMenuButtonEnter, onMenuButtonStay, onMenuButtonExit); //菜单 pico右手柄 无效  
            ButtonDispatchModel(inputDevice, CommonUsages.primary2DAxisClick, onJoy2DAxisClickEnter, onJoy2DAxisClickStay, onJoy2DAxisClickExit);//摇杆

            JoyStickDispatchModel(inputDevice, CommonUsages.primary2DAxis, onJoy2DAxisMoveEnter, onJoy2DAxisMoveStay, onJoy2DAxisMoveExit);//摇杆
        }
        else
        {
            Init();
        }
    }

    /// <summary>
    /// 手柄振动
    /// </summary>
    /// <param name="amplitude">振幅为 0 - 1</param>
    /// <param name="duration">振动时长为   秒 </param>
    public void Haptic(float amplitude = 0.5f, float duration = 1) //  秒 
    {
        if (inputDevice != null && inputDevice.isValid)
        {
            bool isTry = inputDevice.TryGetHapticCapabilities(out HapticCapabilities capabilities);
            if (isTry)
            {
                //int channel = 0;
                //uint channel = capabilities.numChannels;
                //int channel = xRNode == XRNode.LeftHand ? 0 : 1; 
                //震动方法,第一个参数为频道,默认0即可,第二个参数是震动频率0-1,第三个参数为震动时长  
                //inputDevice.SendHapticImpulse((uint)channel, amplitude, duration);

                ////震动强度:0.1f、时间2000毫秒(2秒)、震动左手柄还是右手柄(这里是两个都震动) 
                //Unity.XR.PXR.PXR_Input.SetControllerVibration(amplitude, (int)duration * 1000, xRNode == XRNode.LeftHand ? Unity.XR.PXR.PXR_Input.Controller.LeftController : Unity.XR.PXR.PXR_Input.Controller.RightController);


                //编辑器 无反应 发布有效
                //为右手柄开启触觉反馈,振幅为 0.5,振动时长为 500ms,振动频率为 100Hz
                Unity.XR.PXR.PXR_Input.SendHapticImpulse(
                     xRNode == XRNode.LeftHand ? Unity.XR.PXR.PXR_Input.VibrateType.LeftController : Unity.XR.PXR.PXR_Input.VibrateType.RightController
                    , amplitude, (int)(duration * 1000), 100);

                Log("Haptic: " + inputDevice.name);
            }
        }
        else
        {
            Init();
        }
    }

    /// <summary>
    /// 按钮事件源触发模板
    /// </summary>
    /// <param name="device">设备</param>
    /// <param name="usage">功能特征</param>
    /// <param name="btnEnter">开始事件</param>
    /// <param name="btnStay">进行中事件</param>
    /// <param name="btnExit">结束事件</param>
    private void ButtonDispatchModel(InputDevice device, InputFeatureUsage<bool> usage, Action btnEnter, Action btnStay, Action btnExit)
    {
        //为首次执行的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)
        {
            if (!stateDic[featureKey])
            {
                stateDic[featureKey] = true;
                btnEnter?.Invoke();

                Haptic(0.5f, 0.01f);
                Log($"usage Enter: {featureKey}  {btnEnter}");
            }
            btnStay?.Invoke();
            Log($"usage Stay: {featureKey}  {btnStay}");
        }
        else
        {
            if (stateDic[featureKey])
            {
                btnExit?.Invoke();
                stateDic[featureKey] = false;

                Log($"usage Exit: {featureKey}  {btnExit}");
            }
        }
    }

    /// <summary>
    /// 摇杆事件源触发模板
    /// </summary>
    /// <param name="device">设备</param>
    /// <param name="usage">功能特征</param>
    /// <param name="btnEnter">开始事件</param>
    /// <param name="btnStay">进行中事件</param>
    /// <param name="btnExit">结束事件</param>
    private void JoyStickDispatchModel(InputDevice device, InputFeatureUsage<Vector2> usage, Action<Vector2> btnEnter, Action<Vector2> btnStay, Action<Vector2> btnExit)
    {
        //为首次执行的feature添加bool状态 -- 用以判断Enter和Up状态

        string featureKey = $"{device.characteristics}_{usage.name}";
        if (!stateDic.ContainsKey(featureKey))
        {
            stateDic.Add(featureKey, false);
        }
        Vector2 axis;
        bool isDown = device.TryGetFeatureValue(usage, out axis);
        if (isDown && !axis.Equals(Vector2.zero))
        {
            if (!stateDic[featureKey])
            {
                stateDic[featureKey] = true;
                btnEnter?.Invoke(axis);
                Log($"usage Enter: {featureKey}  {btnEnter}  {axis}");
            }


            btnStay?.Invoke(axis);
            Log($"usage Stay: {featureKey}  {btnStay}  {axis}");
        }
        else
        {
            if (stateDic[featureKey])
            {
                btnExit?.Invoke(axis);
                stateDic[featureKey] = false;

                Log($"usage Exit: {featureKey}  {btnExit}  {axis}");
            }
        }
    }

    private void Log(string value)
    {
        Debug.Log(value);
        DebugHelper.instance.Log(value);
    }

    #region 扩展

    public void RmoveActionAll()
    {
        string[] enumNames = Enum.GetNames(typeof(CommonUsagesType));
        int count = enumNames.Length;
        for (int i = 0; i < count; i++)
        {
            RmoveAction((CommonUsagesType)Enum.Parse(typeof(CommonUsagesType), enumNames[i]));
        }

        //foreach (CommonUsagesType item in Enum.GetValues(typeof(CommonUsagesType)))
        //{
        //    RmoveAction(item);
        //}
    }
    public void RmoveAction(CommonUsagesType usagesType)
    {
        switch (usagesType)
        {
            case CommonUsagesType.TriggerButton:

                onTriggerEnter = null;
                onTriggerStay = null;
                onTriggerExit = null;
                break;
            case CommonUsagesType.GripButton:
                onGripEnter = null;
                onGripStay = null;
                onGripUp = null;
                break;
            case CommonUsagesType.AXButton:
                onAXButtonEnter = null;
                onAXButtonStay = null;
                onAXButtonExit = null;
                break;
            case CommonUsagesType.BYButton:
                onBYButtonEnter = null;
                onBYButtonStay = null;
                onBYButonExit = null;
                break;
            case CommonUsagesType.MenuButton:
                onMenuButtonEnter = null;
                onMenuButtonStay = null;
                onMenuButtonExit = null;
                break;
            case CommonUsagesType.Joy2DAxisButton:
                onJoy2DAxisClickEnter = null;
                onJoy2DAxisClickStay = null;
                onJoy2DAxisClickExit = null;
                break;
            case CommonUsagesType.Joy2DAxisMove:
                onJoy2DAxisMoveEnter = null;
                onJoy2DAxisMoveStay = null;
                onJoy2DAxisMoveExit = null;
                break;

            default:
                break;
        }
    }

    public void AddOrRmoveAction(bool isAdd, CommonUsagesType usagesType, Action btnEnter = null, Action btnStay = null, Action btnExit = null)
    {
        switch (usagesType)
        {
            case CommonUsagesType.TriggerButton:
                AddOrRmoveAction(isAdd, ref onTriggerEnter, ref onTriggerStay, ref onTriggerExit, btnEnter, btnStay, btnExit);
                break;
            case CommonUsagesType.GripButton:
                AddOrRmoveAction(isAdd, ref onGripEnter, ref onGripStay, ref onGripUp, btnEnter, btnStay, btnExit);
                break;
            case CommonUsagesType.AXButton:
                AddOrRmoveAction(isAdd, ref onAXButtonEnter, ref onAXButtonStay, ref onAXButtonExit, btnEnter, btnStay, btnExit);
                break;
            case CommonUsagesType.BYButton:
                AddOrRmoveAction(isAdd, ref onBYButtonEnter, ref onBYButtonStay, ref onBYButonExit, btnEnter, btnStay, btnExit);
                break;
            case CommonUsagesType.MenuButton:
                AddOrRmoveAction(isAdd, ref onMenuButtonEnter, ref onMenuButtonStay, ref onMenuButtonExit, btnEnter, btnStay, btnExit);
                break;
            case CommonUsagesType.Joy2DAxisButton:
                AddOrRmoveAction(isAdd, ref onJoy2DAxisClickEnter, ref onJoy2DAxisClickStay, ref onJoy2DAxisClickExit, btnEnter, btnStay, btnExit);
                break;
            default:
                break;
        }
    }
    public void AddOrRmoveAction(bool isAdd, CommonUsagesType usagesType, Action<Vector2> btnEnter = null, Action<Vector2> btnStay = null, Action<Vector2> btnExit = null)
    {
        switch (usagesType)
        {
            case CommonUsagesType.Joy2DAxisMove:
                AddOrRmoveAction(isAdd, ref onJoy2DAxisMoveEnter, ref onJoy2DAxisMoveStay, ref onJoy2DAxisMoveExit, btnEnter, btnStay, btnExit);
                break;
            default:
                break;
        }
    }
    private void AddOrRmoveAction(bool isAdd, ref Action obtnEnter, ref Action obtnStay, ref Action obtnExit, Action btnEnter, Action btnStay, Action btnExit)
    {
        if (isAdd)
        {
            obtnEnter += btnEnter;
            obtnStay += btnStay;
            obtnExit += btnExit;
        }
        else
        {
            obtnEnter -= btnEnter;
            obtnStay -= btnStay;
            obtnExit -= btnExit;
        }
    }
    private void AddOrRmoveAction(bool isAdd, ref Action<Vector2> obtnEnter, ref Action<Vector2> obtnStay, ref Action<Vector2> obtnExit, Action<Vector2> btnEnter, Action<Vector2> btnStay, Action<Vector2> btnExit)
    {
        if (isAdd)
        {
            obtnEnter += btnEnter;
            obtnStay += btnStay;
            obtnExit += btnExit;
        }
        else
        {
            obtnEnter -= btnEnter;
            obtnStay -= btnStay;
            obtnExit -= btnExit;
        }
    }

    #endregion
}

public enum CommonUsagesType
{
    TriggerButton,
    GripButton,
    AXButton,
    BYButton,
    MenuButton,
    Joy2DAxisButton,
    Joy2DAxisMove,
}


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值