Steam VR的使用(二)

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);//移除上一個點集的多餘數據

生成拋物線點集後,接下來就是繪製曲線,LineRenderGL均可以很方便的繪製。可是就性能來講,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]);
            }
        }
    }
}
相關文章
相關標籤/搜索