zoukankan      html  css  js  c++  java
  • unity导弹算法 预计目标点

    关于导弹的飞行算法,网上有很多教程。简单算法无非是获取目标点的当前位置,然后导弹朝目标方向移动。高深点的,就是通过计算获取碰撞点然后朝着目标移动。如果你能看懂这个高深算法的话,可以去看原帖:http://game.ceeger.com/forum/read.php?tid=3919

    需要注意的是,原帖存在错误。而且一些方法使用的不合理。下面是我整合后的代码,欢迎大家提出不同见解。

    想要实现导弹的“拦截”功能,首先需要根据目标物体的速度,位置,导弹的速度,位置,计算出两者相交的预计点。然后导弹朝碰撞点移动。

    因为目标可能做不规则运动,所以需要公式计算物体的平均速度。即速度=距离/时间。物体的方向,则是当前位置-上一位置。下面是计算物体的速度和方向的具体代码:

     

    复制代码
    using UnityEngine;
    using System.Collections;
    
    public class SpeedTest : MonoBehaviour {
        private float lastTime;
        private Vector3 lastPos;
        private float dtime;
        [HideInInspector]
        public Vector3 CurrentVector;
        [HideInInspector]
        public float Speed;
        
        // Update is called once per frame
        void OnEnable() {
            lastTime = Time.time;
            lastPos = transform.position;
        }
        void Update () {
            dtime = Time.time - lastTime;
            if (dtime > 0) {
                lastTime = Time.time;
                
                Speed = PhycisMath.GetSpeed(lastPos, transform.position, dtime);
                CurrentVector = PhycisMath.GetDir(lastPos, transform.position);
                if (Mathf.Abs(Speed)<0.001f)
                {
                    CurrentVector = transform.TransformDirection(Vector3.forward);
                }
                lastPos = transform.position;
            }
        }
    }
    复制代码

    上面是通过位移来算的速度和方向,与物理效果无关,所以拥有更好的适用性,可以用来不规则的平滑运动计算。为了代码的直观,将一些常用的方法封装于一个静态方法类中。

    复制代码
    using UnityEngine;
    using System.Collections;
    
    public class PhycisMath
    {
        public static float GetSpeed(Vector3 lastPos,Vector3 newPs,float time) {
            if (time == 0) return 0;
            return Vector3.Distance(lastPos, newPs) / time;
        }
        public static Vector3 GetDir(Vector3 lastPos, Vector3 newPs)
        {
            return (newPs - lastPos).normalized;
        }
        public static float GetDelta(float a,float b,float c) {
            return b * b - 4 * a * c;
        }
        public static float GetRad(float dis, float angle)
        {
            return -(2 * dis * Mathf.Cos(angle * Mathf.Deg2Rad));
        }
        public static float GetPom(float a, float b)
        {
            return 1-Mathf.Pow(a,b);
        }
        public static float GetSqrtOfMath(float a,float b, float d) {
            float a1 = (-b + Mathf.Sqrt(d)) / (2 * a);
            float a2 = (-b - Mathf.Sqrt(d)) / (2 * a);
    
            return a1>a2?a1:a2;
        }
        public Vector3 GetHitPoint() {
            return Vector3.zero;
        }
    }
    复制代码

    接下来是写一个雷达,通过一系列“复杂”的运算获取碰撞点位置。

    复制代码
    using UnityEngine;
    using System.Collections;
    
    public class RadarOfRocket : MonoBehaviour {
        //我们的导弹的轨道计算是基于Transform的,
        //纯数学的计算,这样更精确,适用性更好
        public Transform target;//目标
        private SpeedTest rocketSpeed;//
        private SpeedTest targetSpeed;
    
    
        private Vector3 targetDir;
        private float angle;
        private float distence;
    
        private bool isAim = false;
    
        public bool IsAim
        {
            get { return isAim; }
            set { isAim = value; }
        }
        private Vector3 aimPos;
    
        public Vector3 AimPos
        {
            get { return aimPos; }
            set { aimPos = value; }
        }
        void checkTarget() {
            if (!(rocketSpeed=GetComponent<SpeedTest>()))
            {
                gameObject.AddComponent<SpeedTest>();
                rocketSpeed = GetComponent<SpeedTest>();
            }
            if (target&&!(targetSpeed = target.GetComponent<SpeedTest>()))
            {
                target.gameObject.AddComponent<SpeedTest>();
                targetSpeed = target.GetComponent<SpeedTest>();
            }
        }
        void OnEnable() {
            
    
            checkTarget();
        }
        void Update() {
            if (target)
            TestAim();
        }
        public void TestAim() {
    
            if (Mathf.Abs(targetSpeed.Speed) < 0.01f)
            { //物体的速度过小,则默认物体是静止的。
    
                isAim = true;
                aimPos = target.position;
            }
            else {
                targetDir = transform.position - target.position;
                angle = Vector3.Angle(targetDir, targetSpeed.CurrentVector);
    
                distence = targetDir.magnitude;
    
                float a = PhycisMath.GetPom((rocketSpeed.Speed / targetSpeed.Speed), 2);
                float b = PhycisMath.GetRad(distence, angle);
                float c = distence * distence;
                float d = PhycisMath.GetDelta(a, b, c);
                isAim = d >= 0 && !float.IsNaN(d) && !float.IsInfinity(d);
    
                if (isAim)
                {
                    float r = PhycisMath.GetSqrtOfMath(a, b, d);
                    if (r < 0) isAim = false;//如果得出的是负值,则代表交点有误
                    aimPos = target.transform.position + targetSpeed.CurrentVector * r;
                }
            
            }
    
    
    
            
        }
    }
    复制代码

    原博客中的解是获取较小的那个。但是据我测试,只有正解时目标点才正确。大家也可以进行测试。值得注意的是,导弹的速度必须要大于目标的速度,不然导弹无法靠近目标。

    好,获取目标点的代码已经完成了。接着是导弹的飞行代码。关于这部分,一般的做法是通过移动+转向实现导弹的轨迹。代码很简单。距离和角度的过滤我就省略了,基本上新手都能写出来。

     

    复制代码
    using UnityEngine;
    using System.Collections;
    [RequireComponent(typeof(RadarOfRocket))]
    public class Missile : MonoBehaviour {
        private RadarOfRocket radar;
        public float Speed = 100;
        public float RoteSpeed = 3;
        public float Noise = 0;
        void OnEnable() {
            radar = GetComponent<RadarOfRocket>();
        }
        void Update() {
            Fly();
            if (radar.IsAim) {
                FlyToTarget(radar.AimPos-transform.position);
            }
        }
        private void FlyToTarget(Vector3 point) {
            if (point != Vector3.zero) {
                Quaternion missileRotation = Quaternion.LookRotation(point, Vector3.up);
    
                transform.rotation = Quaternion.Slerp(transform.rotation, missileRotation, Time.deltaTime * RoteSpeed);
            }
            
        }
        private void Fly() {
            Move(transform.forward.normalized+transform.right*Mathf.PingPong(Time.time,0.5f)*Noise, Speed*Time.deltaTime);
        }
        public void Move(Vector3 dir,float speed){
            transform.Translate(dir*speed,Space.World);
        }
        void OnTriggerEnter(Collider other)
        {
            print("hit");
        }
    
       
    
    }
    复制代码

    好,上面就是导弹的所有代码了。谢谢观看。

     

    考虑到可能有小白拿到代码可能完全不知道怎么弄,故增加以下注释。

    将Radar.cs脚本和Missile.cs脚本托给你做好的导弹。然后将目标Transform赋值给Radar的target即可完成拦截导弹的演示。

    关于目标怎样的移动,你可以自己定义,像直线移动或者圆周移动,不规则平滑移动等等都可以进行拦截。具体的效果由参数控制,但是一定要注意导弹的速度一定要大于目标的

    速度,拦截导弹才能发挥作用!

    本文的链接是:http://www.cnblogs.com/jqg-aliang/p/4768101.html 转载请申明出处谢谢!

  • 相关阅读:
    DC综合流程
    DC set_tcl脚本配置
    同步FIFO设计
    顺序脉冲 发生器
    状态机的写法
    verilog串并转换
    indexOf()
    jQuery 效果
    jQuery 事件
    jQuery css
  • 原文地址:https://www.cnblogs.com/backlighting/p/5276173.html
Copyright © 2011-2022 走看看