ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【VR开发篇】Unity3D 使用物理公式实现抛物线传送功能

2021-03-22 14:33:44  阅读:553  来源: 互联网

标签:Unity3D Vector3 Mathf private VR 抛物线 tsfTleportRayHost v3CheckVector


【VR开发篇】Unity3D 使用物理公式实现抛物线传送功能

  • 发布时间:2021/03/22
  • 作者:七夜丶

文章目录


前言

  • 在刚接触VR开发的时候,涉及到移动时经常会用到传送,如果用平移的方式移动,可能大部分人连隔夜饭都会吐出来,所以刚开始做VR项目一般程序猿都会选择接入插件的方式开发。
  • 但一般插件的功能繁多,各种挂载,各种选择或填入参数,很麻烦,而且大多数的额外功能都用不上,最主要的就是不可用,一旦出现Bug,很难排查问题所在。
  • 常用到的VR插件有:VRTK、SteamVR Plugin等,一般用起来很鸡肋,所以就自己搞了一套支持HTCvive硬件的驱动模块插件,已经完美支持了四个项目,目前在稳定运行中,此篇文章只介绍传送模块的功能实现,VR驱动模块等后续发布。

一、前期准备

1、引擎及语言

  • 引擎:Unity3D 2019.4.13f1c1
  • 语言:C#
  • 编辑器:VisualStudio2019

2、用到的物理公式

平抛或斜抛运动可分解为:

在这里插入图片描述
> 符号含义

  x − 水 平 方 向 上 移 动 的 距 离 \ x -水平方向上移动的距离  x−水平方向上移动的距离
  y − 竖 直 方 向 上 移 动 的 距 离 \ y -竖直方向上移动的距离  y−竖直方向上移动的距离
  v 0 − 初 速 度 \ v_0 -初速度  v0​−初速度
  θ − 发 射 方 向 与 水 平 面 的 夹 角 角 度 \ θ -发射方向与水平面的夹角角度  θ−发射方向与水平面的夹角角度
  t − 时 间 \ t -时间  t−时间

> 水平方向的匀速直线运动

  • 匀速直线运动公式:
      x = v 0 c o s ( θ ) t \ x = v_0cos(θ)t  x=v0​cos(θ)t

> 竖直方向的变速(加速/减速)直线运动
VR项目中手柄发射的传送射线,分三种情况:

  • 1、斜上抛抛物线;

    • 竖直方向上的速度以-g的加速度逐渐变小,直至为0,然后向下以大小为g的加速度做自由落体运动。
  • 2、斜下抛抛物线;

    • 竖直方向上的速度以-g的加速度逐渐变小,直至为0,然后向下以大小为g的加速度做自由落体运动。
  • 3、平抛抛物线;

    • 以大小为g的加速度做自由落体运动。
  • 变速(减速)直线运动公式:
      y = v 0 s i n ( θ ) t − 1 2 g t 2 \ y = v_0sin(θ)t - {1 \over 2}gt^2  y=v0​sin(θ)t−21​gt2
  • 变速(加速)直线运动公式:
      y = v 0 s i n ( θ ) t + 1 2 g t 2 \ y = v_0sin(θ)t + {1 \over 2}gt^2  y=v0​sin(θ)t+21​gt2
  • 自由落体运动公式:
      y = 1 2 g t 2 \ y = {1 \over 2}gt^2  y=21​gt2

二、核心代码

1.传送射线代码

using UnityEngine;

public class Test : MonoBehaviour
{
    /// <summary> 是否显示Debug辅助线 </summary>
    public bool _bIsShowDebugRay = false;

    /// <summary> 传送射线宿主 </summary>
    public Transform m_tsfTleportRayHost = null;

    /// <summary> 射线渲染组件 </summary>
    public LineRenderer m_lrTeleportRayShowLine = null;
    /// <summary> 射线精度(不可小于2) </summary>
    public int m_tPrecision = 10;
    /// <summary> 抛物线初速度 </summary>
    public float m_fVelocity = 1.0f;
    /// <summary> 模拟地球标准重力加速度 </summary>
    public float m_fG = -9.8f;

    /// <summary> 射线初始发射高度 </summary>
    private float m_fShootHeight = 2f;
    /// <summary> 累计时间 </summary>
    private float m_fAccumulateTime = 0.0f;
    /// <summary> 射线打到地面的最大时长 </summary>
    private float m_fHitLandMaxTime = 0.0f;
    /// <summary> 时间步长 </summary>
    private float m_fTimeStep = 0.0f;
    /// <summary> 夹角 </summary>
    private float m_fTheta = 0.0f;
    /// <summary> 符号 </summary>
    private int m_tSymble = 1;
    /// <summary> 线段使用个数 </summary>
    private int _tSegmentCount = 0;

    /// <summary> 计算得到的抛物线坐标 </summary>
    private Vector3 m_v3CalculatePos = Vector3.zero;
    /// <summary> 上一个检测点坐标 </summary>
    private Vector3 m_v3LastCheckPos = Vector3.zero;
    /// <summary> 当前检测点坐标 </summary>
    private Vector3 m_v3CurCheckPos = Vector3.zero;
    /// <summary> 检测点方向向量 </summary>
    private Vector3 m_v3CheckVector = Vector3.zero;
    /// <summary> 检测点位置坐标数组 </summary>
    private Vector3[] m_v3BrokenNodePos;
    /// <summary> 射线检测数据 </summary>
    private RaycastHit m_raycastHit;

    void Update()
    {
        if (null == m_tsfTleportRayHost)
        {
            return;
        }

        // 精度修正
        m_tPrecision = Mathf.Clamp(m_tPrecision,2,100);
        // 初始赋值
        m_fShootHeight = Mathf.Abs(m_tsfTleportRayHost.position.y);
        m_fHitLandMaxTime = m_fVelocity * Mathf.Sin(m_fTheta) * 2 / Mathf.Abs(m_fG) + Mathf.Sqrt(m_fShootHeight * 2/ Mathf.Abs(m_fG));
        m_fTimeStep = m_fHitLandMaxTime / (m_tPrecision - 1);
        m_fTheta = Vector3.Angle(m_tsfTleportRayHost.forward, Vector3.ProjectOnPlane(m_tsfTleportRayHost.forward, Vector3.up)) * Mathf.Deg2Rad;
        m_tSymble = (m_tsfTleportRayHost.position + m_tsfTleportRayHost.forward).y > m_tsfTleportRayHost.position.y ? 1 : -1;
        m_fAccumulateTime = 0;

      // 自动扩容
        if (null == m_v3BrokenNodePos || m_v3BrokenNodePos.Length != m_tPrecision)
        {
            m_v3BrokenNodePos = new Vector3[m_tPrecision];
        }

        // 计算抛物线轨迹
        for (int i = 0; i < m_tPrecision; i++)
        {
            if (0 == i)
            {
                m_v3LastCheckPos = m_tsfTleportRayHost.position - m_tsfTleportRayHost.forward;
            }

            // 时间T时,平抛距离的计算点坐标
            m_v3CalculatePos.z = m_fVelocity * Mathf.Cos(m_fTheta) * m_fAccumulateTime;
            // 时间T时,竖直上抛距离的计算点坐标
            m_v3CalculatePos.y = m_fVelocity * Mathf.Sin(m_fTheta) * m_fAccumulateTime * m_tSymble + 0.5f * m_fG * m_fAccumulateTime * m_fAccumulateTime;
            // 计算当前检测点坐标
            m_v3CurCheckPos = m_tsfTleportRayHost.position + Quaternion.AngleAxis(m_tsfTleportRayHost.eulerAngles.y, Vector3.up) * m_v3CalculatePos;
            // 计算当前检测向量
            m_v3CheckVector = m_v3CurCheckPos - m_v3LastCheckPos;
            _tSegmentCount = i + 1;

            // 检测其他相交物体
            if (Physics.Raycast(m_v3LastCheckPos, m_v3CheckVector,out m_raycastHit, m_v3CheckVector.magnitude))
            {
                // 打到物体后,最后一段线段使用HitPoint - LastCheckPos
                m_v3CheckVector = m_raycastHit.point - m_v3LastCheckPos;
                m_v3BrokenNodePos[i] = m_raycastHit.point;

                // 辅助线
                TestDebugLine(m_v3LastCheckPos, m_v3CheckVector, Color.green, m_v3CheckVector.magnitude);

                break;
            }
            else
            {
                m_v3BrokenNodePos[i] = m_v3CurCheckPos;

                // 辅助线
                TestDebugLine(m_v3LastCheckPos, m_v3CheckVector, Color.green, m_v3CheckVector.magnitude);
            }

            m_v3LastCheckPos = m_v3CurCheckPos;
            m_fAccumulateTime += m_fTimeStep;
        }

        // 渲染抛物线
        m_lrTeleportRayShowLine.positionCount = _tSegmentCount;
        m_lrTeleportRayShowLine.SetPositions(m_v3BrokenNodePos);
    }

    /// <summary> 测试用的Debug线 </summary>
    private void TestDebugLine(Vector3 _v3Origion, Vector3 _v3Target, Color _color, float _fLength)
    {
        if (!_bIsShowDebugRay)
        {
            return;
        }
#if UNITY_EDITOR
        Debug.DrawRay(_v3Origion, _v3Target, _color, _fLength);
#endif
    }
}

2.实际表现

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


三、总结

  • 以上为VR传送射线的算法,不需要任何其他插件和组件,传送射线的美化和项目接入需要根据不同项目做处理,在此不做过多赘述;
  • 如果有更好的实现方式和表现方式,请多多指点和学习交流~
  • 如果在VR项目开发或者游戏开发中遇到问题,欢迎随时留言交流~会在看到留言的第一时间回复!

标签:Unity3D,Vector3,Mathf,private,VR,抛物线,tsfTleportRayHost,v3CheckVector
来源: https://blog.csdn.net/weixin_43349549/article/details/115067405

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有