Unity 计算导弹抛物线弹道和转向

冰封百度

在三维空间中,利用抛物线公式计算弹道,得到一个发射初速度,让导弹打击到指定地点
效果图:
图片描述

测试脚本:
通过Height调整导弹的飞行高度
通过Gravity调整导弹的飞行速度
图片描述

代码实现

关键代码:

// 求导弹初始速度
public void InitVelocity(Vector3 start, Vector3 end, float height = 10, float gravity = -9.8f)
    float topY = Mathf.Max(start.y, end.y) + height;
    float d1 = topY - start.y;
    float d2 = topY - end.y;
    float g2 = 2 / -gravity;
    float t1 = Mathf.Sqrt(g2 * d1);
    float t2 = Mathf.Sqrt(g2 * d2);
    float t = t1 + t2;
    float vX = (end.x - start.x) / t;
    float vZ = (end.z - start.z) / t;
    float vY = -gravity * t1;
    Vector3 v0 = new Vector3(vX, vY, vZ);
    return v0;
}


// 求下一帧导弹的位置:
public static Vector3 NextPosition(Vector3 position, Vector3 velocity, float gravity, float time) {
    float dY = 0.5f * gravity * time * time;
    return position + velocity * time + new Vector3(0, dY, 0);
}

代码封装:

using UnityEngine;

/// <summary>
/// 抛物线运动轨迹
/// ZhangYu 2019-04-28
/// <param>blog:https://segmentfault.com/a/1190000018336439</param>
/// </summary>
public class ParabolaPath {

    /// <summary> 是否夹紧起点和终点 </summary>
    public bool isClampStartEnd;
    private Vector3 m_start;
    private Vector3 m_end;
    private float m_height;
    private float m_gravity;
    private float m_upTime;
    private float m_downTime;
    private float m_totalTime;
    private Vector3 m_velocityStart;
    private Vector3 m_position;
    private float m_time;

    /// <summary> 初始化抛物线运动轨迹 </summary>
    /// <param name="start">起点</param>
    /// <param name="end">终点</param>
    /// <param name="height">高度(相对于两个点的最高位置 高出多少)</param>
    /// <param name="gravity">重力加速度(负数)</param>
    /// <returns></returns>
    public ParabolaPath(Vector3 start, Vector3 end, float height = 10, float gravity = -9.8f) {
        Init(start, end, height, gravity);
    }

    /// <summary> 初始化抛物线运动轨迹 </summary>
    /// <param name="start">起点</param>
    /// <param name="end">终点</param>
    /// <param name="height">高度(相对于两个点的最高位置 高出多少)</param>
    /// <param name="gravity">重力加速度(负数)</param>
    /// <returns></returns>
    public void Init(Vector3 start, Vector3 end, float height = 10, float gravity = -9.8f) {
        float topY = Mathf.Max(start.y, end.y) + height;
        float d1 = topY - start.y;
        float d2 = topY - end.y;
        float g2 = 2 / -gravity;
        float t1 = Mathf.Sqrt(g2 * d1);
        float t2 = Mathf.Sqrt(g2 * d2);
        float t = t1 + t2;
        float vX = (end.x - start.x) / t;
        float vZ = (end.z - start.z) / t;
        float vY = -gravity * t1;
        m_start = start;
        m_end = end;
        m_height = height;
        m_gravity = gravity;
        m_upTime = t1;
        m_downTime = t2;
        m_totalTime = t;
        m_velocityStart = new Vector3(vX, vY, vZ);
        m_position = m_start;
        m_time = 0;
    }

    /// <summary> 起点 </summary>
    public Vector3 start { get { return m_start; } }
    /// <summary> 终点 </summary>
    public Vector3 end { get { return m_end; } }
    /// <summary> 目标高度 </summary>
    public float height { get { return m_height; } }
    /// <summary> 重力加速度 </summary>
    public float gravity { get { return m_gravity; } }
    /// <summary> 上升时间 </summary>
    public float upTime { get { return m_upTime; } }
    /// <summary> 下降时间 </summary>
    public float downTime { get { return m_downTime; } }
    /// <summary> 总运动时间 </summary>
    public float totalTime { get { return m_totalTime; } }
    /// <summary> 顶点 </summary>
    public Vector3 top { get { return GetPosition(m_upTime); } }
    /// <summary> 初始速度 </summary>
    public Vector3 velocityStart { get { return m_velocityStart; } }
    /// <summary> 当前位置 </summary>
    public Vector3 position { get { return m_position; } }
    /// <summary> 当前速度 </summary>
    public Vector3 velocity { get { return GetVelocity(m_time); } }

    /// <summary> 当前时间 </summary>
    public float time {
        get { return m_time; }
        set {
            if (isClampStartEnd) value = Mathf.Clamp(value, 0, m_totalTime);
            m_time = value;
            m_position = GetPosition(value);
        }
    }

    /// <summary> 获取某个时间点的位置 </summary>
    public Vector3 GetPosition(float time) {
        if (time == 0) return m_start;
        if (time == m_totalTime) return m_end;
        float dY = 0.5f * m_gravity * time * time;
        return m_start + m_velocityStart * time + new Vector3(0, dY, 0);
    }

    /// <summary> 获取某个时间点的速度 </summary>
    public Vector3 GetVelocity(float time) {
        if (time == 0) return m_velocityStart;
        return m_velocityStart + new Vector3(0, m_velocityStart.y + m_gravity * time, 0);
    }

}

测试代码:

using UnityEngine;

/// <summary>
/// 抛物线导弹
/// <para>计算弹道和转向</para>
/// <para>ZhangYu 2019-02-27</para>
/// </summary>
public class Missile : MonoBehaviour {

    public Transform target;        // 目标
    public float height = 16f;      // 高度
    public float gravity = -9.8f;   // 重力加速度
    private ParabolaPath path;      // 抛物线运动轨迹

    private void Start() {
        path = new ParabolaPath(transform.position, target.position, height, gravity);
        path.isClampStartEnd = true;
        transform.LookAt(path.GetPosition(path.time + Time.deltaTime));
    }

    private void Update() {
        // 计算位移
        float t = Time.deltaTime;
        path.time += t;
        transform.position = path.position;

        // 计算转向
        transform.LookAt(path.GetPosition(path.time + t));

        // 简单模拟一下碰撞检测
        if (path.time >= path.totalTime) enabled = false;
    }

}
阅读 10.7k

冰封百度的学习笔记
程序生涯中的技术整理

Unity游戏程序员一枚。生命不息,学习不止。

193 声望
36 粉丝
0 条评论

Unity游戏程序员一枚。生命不息,学习不止。

193 声望
36 粉丝
文章目录
宣传栏