Pico Neo3 通过unity实现VR手柄瞬移功能

效果展示:

一、Pico sdk导入以及环境搭建

本文unity测试版本为2019.4.19,picosdk版本为PicoVR Unity SDK v2.8.11

PICO环境搭建比较简单,这里简要说明两个点:

1、渲染设置
Graphics APIs暂不支持Vulkan,对于OpenGLES2,OpenGLES3,开发者需要按照需求选择。

Pico Neo3 通过unity实现VR手柄瞬移功能_第1张图片

2、对于API Level的设置要求
Android SDK:API Level23及以上(6.0)

Pico Neo3 通过unity实现VR手柄瞬移功能_第2张图片

二、瞬移功能的实现

1、插件导入

插件导入之后,会弹出这个提示,让我们获取ID,非商业用途的话,我们直接忽略即可

Pico Neo3 通过unity实现VR手柄瞬移功能_第3张图片

2、设置预制体参数和添加组件的流程

选择好了之后,我们把sdk里面的两个必要预制体放到场景里面来(PicoMobileSDK->Pvr_UnitySDK->Pvr_UnitySDK以及PicoMobileSDK->Pvr_Controller->ControllerManager),这两个分别是控制VR头显和手柄的显示,并且我们把ControllerManager作为Pvr_UnitySDK的子物体。

Pico Neo3 通过unity实现VR手柄瞬移功能_第4张图片

此时我们运行,即可在Game视图里面看到VR视角(可以鼠标左键加Alt键模拟控制)

Pico Neo3 通过unity实现VR手柄瞬移功能_第5张图片

这里我们需要注意的一个点,PICO 的预制体Pvr_UnitySDK的Y轴就是实际体验VR的玩家的高度,我们需要把Pvr_UnitySDK的y轴设置为现实里面人的高度(推荐设置1.6)

Pico Neo3 通过unity实现VR手柄瞬移功能_第6张图片

接下来我们需要在Pvr_UnitySDK的子物体Event上面添加一个输入控制的组件

Pico Neo3 通过unity实现VR手柄瞬移功能_第7张图片

然后在Pvr_UnitySDK下面新建空物体命名为HeadControl,空物体下面新建一个精灵命名为HeadSetControl,精灵组件赋值好,并且为HeadSetControl添加组件Pvr_UIPointer,该子物体设置好了先禁用不显示;

Pico Neo3 通过unity实现VR手柄瞬移功能_第8张图片
Pico Neo3 通过unity实现VR手柄瞬移功能_第9张图片
然后我们在ControllerManager上添加一个组件Pvr_ControllerDemo,组件里面把必要的三个值赋值好即可:

Pico Neo3 通过unity实现VR手柄瞬移功能_第10张图片

3、需要写一个瞬移功能实现的脚本

坚持一下最后一步了,我们需要新建一个空物体命名Teleport,挂上脚本Teleport,脚本如下:

using Pvr_UnitySDKAPI;
using System.Collections;
using UnityEngine;

public class Teleport : MonoBehaviour
{
    public static Pvr_KeyCode TOUCHPAD = Pvr_KeyCode.TOUCHPAD;
    public static Pvr_KeyCode TRIGGER = Pvr_KeyCode.TRIGGER;
    public float fadeTime = 0.2f;
    public bool IsBezierCurve = false;
    public bool IsScreenFade = false;
    public Material LineMat;
    public GameObject PointGo;
    public Material PointGoMat;
    private GameObject cube;
    private GameObject currentController = null;
    private Vector3 currentHitPoint = Vector3.zero;
    private Color fadeColor = new Color(0.9f, 0.9f, 0.9f, 0f);
    private Material fademat;

    private LineRenderer line;
    private Ray ray;
    private GameObject sdkManagerGo;

    public GameObject CurrentController
    {
        get
        {
            if (currentController == null)
                currentController = FindObjectOfType<Pvr_ControllerDemo>().currentController;
            return currentController;
        }
    }

    public static Vector3[] GetBeizerPathPointList(Vector3 startPoint, Vector3 controlPoint, Vector3 endPoint, int pointNum)
    {
        Vector3[] BeizerPathPointList = new Vector3[pointNum];
        for (int i = 1; i <= pointNum; i++)
        {
            float t = i / (float)pointNum;
            Vector3 point = GetBeizerPathPoint(t, startPoint,
                controlPoint, endPoint);
            BeizerPathPointList[i - 1] = point;
        }
        return BeizerPathPointList;
    }

    private static Vector3 GetBeizerPathPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2)
    {
        return (1 - t) * (1 - t) * p0 + 2 * t * (1 - t) * p1 + t * t * p2;
    }

    private static bool GetTeleportKey()
    {
        return Controller.UPvr_GetKey(0, TOUCHPAD) ||
            Controller.UPvr_GetKey(1, TOUCHPAD) ||
            Controller.UPvr_GetKey(0, TRIGGER) ||
            Controller.UPvr_GetKey(1, TRIGGER) ||
            Input.GetMouseButton(0);
    }

    private static bool GetTeleportKeyUp()
    {
        return Controller.UPvr_GetKeyUp(0, TOUCHPAD) ||
            Controller.UPvr_GetKeyUp(1, TOUCHPAD) ||
            Controller.UPvr_GetKeyUp(0, TRIGGER) ||
            Controller.UPvr_GetKeyUp(1, TRIGGER) ||
            Input.GetMouseButtonUp(0);
    }

    private void DrawLine()
    {
        Vector3 startPoint = CurrentController.transform.Find("start").position;
        Vector3 endPoint = CurrentController.transform.Find("dot").position;
        Vector3 controllerPoint = CurrentController.transform.Find("controller").position;
        if (!IsBezierCurve)
        {
            line.positionCount = 2;
            line.SetPosition(0, startPoint);
            line.SetPosition(1, endPoint);
        }
        else
        {
            float distance = Vector3.Distance(startPoint, endPoint);
            Vector3 controlPoint = startPoint + (startPoint - controllerPoint).normalized * distance / 1.6f;

            Vector3[] bcList = GetBeizerPathPointList(startPoint, controlPoint, endPoint, 30);
            line.positionCount = bcList.Length + 1;
            line.SetPosition(0, startPoint);
            for (int i = 0; i < bcList.Length; i++)
            {
                Vector3 v = bcList[i];
                line.SetPosition(i + 1, v);
            }
        }
    }

    private bool HitFloor(ref RaycastHit hit)
    {
        return 1 << hit.transform.gameObject.layer == LayerMask.GetMask("TransparentFX");
    }

    private void LineInit()
    {
        if (GetComponent<LineRenderer>())
            line = GetComponent<LineRenderer>();
        else
            line = gameObject.AddComponent<LineRenderer>();
        line.material = LineMat;
        line.startWidth = 0.02f;
        line.numCapVertices = 5;
    }

    private void MoveCameraPrefab(Vector3 target)
    {
        if (GetTeleportKeyUp())
        {
            if (IsScreenFade)
                StartCoroutine(ScreenFade(target));
            else
                sdkManagerGo.transform.position = new Vector3(target.x, target.y + 1.67f, target.z);
        }
    }

    private IEnumerator ScreenFade(Vector3 target)
    {
        float ShowTimer = 0.0f;
        float HideTimer = 0.0f;
        fademat.color = fadeColor;
        cube.SetActive(true);
        Color color = fadeColor;
        while (ShowTimer < fadeTime)
        {
            yield return new WaitForEndOfFrame();
            ShowTimer += Time.deltaTime;
            color.a = Mathf.Clamp01(ShowTimer / fadeTime);
            if (color.a > 0.8f)
                break;
            fademat.color = color;
        }
        sdkManagerGo.transform.position = new Vector3(target.x, target.y + 1.67f, target.z);
        while (HideTimer < fadeTime)
        {
            yield return new WaitForEndOfFrame();
            HideTimer += Time.deltaTime;
            color.a = 0.8f - Mathf.Clamp01(HideTimer / fadeTime);
            if (color.a < 0.01f)
                break;
            fademat.color = color;
        }
        cube.SetActive(false);
    }

    private void Start()
    {
        LineInit();
        sdkManagerGo = FindObjectOfType<Pvr_UnitySDKManager>().gameObject;

        fademat = new Material(Shader.Find("Sprites/Default"));
        cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
        cube.GetComponent<MeshRenderer>().material = fademat;
        cube.transform.position = sdkManagerGo.transform.position;
        cube.transform.parent = sdkManagerGo.transform;
        cube.SetActive(false);

        if (PointGoMat != null)
            PointGo.GetComponent<MeshRenderer>().material = PointGoMat;
        PointGo.SetActive(false);

        ray = new Ray();
    }

    // Update is called once per frame

    private void Update()
    {
        if (CurrentController != null && GetTeleportKey())
        {
            line.enabled = true;
            //sdkManagerGo = currentController.transform.parent.gameObject;
            ray.direction = CurrentController.transform.Find("dot").position - CurrentController.transform.Find("start").position;
            ray.origin = CurrentController.transform.Find("start").position;

            RaycastHit hit;
            if (Physics.Raycast(ray, out hit))
            {
                currentHitPoint = hit.point;
                if (HitFloor(ref hit) && hit.point != null)
                {
                    PointGo.transform.position = hit.point;

                    PointGo.SetActive(true);
                    //CurrentController.transform.Find("dot").position = hit.point;
                }
            }
            else { PointGo.SetActive(false); }

            DrawLine();
        }
        else
        {
            if (currentHitPoint != Vector3.zero)
            {
                if (PointGo.activeInHierarchy)
                {
                    MoveCameraPrefab(currentHitPoint);

                    currentHitPoint = Vector3.zero;
                    PointGo.SetActive(false);
                }
            }

            if (line.enabled == false)
                return;
            line.enabled = false;
        }
    }
}

脚本写完之后sdk里面的一个脚本会有一个报错,只需将Pvr_ControllerDemo脚本中的currentController变量设为public类型的即可。
然后,再创建一个Capsule作为Point的子物体,建议将其Scale设为(0.1,0.01,0.1),并且将其隐藏,切记不能移除其碰撞体。

Pico Neo3 通过unity实现VR手柄瞬移功能_第11张图片

这里我个人做了一个动态材质(LineMat插槽上放普通材质也可以),在Capsule下面还放了两个特效光圈,这个是会曲线终点的位置显示的。

至此,我们的瞬移功能就完成了,如果手上没有PICO设备,可以先在电脑上模拟运行下,效果应该如下:

Pico Neo3 通过unity实现VR手柄瞬移功能_第12张图片

4、发布APK文件,在PICO上面测试吧

有疑问 欢迎vx咨询159-7084-3394

你可能感兴趣的:(Pico学习,关于VR方向,各类插件的使用,vr,unity,游戏引擎)