SteamVR 抛物线移动安全
其实实现抛物线很简单,生成一组抛物点,而后将点渲染成线就好。渲染成线有不少方式,你能够用模型,也能够用GL的绘制线段,也能够用LineRender。重点是优化点的生成计算。框架
楼主上班比较忙,不多写demo。我作项目通常有严密的框架,可是为了更加简明的为你们展现功能的实现剔除了不少代码,而且写了不符合我风格的代码,就是为了让你们能看清楚功能的实现。性能
我不是我的开发者,家中无测试环境,若是有问题能够在下面回复。本教程中的代码已经用在实际的案例上,由于工做保密我不能截图或者共享项目源码,只是把开发过程的部分代码功能以我的的名义分享,欢迎交流。测试
如何生成抛物点,一个很简单的公式:优化
nextPos = lastPos + 水平位移 + 垂直位移。spa
稍后的代码有详尽的注释我就很少赘述了。orm
重点是对点集的优化,优化抛物点分两个部分,一是如何计算碰撞点,二是内存开销的优化。考虑到通常的VR项目只在水平面上进行移动,因此经过判断抛物点的y轴来断定碰撞。关于内存开销,咱们能够限定生成的抛物点个数,同时优化计算。咱们使用一个List来保存点集,动态的根据点的状况来生成点,或者改变点的位置。例如当手柄角度不变时,只须要将List集合中的点改变Y轴就好了。blog
如今思路已经明了了。如下是伪代码:教程
int i = 0;事件
while( nextPos.y>0 && maxPoint>0 ){
if(list.count<=i){
list.add(nextPos);
}
else{
list[i] = nextPos;
}
///生成,优化,计算下一个抛物点
i++;
}
list.remove(i,list.count-i);//移除上一个点集的多余数据
生成抛物线点集后,接下来就是绘制曲线,LineRender,GL均可以很方便的绘制。可是就性能来讲,GL更加快一点。建议你们用GL。固然你们也可使用本身的模型绘制,原理就是,在对应的点生成你的模型,而后计算对应的角度便可。这几种实现方案我都会写到代码中,效果你们本身调用查看。
最后就是生成抛物线的终点,由于我是按照y轴来判断抛物线的落点,可是落点可能有障碍物等等,或者落点在墙角,这些位置显然是不能跳跃的,因此咱们须要对落点进行判断。在Physics中有一个方法能够检测一个球形范围是否会碰撞,咱们能够给地面添加一个层,以便忽略地面的检测,这样就能够安全的着陆了。
好了,也许你没听懂,不要紧,接下来就是代码,代码中也有详尽的注释。
using UnityEngine; using System.Collections.Generic; using System; /// <summary> /// 抛物线脚本 /// </summary> public class HandRay : MonoBehaviour { private Transform CurrentHand; //当前触发的手 private List<Vector3> pointList; //曲线点集合 private Quaternion lastRotation; //上一个移动的角度 private Vector3 lastPostion; //上一个位置 private Vector3 lastPos; //上一个点,为了优化将一个临时变量作成全局的,节省内存开销 private Vector3 nextPos;//下一个点,理由同上 private event Action OnChangeTransform;//一个事件,用来检测手柄位置和角度变化的 private Material material;//渲染射线的材质球 private Vector3 HitPoint;//抛物线的碰撞点 private Ray ray; private bool canJump = false;// public GameObject PointEffect;//一个特效,就是在射线的终点放置一个光柱什么的,你们能够本身作这个特效 public int MaxPoint; //生成曲线的点最大数量 public float Distence;//水平位移 public float Grity;//垂直位移 public float CheckRange;//检测位置是否存在障碍物 public void Awake() { SetData(); } public void Start() { pointList = new List<Vector3>(); OnChangeTransform += OnChangeTransformCallBack; HitPoint = -Vector3.one; ray = new Ray(); } public void Update() { //当手柄按下触摸键同时角度合适时触发事件开始计算点 if (CurrentHand != null && ((CurrentHand.eulerAngles.x > 275 && CurrentHand.eulerAngles.x <= 360) || (CurrentHand.eulerAngles.x >= -0.01f && CurrentHand.eulerAngles.x < 85))) { if (OnChangeTransform != null) OnChangeTransform(); } else { pointList.Clear(); PointEffect.SetActive(false); } } /// <summary> /// 计算抛物线的点 /// 此方法已经优化过性能 /// /// </summary> private void OnChangeTransformCallBack() { if (lastRotation != CurrentHand.rotation || lastPostion != CurrentHand.position) { lastPos = nextPos = CurrentHand.position; int i = 0; while (nextPos.y > 0 && (i < MaxPoint)) { if (pointList.Count <= i) { pointList.Add(nextPos); } else { pointList[i] = nextPos; } if (lastRotation == CurrentHand.rotation && lastPostion != CurrentHand.position && i < pointList.Count - 1) { nextPos = pointList[i + 1] + CurrentHand.position - lastPostion; } else { nextPos = lastPos + CurrentHand.rotation * Vector3.forward * Distence + Vector3.up * Grity * 0.1f * i * Time.fixedDeltaTime; } lastPos = nextPos; i++; } if (pointList.Count > i) { pointList.RemoveRange(i, pointList.Count - i); } lastRotation = CurrentHand.rotation; lastPostion = CurrentHand.position; if (pointList.Count > 1) { HitPoint = pointList[pointList.Count - 1]; PointEffect.SetActive(true); PointEffect.transform.position = HitPoint; } else { HitPoint = -Vector3.one; PointEffect.SetActive(false); } } } public void Enable() { SteamVR_InitManager.Instance.OnLeftDeviceActive += OnHandActive; SteamVR_InitManager.Instance.OnRightDeviceActive += OnHandActive; OnChangeTransform += OnChangeTransformCallBack; } public void OnHandActive(SteamVR_TrackedObject obj) { DeviceInput device = obj.GetComponent<DeviceInput>(); device.OnPressDownPadV3 += OnPressDownPad; device.OnPressUpPad += OnPressUpPadAction; } public void OnHandDis(SteamVR_TrackedObject obj) { if (obj && obj.gameObject.activeSelf) { DeviceInput device = obj.GetComponent<DeviceInput>(); device.OnPressDownPadV3 -= OnPressDownPad; device.OnPressUpPad -= OnPressUpPadAction; } } public void Disable() { SteamVR_InitManager.Instance.OnLeftDeviceActive -= OnHandActive; SteamVR_InitManager.Instance.OnRightDeviceActive -= OnHandActive; OnHandDis(SteamVR_InitManager.Instance.LeftObject); OnHandDis(SteamVR_InitManager.Instance.LeftObject); OnChangeTransform -= OnChangeTransformCallBack; } public void SetData() { if (PointEffect) PointEffect.SetActive(false); } /// <summary> /// 抬起触摸板时,计算落脚点 /// </summary> private void OnPressUpPadAction() { if (CurrentHand == null) return; canJump = true; ray.origin = CurrentHand.position; Vector3 dir = HitPoint - CurrentHand.position; ray.direction = dir; if (Physics.CheckSphere(HitPoint, CheckRange, ~(1 << 8))) { canJump = false; } if (canJump) { JumpPoint(HitPoint); } CurrentHand = null; } /// <summary> /// 跳到指定的点 /// </summary> /// <param name="point"></param> public void JumpPoint(Vector3 point) { point.y = transform.position.y; transform.position = point; } private void OnPressDownPad(Transform parent) { CurrentHand = parent; } /// <summary> /// 使用GL来绘制曲线 /// 将点绘制出来 /// </summary> void OnRenderObject() { material.SetPass(0); if (pointList == null) return; GL.Begin(GL.LINES); for (int i = 0; i < pointList.Count; i++) { GL.Vertex(pointList[i]); } GL.End(); } /// <summary> /// 一个额外的附加方法,即用一个曲线来绘制抛物线,性能较低,由于点数比较多 /// 感兴趣的能够把此方法添加到Update中更新 /// </summary> public void ShowLineByRender() { LineRenderer line = GetComponent<LineRenderer>(); if (line) { line.SetVertexCount(pointList.Count); for (int i = 0; i < pointList.Count; i++) { line.SetPosition(i, pointList[i]); } } } }