提前预测刚体移动轨迹 预测运动轨迹

2023-12-24 06:32:42

一、效果

在这里插入图片描述

二、介绍

通过计算Unity物理系统的运动方位来判断下一步移动的位置,主要用于物体运动的提前预测,通常使用于炮弹发射,机枪发射,足球篮球网球的运动等多种真实运动的物体。

三、脚本

RigidbodyExtension.cs 计算工具类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace Dweiss
{
    public static class RigidbodyExtension
    {
        public static Vector3[] CalculateMovement(this Rigidbody that,
               int stepCount, float timeBeteenStep)
        {
            return that.CalculateMovement(stepCount, timeBeteenStep, Vector3.zero, Vector3.zero);
        }
        public static Vector3[] CalculateMovement(this Rigidbody that,
               int stepCount, float timeBeteenStep, Vector3 addedSpeed)
        {
            return that.CalculateMovement(stepCount, timeBeteenStep, addedSpeed, Vector3.zero);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="that"></param>
        /// <param name="stepCount">Number of steps</param>
        /// <param name="timeBeteenStep">number of frames to skip</param>
        /// <param name="addedSpeed"></param>
        /// <param name="addedForce"></param>
        /// <returns></returns>
        public static Vector3[] CalculateMovement(this Rigidbody that,
               int stepCount, float timeBeteenStep, Vector3 addedSpeed, Vector3 addedForce)
        {


            //var ret = new Vector3[stepCount];

            // var addedV = (addedForce / that.mass) * Time.fixedDeltaTime;
            var v = (that.isKinematic == false ? that.velocity : Vector3.zero);// + addedSpeed + addedV;
            var a = (that.useGravity && that.isKinematic == false ? Physics.gravity : Vector3.zero);
            return CalculateMovement(that.transform.position, v, a, stepCount, timeBeteenStep, addedSpeed, addedForce, that.mass, that.drag);

            //var x = that.transform.position;
            //var calc = new Vector3[] { x, v };
            //for (var i = 0; i < stepCount; ++i)
            //{
            //    calc = CalculateNewPos(calc[0], calc[1], a, that.drag, timeBeteenStep);
            //    ret[i] = calc[0];
            //}
            //return ret;
        }

        public static Vector3[] CalculateMovement(Vector3 position, Vector3 velocity, Vector3 acc, 
               int stepCount, float timeBeteenStep, Vector3 addedSpeed, Vector3 addedForce, float mass, float drag)
        {
            var ret = new Vector3[stepCount];

            var addedV = (addedForce / mass) * Time.fixedDeltaTime;
            var v = velocity + addedSpeed + addedV;
            var a = acc;

            var x = position;
            var calc = new Vector3[] { x, v };
            for (var i = 0; i < stepCount; ++i)
            {
                calc = CalculateNewPos(calc[0], calc[1], a, drag, timeBeteenStep);
                ret[i] = calc[0];
            }
            return ret;
        }

        private static Vector3[] CalculateNewPos(Vector3 x, Vector3 v, Vector3 a, float drag, float deltaTimeCount)
        {
            var dt = Time.fixedDeltaTime;
            var aDt = a * dt;
            var dragDt = 1 - drag * dt;
            dragDt = dragDt < 0 ? 0 : dragDt;
            var acc = .5f * a * dt * dt;
            for (int i = 0; i < deltaTimeCount; ++i)
            {
                v = (v + aDt) * dragDt;
                x = x + v * dt + acc;
            }
            return new Vector3[]{ x, v };
        }


        private static Vector3 CalculateVDrag(Vector3 v, Vector3 a, float drag, int deltaTimeCount)
        {
            var dt = Time.fixedDeltaTime;
            for(int i=0; i < deltaTimeCount; ++i)
                v = (v + a * dt) * (1 - drag * dt);
            return v;
        }

        Doesn't supports 
        public static Vector3[] CalculateTime(this Rigidbody that, Vector3 targetPos)
        {
            return CalculateTime(that, targetPos, Vector3.zero, Vector3.zero);
        }
        public static Vector3[] CalculateTime(this Rigidbody that, Vector3 targetPos,
            Vector3 addedSpeed)
        {
            return CalculateTime(that, targetPos, addedSpeed, Vector3.zero);
        }
        public static Vector3[] CalculateTime(this Rigidbody that, Vector3 targetPos, 
            Vector3 addedSpeed, Vector3 addedForce)
        {

            var addedV = (addedForce / that.mass) * Time.fixedDeltaTime;

            var v = that.velocity + addedSpeed + addedV;

            var a = (that.useGravity && that.isKinematic == false ? Physics.gravity : Vector3.zero);

            var x0 = that.transform.position;
            //x = x0 +vt + .5*a*t^2
            //-b +- SQR(b*b - 4*a*c)/2*a // a= .5*a//b=v//a=x0-x
            //t12 = -v +- SQR(v*v -4 * .5 * a * (x0-x))/ 2 * .5 * a
            var x = x0 - targetPos;
            var sqr = (v.PointMul(v) - 4 * .5f * a.PointMul(x)).Sqrt();
            var t1 = (-v + sqr).PointDiv(2 * .5f * a);
            var t2 = (-v - sqr).PointDiv(2 * .5f * a);

            // a=0: (x0-x) + vt -> t = (x-x0)/v
            var tWhenA0 = x.PointDiv(v);
            //a = 0
            if (float.IsNaN(t1.x)) { t2.x = t1.x = tWhenA0.x; }
            if (float.IsNaN(t1.y)) { t2.y = t1.y = tWhenA0.y; }
            if (float.IsNaN(t1.z)) { t2.z = t1.z = tWhenA0.z; }
            // a = 0 && v = 0
            if (float.IsNaN(t1.x) && x0.x == targetPos.x) { t2.x = t1.x = 0; }
            if (float.IsNaN(t1.y) && x0.y == targetPos.y) { t2.y = t1.y = 0; }
            if (float.IsNaN(t1.z) && x0.z == targetPos.z) { t2.z = t1.z = 0; }
            return new Vector3[] { t1, t2 };
        }

        public static Vector3 Sqrt(this Vector3 v)
        {
            return new Vector3(Mathf.Sqrt(v.x), Mathf.Sqrt(v.y), Mathf.Sqrt(v.z));
        }
        public static Vector3 PointMul(this Vector3 v1, Vector3 v2)
        {
            return new Vector3(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
        }

        public static Vector3 PointDiv(this Vector3 v1, Vector3 v2)
        {
            return new Vector3(v1.x / v2.x, v1.y / v2.y, v1.z / v2.z);
        }

    }
}

DrawLine.cs 画线工具类

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Dweiss;

[RequireComponent(typeof(LineRenderer))]
public class DrawLine : MonoBehaviour {

    /// <summary>
    /// 引导线
    /// </summary>
    public LineRenderer _lr;

    /// <summary>
    /// 刚体
    /// </summary>
    public Rigidbody _rb;

    /// <summary>
    /// 跨度
    /// </summary>
    public float timeBeteenStep = 1;

    /// <summary>
    /// 跨度数量
    /// </summary>
    public int stepCount = 30;
    
    /// <summary>
    /// 加速度
    /// </summary>
    public Vector3 addedV, addedF;

    void Start() {
        
        //找引导线
        _lr = GetComponent<LineRenderer>();
        //找刚体
        _rb = GetComponent<Rigidbody>();
    }

    public void AddPower(Vector3 dir)
    {
        CalcTime();
        _rb.velocity += addedV;
        addedV = Vector3.zero;
        _rb.AddForce(dir, ForceMode.Force);
        addedF = Vector3.zero;
    }


    private void CalcTime()
    {

        Vector3[] t = _rb.CalculateTime(new Vector3(0, 0, 0),
            addedV, addedF);

        var timeT = new Vector3[]{
            new Vector3(Time.time + t[0].x, Time.time + t[0].y, Time.time + t[0].z),
            new Vector3(Time.time + t[1].x, Time.time + t[1].y, Time.time + t[1].z)
        };
    }

    #region 抛物曲线

    /// <summary>
    /// 移动走向曲线
    /// </summary>
    private void DrawMovementLine()
    {
        var res = _rb.CalculateMovement(stepCount, timeBeteenStep, addedV, addedF);
        _lr.positionCount = stepCount + 1;
        _lr.SetPosition(0, transform.position);
        for (int i = 0; i < res.Length; ++i)
        {
            _lr.SetPosition(i + 1, res[i]);
        }
    }

    #endregion

    void Update ()
    {
        DrawMovementLine();

        if (Input.GetKeyDown(KeyCode.W))
        {
            AddPower((Vector3.right + Vector3.up) * 500f);
        }
    }
}

四、资源分享

CSDN下载链接

在我的资源中搜索 RigidBodyExtension

文章来源:https://blog.csdn.net/qq_42194657/article/details/135170931
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。