Hello,我是KitStar。
以下文章整理的不对。还请见谅。
一.什么是瞬移功能?
瞬移就是使用手柄选取一个位置,通过扳机键进行瞬间移动。
因为HTC Vive活动范围有限制(3m * 5m),所以我们在实际的使用中,为了更好的浏览场景,查看场景,会使用这种移动的方式,达到游戏中的视野移动。
移动的方式有两种:一种是水平移动即X,Z轴移动;另一种就是垂直移动,即Y轴移动。
PS : 我们通过射线检测的方法进行开发,因为HTC Vive有类似的脚本。所以我们只需更改脚本的某些部分就可实现瞬移功能。
技术难点
想要移动玩家的位置,就需要改变游戏中camera物体的位置。而这个位置是受定位系统控制的。
每帧SteamVR_TrackedObject(一个贴在camera和controller上的组件)都会获取新的玩家所戴的HMD在房间中的位置和朝向,然后将camera与之同步。
所以仅仅设置camera.transform.position是不可以的——下一帧它就会被设置回去。
那如何做到移动玩家位置,并且不打断定位系统对camera的同步?答案是CameraRig。
图中的camera (head)就是位置会和HMD同步的camera,它的代表了玩家在游戏中的位置。
注意,他的父物体是CameraRig,通常情况下它的位置和朝向都是归零的,这种情况下camera (head)的位置=HMD的位置;
而如果改变它的位置,玩家的位置就会在camera (head)的本地位置(local position)没有变,可绝对位置会受影响。
比如把cameraRig的y设置为2,玩家就会感觉在空中两米的位置飘浮着。
所以想要瞬移,只要改变CameraRig的位置和朝向即可。
二.案例详解
1.下载Steam VR,导入资源
2.将脚本复制,修改类名(部分脚本)
using UnityEngine; using System.Collections; public class SteamVR_LaserPointer : MonoBehaviour { public bool active = true; public Color color; public float thickness = 0.002f; public GameObject holder; public GameObject pointer; bool isActive = false; public bool addRigidBody = false; public Transform reference; public event PointerEventHandler PointerIn; public event PointerEventHandler PointerOut; public Vector3 HitPoint; Transform previousContact = null; // Use this for initialization void Start () { holder = new GameObject(); holder.transform.parent = this.transform; holder.transform.localPosition = Vector3.zero; holder.transform.localRotation = Quaternion.identity; pointer = GameObject.CreatePrimitive(PrimitiveType.Cube); pointer.transform.parent = holder.transform; pointer.transform.localScale = new Vector3(thickness, thickness, 100f); pointer.transform.localPosition = new Vector3(0f, 0f, 50f); pointer.transform.localRotation = Quaternion.identity; BoxCollider collider = pointer.GetComponent<BoxCollider>(); if (addRigidBody) { if (collider) { collider.isTrigger = true; } Rigidbody rigidBody = pointer.AddComponent<Rigidbody>(); rigidBody.isKinematic = true; } else { if(collider) { Object.Destroy(collider); } } Material newMaterial = new Material(Shader.Find("Unlit/Color")); newMaterial.SetColor("_Color", color); pointer.GetComponent<MeshRenderer>().material = newMaterial; } public virtual void OnPointerIn(PointerEventArgs e) { if (PointerIn != null) PointerIn(this, e); } public virtual void OnPointerOut(PointerEventArgs e) { if (PointerOut != null) PointerOut(this, e); } // Update is called once per frame void Update () { if (!isActive) { isActive = true; this.transform.GetChild(0).gameObject.SetActive(true); } float dist = 100f; SteamVR_TrackedController controller = GetComponent<SteamVR_TrackedController>(); Ray raycast = new Ray(transform.position, transform.forward); RaycastHit hit; bool bHit = Physics.Raycast(raycast, out hit); if(previousContact && previousContact != hit.transform) { PointerEventArgs args = new PointerEventArgs(); if (controller != null) { args.controllerIndex = controller.controllerIndex; } args.distance = 0f; args.flags = 0; args.target = previousContact; OnPointerOut(args); previousContact = null; } if(bHit && previousContact != hit.transform) { PointerEventArgs argsIn = new PointerEventArgs(); if (controller != null) { argsIn.controllerIndex = controller.controllerIndex; } argsIn.distance = hit.distance; argsIn.flags = 0; argsIn.target = hit.transform; OnPointerIn(argsIn); previousContact = hit.transform; } if(!bHit) { previousContact = null; } if (bHit) { HitPoint = hit.point; } if (bHit && hit.distance < 100f) { dist = hit.distance; } if (controller != null && controller.triggerPressed) { pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist); } else { pointer.transform.localScale = new Vector3(thickness, thickness, dist); } pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f); } }
3.添加脚本
4.添加传送脚本
using UnityEngine; using System.Collections; public class SteamVR_LaserPointer : MonoBehaviour { public bool active = true; public Color color; public float thickness = 0.002f; public GameObject holder; public GameObject pointer; bool isActive = false; public bool addRigidBody = false; public Transform reference; public event PointerEventHandler PointerIn; public event PointerEventHandler PointerOut; public Vector3 HitPoint; Transform previousContact = null; // Use this for initialization void Start () { holder = new GameObject(); holder.transform.parent = this.transform; holder.transform.localPosition = Vector3.zero; holder.transform.localRotation = Quaternion.identity; pointer = GameObject.CreatePrimitive(PrimitiveType.Cube); pointer.transform.parent = holder.transform; pointer.transform.localScale = new Vector3(thickness, thickness, 100f); pointer.transform.localPosition = new Vector3(0f, 0f, 50f); pointer.transform.localRotation = Quaternion.identity; BoxCollider collider = pointer.GetComponent<BoxCollider>(); if (addRigidBody) { if (collider) { collider.isTrigger = true; } Rigidbody rigidBody = pointer.AddComponent<Rigidbody>(); rigidBody.isKinematic = true; } else { if(collider) { Object.Destroy(collider); } } Material newMaterial = new Material(Shader.Find("Unlit/Color")); newMaterial.SetColor("_Color", color); pointer.GetComponent<MeshRenderer>().material = newMaterial; } public virtual void OnPointerIn(PointerEventArgs e) { if (PointerIn != null) PointerIn(this, e); } public virtual void OnPointerOut(PointerEventArgs e) { if (PointerOut != null) PointerOut(this, e); } // Update is called once per frame void Update () { if (!isActive) { isActive = true; this.transform.GetChild(0).gameObject.SetActive(true); } float dist = 100f; SteamVR_TrackedController controller = GetComponent<SteamVR_TrackedController>(); Ray raycast = new Ray(transform.position, transform.forward); RaycastHit hit; bool bHit = Physics.Raycast(raycast, out hit); if(previousContact && previousContact != hit.transform) { PointerEventArgs args = new PointerEventArgs(); if (controller != null) { args.controllerIndex = controller.controllerIndex; } args.distance = 0f; args.flags = 0; args.target = previousContact; OnPointerOut(args); previousContact = null; } if(bHit && previousContact != hit.transform) { PointerEventArgs argsIn = new PointerEventArgs(); if (controller != null) { argsIn.controllerIndex = controller.controllerIndex; } argsIn.distance = hit.distance; argsIn.flags = 0; argsIn.target = hit.transform; OnPointerIn(argsIn); previousContact = hit.transform; } if(!bHit) { previousContact = null; } if (bHit) { HitPoint = hit.point; } if (bHit && hit.distance < 100f) { dist = hit.distance; } if (controller != null && controller.triggerPressed) { pointer.transform.localScale = new Vector3(thickness * 5f, thickness * 5f, dist); } else { pointer.transform.localScale = new Vector3(thickness, thickness, dist); } pointer.transform.localPosition = new Vector3(0f, 0f, dist/2f); } }
}