查看: 664|回复: 0
打印 上一主题 下一主题

[其他] Unity导弹算法 预计目标点

[复制链接]
may    

8830

主题

81

听众

7万

积分

首席设计师

Rank: 8Rank: 8

纳金币
52336
精华
343

最佳新人 热心会员 灌水之王 活跃会员 突出贡献 荣誉管理 论坛元老

跳转到指定楼层
楼主
发表于 2015-11-30 00:55:26 |只看该作者 |倒序浏览

关于导弹的飞行算法,网上有很多教程。简单算法无非是获取目标点的当前位置,然后导弹朝目标方向移动。高深点的,就是通过计算获取碰撞点然后朝着目标移动。如果你能看懂这个高深算法的话,可以去看原帖:[url=]http://game.ceeger.com/forum/read.php?tid=3919[/url]
需要注意的是,原帖存在错误。而且一些方法使用的不合理。下面是我整合后的代码,欢迎大家提出不同见解。
想要实现导弹的“拦截”功能,首先需要根据目标物体的速度,位置,导弹的速度,位置,计算出两者相交的预计点。然后导弹朝碰撞点移动。
因为目标可能做不规则运动,所以需要公式计算物体的平均速度。即速度=距离/时间。物体的方向,则是当前位置-上一位置。下面是计算物体的速度和方向的具体代码:
  1. using UnityEngine;
  2. using System.Collections;

  3. public class SpeedTest : MonoBehaviour {
  4.     private float lastTime;
  5.     private Vector3 lastPos;
  6.     private float dtime;
  7.     [HideInInspector]
  8.     public Vector3 CurrentVector;
  9.     [HideInInspector]
  10.     public float Speed;
  11.    
  12.     // Update is called once per frame
  13.     void OnEnable() {
  14.         lastTime = Time.time;
  15.         lastPos = transform.position;
  16.     }
  17.     void Update () {
  18.         dtime = Time.time - lastTime;
  19.         if (dtime > 0) {
  20.             lastTime = Time.time;
  21.             
  22.             Speed = PhycisMath.GetSpeed(lastPos, transform.position, dtime);
  23.             CurrentVector = PhycisMath.GetDir(lastPos, transform.position);
  24.             if (Mathf.Abs(Speed)<0.001f)
  25.             {
  26.                 CurrentVector = transform.TransformDirection(Vector3.forward);
  27.             }
  28.             lastPos = transform.position;
  29.         }
  30.     }
  31. }
复制代码
上面是通过位移来算的速度和方向,与物理效果无关,所以拥有更好的适用性,可以用来不规则的平滑运动计算。为了代码的直观,将一些常用的方法封装于一个静态方法类中。
  1. using UnityEngine;
  2. using System.Collections;

  3. public class PhycisMath
  4. {
  5.     public static float GetSpeed(Vector3 lastPos,Vector3 newps,float time) {
  6.         if (time == 0) return 0;
  7.         return Vector3.Distance(lastPos, newPs) / time;
  8.     }
  9.     public static Vector3 GetDir(Vector3 lastPos, Vector3 newPs)
  10.     {
  11.         return (newPs - lastPos).normalized;
  12.     }
  13.     public static float GetDelta(float a,float b,float c) {
  14.         return b * b - 4 * a * c;
  15.     }
  16.     public static float GetRad(float dis, float angle)
  17.     {
  18.         return -(2 * dis * Mathf.Cos(angle * Mathf.Deg2Rad));
  19.     }
  20.     public static float GetPom(float a, float b)
  21.     {
  22.         return 1-Mathf.Pow(a,b);
  23.     }
  24.     public static float GetSqrtOfMath(float a,float b, float d) {
  25.         float a1 = (-b + Mathf.Sqrt(d)) / (2 * a);
  26.         float a2 = (-b - Mathf.Sqrt(d)) / (2 * a);

  27.         return a1>a2?a1:a2;
  28.     }
  29.     public Vector3 GetHitPoint() {
  30.         return Vector3.zero;
  31.     }
  32. }
复制代码
接下来是写一个雷达,通过一系列“复杂”的运算获取碰撞点位置。
  1. using UnityEngine;
  2. using System.Collections;

  3. public class RadarOfRocket : MonoBehaviour {
  4.     //我们的导弹的轨道计算是基于Transform的,
  5.     //纯数学的计算,这样更精确,适用性更好
  6.     public Transform target;//目标
  7.     private SpeedTest rocketSpeed;//
  8.     private SpeedTest targetSpeed;


  9.     private Vector3 targetDir;
  10.     private float angle;
  11.     private float distence;

  12.     private bool isAim = false;

  13.     public bool IsAim
  14.     {
  15.         get { return isAim; }
  16.         set { isAim = value; }
  17.     }
  18.     private Vector3 aimPos;

  19.     public Vector3 AimPos
  20.     {
  21.         get { return aimPos; }
  22.         set { aimPos = value; }
  23.     }
  24.     void checkTarget() {
  25.         if (!(rocketSpeed=GetComponent<SpeedTest>()))
  26.         {
  27.             gameObject.AddComponent<SpeedTest>();
  28.             rocketSpeed = GetComponent<SpeedTest>();
  29.         }
  30.         if (target&&!(targetSpeed = target.GetComponent<SpeedTest>()))
  31.         {
  32.             target.gameObject.AddComponent<SpeedTest>();
  33.             targetSpeed = target.GetComponent<SpeedTest>();
  34.         }
  35.     }
  36.     void OnEnable() {
  37.         

  38.         checkTarget();
  39.     }
  40.     void Update() {
  41.         if (target)
  42.         TestAim();
  43.     }
  44.     public void TestAim() {

  45.         if (Mathf.Abs(targetSpeed.Speed) < 0.01f)
  46.         { //物体的速度过小,则默认物体是静止的。

  47.             isAim = true;
  48.             aimPos = target.position;
  49.         }
  50.         else {
  51.             targetDir = transform.position - target.position;
  52.             angle = Vector3.Angle(targetDir, targetSpeed.CurrentVector);

  53.             distence = targetDir.magnitude;

  54.             float a = PhycisMath.GetPom((rocketSpeed.Speed / targetSpeed.Speed), 2);
  55.             float b = PhycisMath.GetRad(distence, angle);
  56.             float c = distence * distence;
  57.             float d = PhycisMath.GetDelta(a, b, c);
  58.             isAim = d >= 0 && !float.IsNaN(d) && !float.IsInfinity(d);

  59.             if (isAim)
  60.             {
  61.                 float r = PhycisMath.GetSqrtOfMath(a, b, d);
  62.                 if (r < 0) isAim = false;//如果得出的是负值,则代表交点有误
  63.                 aimPos = target.transform.position + targetSpeed.CurrentVector * r;
  64.             }
  65.         
  66.         }



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

好,获取目标点的代码已经完成了。接着是导弹的飞行代码。关于这部分,一般的做法是通过移动+转向实现导弹的轨迹。代码很简单。距离和角度的过滤我就省略了,基本上新手都能写出来。
  1. using UnityEngine;
  2. using System.Collections;
  3. [RequireComponent(typeof(RadarOfRocket))]
  4. public class Missile : MonoBehaviour {
  5.     private RadarOfRocket radar;
  6.     public float Speed = 100;
  7.     public float RoteSpeed = 3;
  8.     public float Noise = 0;
  9.     void OnEnable() {
  10.         radar = GetComponent<RadarOfRocket>();
  11.     }
  12.     void Update() {
  13.         Fly();
  14.         if (radar.IsAim) {
  15.             FlyToTarget(radar.AimPos-transform.position);
  16.         }
  17.     }
  18.     private void FlyToTarget(Vector3 point) {
  19.         if (point != Vector3.zero) {
  20.             Quaternion missileRotation = Quaternion.LookRotation(point, Vector3.up);

  21.             transform.rotation = Quaternion.Slerp(transform.rotation, missileRotation, Time.deltaTime * RoteSpeed);
  22.         }
  23.         
  24.     }
  25.     private void Fly() {
  26.         Move(transform.forward.normalized+transform.right*Mathf.PingPong(Time.time,0.5f)*Noise, Speed*Time.deltaTime);
  27.     }
  28.     public void Move(Vector3 dir,float speed){
  29.         transform.Translate(dir*speed,Space.World);
  30.     }
  31.     void OnTriggerEnter(Collider other)
  32.     {
  33.         print("hit");
  34.     }

  35.    

  36. }
复制代码
考虑到可能有小白拿到代码可能完全不知道怎么弄,故增加以下注释。
将Radar.cs脚本和Missile.cs脚本托给你做好的导弹。然后将目标Transform赋值给Radar的target即可完成拦截导弹的演示。
关于目标怎样的移动,你可以自己定义,像直线移动或者圆周移动,不规则平滑移动等等都可以进行拦截。具体的效果由参数控制,但是一定要注意导弹的速度一定要大于目标的

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

分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏1 支持支持0 反对反对0
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

手机版|纳金网 ( 闽ICP备2021016425号-2/3

GMT+8, 2024-11-14 15:04 , Processed in 0.158427 second(s), 29 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部