Unity制作简单拦截近防炮——如何预测打击目标

突然想尝试一下在Unity中模拟近防炮(拦截炮),该拦截跑应该发射物理子弹并并命中处在运动中的物体,所以在代码中至少应考虑:

  1. 子弹发射矢量速度vb(暂不考虑风阻)
  2. 目标距离 L
  3. 目标当前矢量速度 v
  4. 目标运动加速度 a
  5. 重力加速度 g

有以上这些变量后就可以运用初高中知识来预判目标,并得到良好的命中率:以下算法目前只适用于匀速直线运动和匀变速直线运动。


首先是制作大炮和发射部分。这里简单地将圆柱体或者立方体拉成炮管,后期再替换成正经模型:

然后创建一个Prefab物体用作炮弹,什么形状都行,但是要有Rigibody,阻力调为0;炮管可以不用设置碰撞体,如果一定要碰撞体,那么就用以下方法来避免炮弹和炮管发生碰撞,将炮管和炮弹分别设置不同Layer:

Physics.IgnoreLayerCollisionhttps://docs.unity3d.com/ScriptReference/Physics.IgnoreLayerCollision.html

接下来就是发射炮弹,用StartCoroutine()接口来让大炮不间断地连射,发射频率可以实时更改:

using System.Collections;
using UnityEngine;

public class Interceptor : MonoBehaviour
{
    public Transform TargetObj, PredictedObj;//assigin target 
    public GameObject bullet;// bullet prefab
    public float FiringRate = .3f, bulletSpeed = 10f, towardSpeed = 1f, gravityLead = 0.5f, accurancy = 0.5f;

    public float flyingTime, distance, multiplyler, num_bullets_pertime = 5;
    [SerializeField] Rigidbody TargetRig;
    [SerializeField] Vector3 lastVelocity, acceleration;


    void Start()
    {
        TargetRig = TargetObj.GetComponent<Rigidbody>();
        StartCoroutine(Shoot());// start shooting
    }

    
    IEnumerator Shoot()
    {
        while (TargetObj)//如果有瞄准的物体,那么就一直发射!
        {
            //  for (int i = 0; i <= num_bullets_pertime; i++) { Instantiate(bullet, transform.position, transform.rotation).GetComponent<Rigidbody>().velocity = transform.forward * bulletSpeed * Random.Range(0.1f, 2f); }
            Instantiate(bullet, transform.position, transform.rotation).GetComponent<Rigidbody>().velocity = transform.forward * bulletSpeed;
            yield return new WaitForSeconds(FiringRate);

        }
    }
}

接下来是核心部分,为了直观展现大炮预瞄位置,这里可以创建一个鲜艳的物体来表示,如上图中的红标,并将其绑定到 PredictedObj 变量上。只要将PredictedObj 的位置计算出来,让大炮直接调用LookAt 方法 或者 在操作面板中添加LookAt Constrant 锁定到该物体就可以实现简单的拦截打击了。

要预测PredictedObj 的位置,可以直接运用初高中物理知识:但是这里需要注意的是,最终采用的这种方法并不严谨,因为子弹在飞行的过程中距离 Distance 是会改变的, 因为物体在运动,这就涉及到动态预测——即在三维坐标系中,已知之前的1~5条件,还要加上:

6.  子弹发射时刻位置

7.  目标发射时刻位置

然后用两点距离公式和运动学公式(匀速和匀加速)连列来求解一个繁杂的二元一次方程,进而得出撞击前需要的未知时间T。

我直接裂开🤯,所以暂时不考虑方程求解部分,粗略的将未知T 变为:

  • \Delta t=\frac{Distance}{ v_{b}}

这种方法最大的弊端就是炮弹越慢越难命中。Distance 在Unity中可以简单的调用:

distance = Vector3.Distance(transform.position, TargetObj.position);//get the current distance(insufficiently strict)

bulletSpeed vb 提前设定好了;

加速度 a 在 Unity中需要手动计算,只需要在Update()中保存前一帧的目标速度,然后用公式

  • a=\frac{\Delta v}{\Delta t}  
acceleration = (TargetRig.velocity - lastVelocity) / Time.deltaTime;//acceleration
lastVelocity = TargetRig.velocity;//get the current velocity for next frame acceleration caculation

最后直接明了,一个简单公式解决:

预测物体位置

= 预测物体初始位置 

 + v\cdot \Delta t + \frac{1}{2}a\Delta t^{2} + \frac{1}{2}(-g)\Delta t^{2} 

最后考虑到之前所说的不严谨,就再加上一个随机的小范围的散射,来达到“碰运气”的目的,毕竟就算是严谨计算,也无法应对某些特殊和突发状况,同时尽可能提高子弹速度来缩小\Delta t 的误差,这就是为什么现实生活中的近放炮往往具有超高的射速,极高的子弹速度,一定的散射比例甚至是高空爆炸。

实际效果:

Unity 开发可预测打击近防炮

不难看出在低射速,低频率和低散射时,基本只在匀速直线运动和部分匀变速直线运动中起作用,变速和变加速命中率低的可怜。原因很简单,因为炮弹太慢,飞行时间太长,还没等到炮弹到达,目标早就变到下一个状态了;接着将射速、频率、散射全部调高,命中率一下子就上去了,真是非常的amazing啊

完整代码如下,或许还能优化:

using System.Collections;
using UnityEngine;

public class Interceptor : MonoBehaviour
{
    public Transform TargetObj, PredictedObj;//assigin target 
    public GameObject bullet;// bullet prefab
    public float FiringRate = .3f, bulletSpeed = 10f, followSpeed = 1f, gravityLead = 0.5f, accurancy = 0.5f;

    public float flyingTime, distance;
    [SerializeField] Rigidbody TargetRig;
    [SerializeField] Vector3 lastVelocity, acceleration;


    void Start()
    {
        TargetRig = TargetObj.GetComponent<Rigidbody>();
        StartCoroutine(Shoot());// start shooting
    }

    // Update is called once per frame
    void Update()
    {
        if (!TargetObj) { return; }
        acceleration = (TargetRig.velocity - lastVelocity) / Time.deltaTime;//acceleration
        lastVelocity = TargetRig.velocity;//get the current velocity for next frame acceleration caculation
        distance = Vector3.Distance(transform.position, TargetObj.position);//get the current distance(insufficiently strict)
        flyingTime = distance / bulletSpeed; //dt = Distance / vb

        PredictedObj.position = Vector3.Lerp(PredictedObj.position,//use Lerp to control cannon's follow speed
               (
                TargetObj.position // current position
                + TargetRig.velocity * flyingTime // next position with uniform linear motion: dL1 = L + dt * v
                + 0.5f * Mathf.Pow(flyingTime, 2f) * acceleration// then add distance with uniformly variable motiond during dt: dL2= dL1 + 1/2 * a * dt^2
                + 0.5f * Mathf.Pow(flyingTime, 2f) * -Physics.gravity// next add distance with gravity's uniformly variable motion during dt: dL3= dL2 + 1/2 * a * dt^2
                + new Vector3(Random.Range(-accurancy, accurancy), Random.Range(-accurancy, accurancy), Random.Range(-accurancy, accurancy))//finally add alittle random fractors to incrase hit rate chance 
                ),
                Time.deltaTime * followSpeed);
    }
    IEnumerator Shoot()
    {
        while (TargetObj)
        {
            //  for (int i = 0; i <= num_bullets_pertime; i++) { Instantiate(bullet, transform.position, transform.rotation).GetComponent<Rigidbody>().velocity = transform.forward * bulletSpeed * Random.Range(0.1f, 2f); }
            Instantiate(bullet, transform.position, transform.rotation).GetComponent<Rigidbody>().velocity = transform.forward * bulletSpeed;
            yield return new WaitForSeconds(FiringRate);

        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Bunny Chen

来啊来让我发家致富,一毛钱可!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值