PicoXR中的手柄射线

前言

本文是继PicoXR中的输入事件博客的进一步补充,针对手柄的射线相关API进行解析,并以VR水果忍者为例子封装手柄射线的事件。

手柄射线的获取

unity 连接手柄_c#

XRRayInteractor leftInteractor; //以左手的射线交互器为例
//省略获取脚本的方法,根据名称标签等根据项目而定
public void RayTest()
{
    //接收out输出
	RaycastHit rayInfo;
    //获取左手当前的射线结果RaycastHit
    leftInteractor.GetCurrentRaycastHit(out rayInfo);
    //后续就可以通过rayInfo获取射线击中的碰撞体等等操作
    if(rayInfo.collider != null){}
    
    
    //还有一个方法,直接尝试获取各个射线击中的信息,不如上述方法方便
    Vector3 position = new Vector3();  //击中点的坐标
    Vector3 normal = new Vector3();    //击中点的法线向量
    int positionInLine = 0;
    bool isValidTarget = false; //是否是有效的目标 击中目标是否是可交互的
    //尝试获取击中点的若干信息,若击中返回true 若未击中发回false
    leftInteractor.TryGetHitInfo(ref position,ref normal,ref positionInLine,ref isValidTarget);
    //注:新版Unity XR 的API中ref的形式已经由out代替
}

射线使用举例

在VR水果忍者中,非常重要的一点就是判断水果的切割方向,然后根据切割方向播放相应的特效和水果裂开的模型。可以考虑使用射线检测,碰到水果时产生切割。仅仅靠射线肯定无法知道切割的方向,所以我们还需知道手柄在切割时的挥动方向

下面以Pico neo3的Unity XR SDK为例,介绍其PXR_Input的相关方法,有关更具体的脚本用法,可以参考Pico的官方文档。Documentation - Pico开发者平台 (pico-interactive.com)

//预测控制器在0.1秒后的位置
Vector3 targetPos = PXR_Input.GetControllerPredictPosition
                    (Controller.LeftController, 0.1f);
//知道此位置后 和当前位置做差就可以得到切割方向的方向向量了
//获取手柄挥动方向向量
Vector3 predictDir = targetPos - leftInteractor.transform.position;

根据上面的挥动向量配合射线检测,就能实现切割的输入事件了,但是如果直接使用此逻辑去判断,势必会造成输入和逻辑的紧耦合,所以这里也应该将射线检测封装成事件,供外部注册,采用观察者模式进行。

由于射线检测传递的数据一般不只一项,这里采用C#提供的EventHandler委托配合事件参数类来封装左右手柄控制器的射线切割事件,详细源码如下:

namespace VRFruit.InputSystem
{
    //事件数据类
	public class RayEventArgs : EventArgs
	{
		/// <summary>
		/// 射线信息
		/// </summary>
		public RaycastHit rayInfo;

		/// <summary>
		/// 预测挥动方向向量
		/// </summary>
		public Vector3 predictDir;

		public RayEventArgs(RaycastHit rayInfo,Vector3 predictDir)
        {
			this.rayInfo = rayInfo;
			this.predictDir = predictDir;
        }
	}
}


namespace VRFruit.InputSystem
{
	/// <summary>
	/// 提供输入事件 事件源
	/// </summary>
	public class InputEventCenter : MonoBehaviour
	{
        #region 公开事件

        /// <summary>
        /// 左手柄射线击中到Interactive物体事件
        /// </summary>
        public event EventHandler<RayEventArgs> onCutInteractionObjectL;

        /// <summary>
        /// 右手柄射线击中到Interactive物体事件
        /// </summary>
        public event EventHandler<RayEventArgs> onCutInteractionObjectR;

        #endregion


        /*      属性      */
        XRRayInteractor leftInteractor;
        XRRayInteractor rightInteractor;

        private void Awake()
        {
            leftInteractor = transform.
                FindChildByName(InputVariables.LEFTHAND_CONTROLLER_NAME).GetComponent<XRRayInteractor>();
            rightInteractor = transform.
                FindChildByName(InputVariables.RIGHTHAND_CONTROLLER_NAME).GetComponent<XRRayInteractor>();

        }

        private void Update()
        {
            CutInteractionObjectL();
            CutInteractionObjectR();
        }

        private void CutInteractionObjectL()
        {
            RaycastHit rayInfo;
            leftInteractor.GetCurrentRaycastHit(out rayInfo);

            if (rayInfo.collider != null && rayInfo.collider.tag == InputVariables.INTERACTION_TAG)
            {
                //预测手柄固定时间后的位置
                Vector3 targetPos = PXR_Input.GetControllerPredictPosition
                    (Controller.LeftController, InputVariables.PREDICT_TIME);
                //获取手柄挥动方向向量
                Vector3 predictDir = targetPos - leftInteractor.transform.position;
                //封装事件参数
                RayEventArgs args = new RayEventArgs(rayInfo, predictDir);

                if(onCutInteractionObjectL!=null)
                    onCutInteractionObjectL(null,args);
            }
        }
        private void CutInteractionObjectR()
        {
            RaycastHit rayInfo;
            rightInteractor.GetCurrentRaycastHit(out rayInfo);
            if (rayInfo.collider != null && rayInfo.collider.tag == InputVariables.INTERACTION_TAG)
            {
                //预测手柄固定时间后的位置
                Vector3 targetPos = PXR_Input.GetControllerPredictPosition
                    (Controller.RightController, InputVariables.PREDICT_TIME);
                //获取手柄挥动方向向量
                Vector3 predictDir = targetPos - rightInteractor.transform.position;
                //封装事件参数
                RayEventArgs args = new RayEventArgs(rayInfo, predictDir);

                if(onCutInteractionObjectR!=null)
                    onCutInteractionObjectR(null, args);
            }
        }
    }
}