随着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的串流。
可以理解为大框架,先导入大框架,然后根据要用到的设备导入相应的Sdk,这样就可以多种设备一次切换,打出多个包。
安装完要重启,Untiy改变输入类型都是要重启的,选Yes.
双击VRIF2.0包导入:
这样VRIF就导入完成了。
打开PackageManager,点击+号,选择Add package by name。然后输入:com.unity.xr.interaction.toolkit
导入XR Interaction Toolkit插件,并更新
导入Text Mesh Pro插件
修改这两项,第二个一定要选Oculus Touch Controller Profile,在Pico串流的情况手柄才能追踪,选了HTC ViveController Profile会手柄追踪不了的。
设置VRIF的输入输出,也可什么也不勾,一样能正常使用,UI射线也不会有问题。
好了,现在运行这个XR Demo,用Pico串流就可以走SteamVR平台来流玩这个场景了
勾选了XR Interaction Toolkit,会导致UI射线有问题,则:
解压并将此包放到项目的Assets同级目录下,防止此包某天被删除,再打开就报错了。
打开PackageManager,点击+号,选择Add package by disl.....。然后找到此包并导入,导入后Apply一下
勾选Pico
最后,打包
现在打包出来Pico就可以使用了
之前已经打包了Pico平台了,现在打包Oculus设备
安装Oculus XR Plugin插件
取消勾选Pico,勾选OCulus直接打包就可以了
打包SteamVR,切换成window平台,直接打包就可以了
当按下按钮后,不抬起来,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 ControllerInput = new List() { 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();
if (_instance == null)
{
// Check for existing event system
EventSystem eventSystem = EventSystem.current;
if (eventSystem == null)
{
eventSystem = new GameObject("EventSystem").AddComponent(); ;
}
_instance = eventSystem.gameObject.AddComponent();
}
}
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();
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();
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(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() == 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;
}
///
/// 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
///
///
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(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(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(EventData.pointerPressRaycast.gameObject));
ExecuteEvents.Execute(EventData.pointerPress, EventData, ExecuteEvents.pointerDownHandler);
// Set Drag Objects and Events
SetDraggingObject(ExecuteEvents.GetEventHandler(EventData.pointerPressRaycast.gameObject));
ExecuteEvents.Execute(EventData.pointerDrag, EventData, ExecuteEvents.beginDragHandler);
}
public virtual void Release()
{
SetReleasingObject(ExecuteEvents.GetEventHandler(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
Button的导航选为None,这个在Andoroid上点用都没有,还会导致UGUI有Bug
这样VRIF的交互与UGUI就行了,射线进入为Highlighted Color,按下为Pressed Color,Selected Color不触发。
现在Bug就解决了,也实现了一次开发多个平台的分发,也可以串流实时调式了,如果是Pico要应用加密,则在XR Rig Advanced加上PXR Manager脚本就行了。
完结!!!
撒花!!!