您现在的位置是:首页 >学无止境 >VR Interaction Framework插件的使用(适用于Pico串流)网站首页学无止境

VR Interaction Framework插件的使用(适用于Pico串流)

qq_39858654 2024-06-17 11:26:46
简介VR Interaction Framework插件的使用(适用于Pico串流)

        随着Pico被字节收购,追踪优化变好了,Pico的串流也越来越好用了,现在已经可以用于开发了,现在所用的设备为Pico Neo3消费版,个人感觉的使用感受是,比起上万的HTC Vive设备,几千的Pico Neo3消费版,某种程度来说是更加好用的,现在我要介绍的VR开发框架为VRIF,全称为VR Interaction Framework。

VRIF插件获取:Unity商店

VRIF官网:VRIF Wiki

为什么去学习这个框架呢?

1、它适合用多个平台,基本上来说,一次开发就可以打包到大部分的主流的平台,如:PC VR设备、Pico设备、Oculus设备等。

2、得益于Openxr和XR Interaction Toolkit的一致性,而VRIF又集成了XR Interaction Toolkit输入输出,所以它可以做到串流调式和Pico打包无缝衔接。

3、开发速度快,VRIF已经在案例中实现了很多VR开发的常用功能,只要拿来用就行。

4、作者更新快,目前已更新到了2.0,集成了 FinalIK和PhotonPUN 示例。

5、Unity主推,经测试此框架,适配了Pico Neo3/Pico Neo4系例设备

串流调式和Pico打包无缝衔接的实现基于OpenXR+XR Interaction Toolkit这方案,现在Pico最新版SDK就是此进行开发的,而Openxr和XR Interaction Toolkit这方案也适用于HTC vive和Oculus的串流。

VRIF框架的配置

可以理解为大框架,先导入大框架,然后根据要用到的设备导入相应的Sdk,这样就可以多种设备一次切换,打出多个包。

第一步:创建一个新项目,导入Unity新版本输入系统包

 安装完要重启,Untiy改变输入类型都是要重启的,选Yes.

第二步:导入VRIF2.0包

双击VRIF2.0包导入:

 这样VRIF就导入完成了。

第三步:导入OpenXR和XR Interaction Toolkit插件

打开PackageManager,点击+号,选择Add package by name。然后输入:com.unity.xr.interaction.toolkit

导入XR Interaction Toolkit插件,并更新

 

导入Text Mesh Pro插件

 

 导入默认的动作绑定资产

第四步:完成VRIF串流配置

 修改这两项,第二个一定要选Oculus Touch Controller Profile,在Pico串流的情况手柄才能追踪,选了HTC ViveController Profile会手柄追踪不了的。

设置VRIF的输入输出,也可什么也不勾,一样能正常使用,UI射线也不会有问题。

 

 

好了,现在运行这个XR Demo,用Pico串流就可以走SteamVR平台来流玩这个场景了

勾选了XR Interaction Toolkit,会导致UI射线有问题,则:

 取消勾选后,VRIF的射线就没问题了

第五步:导入PicoSDK,完成项目配置

 去官网下载最新的SDK

 解压并将此包放到项目的Assets同级目录下,防止此包某天被删除,再打开就报错了。

 打开PackageManager,点击+号,选择Add package by disl.....。然后找到此包并导入,导入后Apply一下 

勾选Pico

 

最后,打包 

现在打包出来Pico就可以使用了

打包的不同平台

之前已经打包了Pico平台了,现在打包Oculus设备

安装Oculus XR Plugin插件

 取消勾选Pico,勾选OCulus直接打包就可以了

 打包SteamVR,切换成window平台,直接打包就可以了

VRIF的UI的Bug问题 

当按下按钮后,不抬起来,Button的颜色变不回来 

修改VRIF的VRUISystem的源码

 修改为,下面这样的:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.InputSystem;
using UnityEngine.UI;

namespace BNG
{

    public class VRUISystem : BaseInputModule
    {

        [Header("XR Controller Options : ")]
        [Tooltip("This setting determines if LeftPointerTransform or RightPointerTransform will be used as a forward vector for World Space UI events")]
        public ControllerHand SelectedHand = ControllerHand.Right;

#if XRIT_INTEGRATION
        [Header("XR Interaction Toolkit Options : ")]
        public bool UseXRInteractionToolkitUISystem = true;

#endif

        [Tooltip("A transform on the left controller to use when raycasting for world space UI events")]
        public Transform LeftPointerTransform;

        [Tooltip("A transform on the right controller to use when raycasting for world space UI events")]
        public Transform RightPointerTransform;

        [Tooltip("Controller Binding to use for input down, up, etc.")]
        public List<ControllerBinding> ControllerInput = new List<ControllerBinding>() { ControllerBinding.RightTrigger };

        [Tooltip("Unity Input Action used to simulate a click or touch event")]
        public InputActionReference UIInputAction;

        [Tooltip("If true a PhysicsRaycaster component will be added to the UI camera, allowing physical objects to use IPointer events such as OnPointClick, OnPointEnter, etc.")]
        public bool AddPhysicsRaycaster = false;

        public LayerMask PhysicsRaycasterEventMask;

        [Tooltip("If true the Right Thumbstick will send scroll events to the UI")]
        public bool RightThumbstickScroll = true;

        [Header("Shown for Debug : ")]
        public GameObject PressingObject;
        public GameObject DraggingObject;
        public GameObject ReleasingObject;

        public PointerEventData EventData { get; private set; }

        Camera cameraCaster;

        private GameObject _initialPressObject;
        private bool _lastInputDown;
        bool inputDown;

        private static VRUISystem _instance;
        public static VRUISystem Instance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = GameObject.FindObjectOfType<VRUISystem>();

                    if (_instance == null)
                    {
                        // Check for existing event system
                        EventSystem eventSystem = EventSystem.current;
                        if (eventSystem == null)
                        {
                            eventSystem = new GameObject("EventSystem").AddComponent<EventSystem>(); ;
                        }

                        _instance = eventSystem.gameObject.AddComponent<VRUISystem>();
                    }
                }

                return _instance;
            }
        }

        protected override void Awake()
        {
#if XRIT_INTEGRATION
            if(!UseXRInteractionToolkitUISystem) {
                initEventSystem();
            }
#else
            initEventSystem();
#endif
        }

        protected virtual void initEventSystem()
        {
            UpdateControllerHand(SelectedHand);

            AssignCameraToAllCanvases(cameraCaster);

            EventData = new PointerEventData(eventSystem);
            EventData.position = new Vector2(cameraCaster.pixelWidth / 2, cameraCaster.pixelHeight / 2);
        }

        protected override void Start()
        {
            base.Start();

#if XRIT_INTEGRATION
            if (UseXRInteractionToolkitUISystem) {
                AssignCameraToAllCanvases(Camera.main);

                if (this.gameObject.activeSelf) {
                    // Debug.Log("Diabling VRIF's VRUISystem since XR Interaction Kit UI support is enabled");
                    this.enabled = false;
                }
            }
#else
            AssignCameraToAllCanvases(cameraCaster);
#endif
        }

        void init()
        {
            if (cameraCaster == null)
            {

                // Create the camera required for the caster.
                // We can reduce the fov and disable the camera component for performance
                var go = new GameObject("CameraCaster");
                cameraCaster = go.AddComponent<Camera>();
                cameraCaster.stereoTargetEye = StereoTargetEyeMask.None;
                cameraCaster.fieldOfView = 5f;
                cameraCaster.nearClipPlane = 0.01f;
                cameraCaster.clearFlags = CameraClearFlags.Nothing;
                cameraCaster.enabled = false;

                // Add PhysicsRaycaster so other objects can subscribe to IPointer events
                if (AddPhysicsRaycaster)
                {
                    var pr = go.AddComponent<PhysicsRaycaster>();
                    pr.eventMask = PhysicsRaycasterEventMask;
                }
            }
        }

        public override void Process()
        {
#if XRIT_INTEGRATION
            // XRIT doesn't process here
            if(UseXRInteractionToolkitUISystem == false) {
                DoProcess();
            }
#else
            DoProcess();
#endif
        }

        public void DoProcess()
        {
            // Input isn't ready if this Camera Caster's gameObject isn't active
            if (EventData == null || !CameraCasterReady())
            {
                return;
            }

            EventData.position = new Vector2(cameraCaster.pixelWidth / 2, cameraCaster.pixelHeight / 2);

            eventSystem.RaycastAll(EventData, m_RaycastResultCache);

            EventData.pointerCurrentRaycast = FindFirstRaycast(m_RaycastResultCache);
            m_RaycastResultCache.Clear();

            // Handle Hover
            HandlePointerExitAndEnter(EventData, EventData.pointerCurrentRaycast.gameObject);

            // Handle Drag
            ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.dragHandler);

            // Handle scroll
            if (RightThumbstickScroll)
            {
                EventData.scrollDelta = InputBridge.Instance.RightThumbstickAxis;
                if (!Mathf.Approximately(EventData.scrollDelta.sqrMagnitude, 0))
                {
                    ExecuteEvents.Execute(ExecuteEvents.GetEventHandler<IScrollHandler>(EventData.pointerCurrentRaycast.gameObject), EventData, ExecuteEvents.scrollHandler);
                }
            }

            // Press Events
            inputDown = InputReady();

            // On Trigger Down > TriggerDownValue this frame but not last
            if (inputDown && _lastInputDown == false)
            {
                PressDown();
            }
            // On Held Down
            else if (inputDown)
            {
                //Press();
            }
            // On Release
            else
            {
                Release();
            }

            _lastInputDown = inputDown;
        }

        public virtual bool InputReady()
        {

            // Input isn't ready if this Camera Caster's gameObject isn't active
            if (!CameraCasterReady())
            {
                return false;
            }

            // Check Unity Action
            if (UIInputAction != null && UIInputAction.action.ReadValue<float>() == 1f)
            {
                return true;
            }

            // Check for bound controller button
            for (int x = 0; x < ControllerInput.Count; x++)
            {
                if (InputBridge.Instance.GetControllerBindingValue(ControllerInput[x]))
                {
                    return true;
                }
            }

            return false;
        }


        /// <summary>
        /// Returns true if we have a camera caster enabled and ready to send out data
        /// Returns false if the camera caster is null or it's gameobject is disabled
        /// </summary>
        /// <returns></returns>
        public virtual bool CameraCasterReady()
        {
            if (cameraCaster != null && !cameraCaster.gameObject.activeInHierarchy)
            {
                return false;
            }

            return true;
        }

        public virtual void PressDown()
        {
            EventData.pointerPressRaycast = EventData.pointerCurrentRaycast;

            // Deselect if selection changed
            if (_initialPressObject != null)
            {
                ExecuteEvents.Execute(_initialPressObject, EventData, ExecuteEvents.deselectHandler);
                _initialPressObject = null;
            }

            _initialPressObject = ExecuteEvents.GetEventHandler<IPointerClickHandler>(EventData.pointerPressRaycast.gameObject);

            // Set Press Objects and Events
            SetPressingObject(_initialPressObject);
            ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerDownHandler);

            // Set Drag Objects and Events
            SetDraggingObject(ExecuteEvents.GetEventHandler<IDragHandler>(EventData.pointerPressRaycast.gameObject));
            ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.beginDragHandler);
        }

        public virtual void Press()
        {
            EventData.pointerPressRaycast = EventData.pointerCurrentRaycast;

            // Set Press Objects and Events
            SetPressingObject(ExecuteEvents.GetEventHandler<IPointerClickHandler>(EventData.pointerPressRaycast.gameObject));
            ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerDownHandler);

            // Set Drag Objects and Events
            SetDraggingObject(ExecuteEvents.GetEventHandler<IDragHandler>(EventData.pointerPressRaycast.gameObject));
            ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.beginDragHandler);
        }

        public virtual void Release()
        {
            SetReleasingObject(ExecuteEvents.GetEventHandler<IPointerClickHandler>(EventData.pointerCurrentRaycast.gameObject));
            // Considered a click event if released after an initial click
            if (EventData.pointerPress == ReleasingObject)
            {
                ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerClickHandler);
            }
            ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerUpHandler);
            ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.endDragHandler);
            // Send deselect to
            ExecuteEvents.Execute(ReleasingObject, EventData, ExecuteEvents.deselectHandler);
            ClearAll();
        }

        public virtual void ClearAll()
        {
            SetPressingObject(null);
            SetDraggingObject(null);

            EventData.pointerCurrentRaycast.Clear();
        }

        public virtual void SetPressingObject(GameObject pressing)
        {
            EventData.pointerPress = pressing;
            PressingObject = pressing;
        }

        public virtual void SetDraggingObject(GameObject dragging)
        {
            EventData.pointerDrag = dragging;
            DraggingObject = dragging;
        }

        public virtual void SetReleasingObject(GameObject releasing)
        {
            ReleasingObject = releasing;
        }

        public virtual void AssignCameraToAllCanvases(Camera cam)
        {
            Canvas[] allCanvas = FindObjectsOfType<Canvas>();
            for (int x = 0; x < allCanvas.Length; x++)
            {
                AddCanvasToCamera(allCanvas[x], cam);
            }
        }

        public virtual void AddCanvas(Canvas canvas)
        {
#if XRIT_INTEGRATION
            if(UseXRInteractionToolkitUISystem) {
                AddCanvasToCamera(canvas, Camera.main);

                // Add raycaster if not already present
                canvas.gameObject.AddComponent<UnityEngine.XR.Interaction.Toolkit.UI.TrackedDeviceGraphicRaycaster>();
            }
            else {
                AddCanvasToCamera(canvas, cameraCaster);
            }
#else
            AddCanvasToCamera(canvas, cameraCaster);
#endif
        }

        public virtual void AddCanvasToCamera(Canvas canvas, Camera cam)
        {
            if (cam != null)
            {
                canvas.worldCamera = cam;
            }
        }

        public virtual void UpdateControllerHand(ControllerHand hand)
        {

            // Make sure variables exist
            init();

            // Setup the Transform
            if (hand == ControllerHand.Left && LeftPointerTransform != null)
            {
                cameraCaster.transform.parent = LeftPointerTransform;
                cameraCaster.transform.localPosition = Vector3.zero;
                cameraCaster.transform.localEulerAngles = Vector3.zero;
            }
            else if (hand == ControllerHand.Right && RightPointerTransform != null)
            {
                cameraCaster.transform.parent = RightPointerTransform;
                cameraCaster.transform.localPosition = Vector3.zero;
                cameraCaster.transform.localEulerAngles = Vector3.zero;
            }
        }
    }
}

Button的导航选为None,这个在Andoroid上点用都没有,还会导致UGUI有Bug

这样VRIF的交互与UGUI就行了,射线进入为Highlighted Color,按下为Pressed Color,Selected Color不触发。

 现在Bug就解决了,也实现了一次开发多个平台的分发,也可以串流实时调式了,如果是Pico要应用加密,则在XR Rig Advanced加上PXR Manager脚本就行了。

完结!!!

撒花!!!

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。