zoukankan      html  css  js  c++  java
  • Steam VR的使用(二)

    SteamVR 抛物线移动

    其实实现抛物线很简单,生成一组抛物点,然后将点渲染成线就好。渲染成线有很多方式,你可以用模型,也可以用GL的绘制线段,也可以用LineRender。重点是优化点的生成计算。

    楼主上班比较忙,很少写demo。我做项目一般有严密的框架,但是为了更加简明的为大家展示功能的实现剔除了很多代码,并且写了不符合我风格的代码,就是为了让大家能看清楚功能的实现。

    我不是个人开发者,家中无测试环境,如果有问题可以在下面回复。本教程中的代码已经用在实际的案例上,因为工作保密我不能截图或者共享项目源码,只是把开发过程的部分代码功能以个人的名义分享,欢迎交流。

    如何生成抛物点,一个很简单的公式:

    nextPos = lastPos + 水平位移 垂直位移。

    稍后的代码有详尽的注释我就不多赘述了。

    重点是对点集的优化,优化抛物点分两个部分,一是如何计算碰撞点,二是内存开销的优化。考虑到一般的VR项目只在水平面上进行移动,所以通过判断抛物点的y轴来判定碰撞。关于内存开销,我们可以限定生成的抛物点个数,同时优化计算。我们使用一个List来保存点集,动态的根据点的情况来生成点,或者改变点的位置。例如当手柄角度不变时,只需要将List集合中的点改变Y轴就行了。

    现在思路已经明了了。以下是伪代码:

     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]);
                }
            }
        }
    }
    

      

     

     

     

  • 相关阅读:
    摒弃FORM表单上传图片,异步批量上传照片
    小功能——简单代码实现邮箱发送邮件
    小工具 ——快速生成验证码
    [转]C++11 多线程
    [转]线性插值&双线性插值&三线性插值
    [转]第四章 使用OpenCV探测来至运动的结构——Chapter 4:Exploring Structure from Motion Using OpenCV
    windows的Timer和写文件方式串口注意!
    OPENCV3.1+VS 坑我笔记!
    最简单的PC机串口通信程序
    用MFC时,如果程序崩溃,检查内存,然后注意GDI数量,在任务管理器里选项-查看列-GDI数量
  • 原文地址:https://www.cnblogs.com/jqg-aliang/p/5735177.html
Copyright © 2011-2022 走看看