效果展示:
一、Pico sdk导入以及环境搭建
本文unity测试版本为2019.4.19,picosdk版本为PicoVR Unity SDK v2.8.11
PICO环境搭建比较简单,这里简要说明两个点:
1、渲染设置
Graphics APIs暂不支持Vulkan,对于OpenGLES2,OpenGLES3,开发者需要按照需求选择。
2、对于API Level的设置要求
Android SDK:API Level23及以上(6.0)
二、瞬移功能的实现
1、插件导入
插件导入之后,会弹出这个提示,让我们获取ID,非商业用途的话,我们直接忽略即可
2、设置预制体参数和添加组件的流程
选择好了之后,我们把sdk里面的两个必要预制体放到场景里面来(PicoMobileSDK->Pvr_UnitySDK->Pvr_UnitySDK以及PicoMobileSDK->Pvr_Controller->ControllerManager),这两个分别是控制VR头显和手柄的显示,并且我们把ControllerManager作为Pvr_UnitySDK的子物体。
此时我们运行,即可在Game视图里面看到VR视角(可以鼠标左键加Alt键模拟控制)
这里我们需要注意的一个点,PICO 的预制体Pvr_UnitySDK的Y轴就是实际体验VR的玩家的高度,我们需要把Pvr_UnitySDK的y轴设置为现实里面人的高度(推荐设置1.6)
接下来我们需要在Pvr_UnitySDK的子物体Event上面添加一个输入控制的组件
然后在Pvr_UnitySDK下面新建空物体命名为HeadControl,空物体下面新建一个精灵命名为HeadSetControl,精灵组件赋值好,并且为HeadSetControl添加组件Pvr_UIPointer,该子物体设置好了先禁用不显示;
然后我们在ControllerManager上添加一个组件Pvr_ControllerDemo,组件里面把必要的三个值赋值好即可:
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),并且将其隐藏,切记不能移除其碰撞体。
这里我个人做了一个动态材质(LineMat插槽上放普通材质也可以),在Capsule下面还放了两个特效光圈,这个是会曲线终点的位置显示的。
至此,我们的瞬移功能就完成了,如果手上没有PICO设备,可以先在电脑上模拟运行下,效果应该如下:
4、发布APK文件,在PICO上面测试吧
有疑问 欢迎vx咨询159-7084-3394