Lead target based on speed of target and bullet

Hello,

So I’m making an AA gun. I’ve managed to get it to lead the player, here’s what I’ve got so far:

using UnityEngine;
using System.Collections;

public class MachineGun : MonoBehaviour
{

    public float RateOfFire;
    public GameObject Player;
    public GameObject Bullet;
    public Transform BulletSpawn;
    public int BottomRandom;
    public int TopRandom;
    public Vector3 OldPlayerPosition;
    public Vector3 PlayerPosition;
    public Vector3 PredictedPlayerPosition;
    public Vector3 LeadDirection;
    public float LeadFactor;
    public PlaneControllerV2 PlaneScript;
    // Use this for initialization
    void Start()
    {
        //good coding practices to find gameobjects on start
        Player = GameObject.FindWithTag("Player");
        OldPlayerPosition = Player.transform.position;
        StartCoroutine(ShootTimer());
    }

    void Update()
    {
        transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(PredictedPlayerPosition, transform.up),
                           Time.deltaTime * 1f);
    }

    void TryToLead()
    {
        //LeadFactor = PlaneScript.CurForwardSpeed;
        //PlayerPosition = Player.transform.position;
        PredictedPlayerPosition = (transform.position - Player.transform.position);
        //PlaneScript.CurForwardSpeed is the speed at how I'm moving the plane. I move the plane like this:
        //Rigid.MovePosition(transform.position + transform.forward * CurForwardSpeed * Time.deltaTime);
        PredictedPlayerPosition += PredictedPlayerPosition.magnitude * Player.GetComponent<Rigidbody>().velocity * PlaneScript.CurForwardSpeed / 7;
    }

    IEnumerator ShootTimer()
    {
        //OldPlayerPosition = Player.transform.position;
        yield return new WaitForSeconds(RateOfFire);
        TryToLead();
        //this fixes rotation
        transform.Rotate(-90, -90, +0);
        //spawn the bullet
        Instantiate(Bullet, BulletSpawn.position, transform.rotation);
        StartCoroutine(ShootTimer());
    }
}

I’ve come so far but am stumped on how to calculate how much to lead based on the speeds of the plane and bullet.

I’m suspicious that the issue is with PlaneScript.CurForwardSpeed, because I’m using Rigidbody.MovePosition for the plane. However, how else would I get the plane’s speed?

That code is based on this: target tracking with lead time - Questions & Answers - Unity Discussions

Currently the AA gun just goes bananas (looks at random directions) and spams the console with this:

Look rotation viewing vector is zero
UnityEngine.Quaternion:LookRotation(Vector3, Vector3)
MachineGun:Update() (at Assets/My stuff/Scripts/MachineGun.cs:30)

Any ideas on what I’ve done wrong?
Thank you so much.

The velocity of a rigidbody will be essentially random if you move it about with MovePosition. However if you are using MovePosition, then you already know the velocity. Just use that.

Thanks for the help.

Actually, I’ve been making this a bit too difficult for myself. After some tweaking, I simplified the code:

using UnityEngine;
using System.Collections;

public class MachineGun : MonoBehaviour
{

    public float RateOfFire;
    public GameObject Player;
    public GameObject Bullet;
    public Transform BulletSpawn;
    public int BottomRandom;
    public int TopRandom;
    public Vector3 OldPlayerPosition;
    public Vector3 PlayerPosition;
    public Vector3 PredictedPlayerPosition;
    public Vector3 LeadDirection;
    public float LeadFactor;
    public PlaneControllerV2 PlaneScript;
    public GameObject PredictedPositionObject;
    // Use this for initialization
    void Start()
    {
        //good coding practices to find gameobjects on start
        Player = GameObject.FindWithTag("Player");
        OldPlayerPosition = Player.transform.position;
        StartCoroutine(ShootTimer());
    }

    void TryToLead()
    {
        //LeadFactor = PlaneScript.CurForwardSpeed;
        //PlayerPosition = Player.transform.position;
        PredictedPlayerPosition = Player.transform.position + (Player.transform.position - OldPlayerPosition) * LeadFactor;
        PredictedPositionObject.transform.position = PredictedPlayerPosition;
    }

    IEnumerator ShootTimer()
    {
        OldPlayerPosition = Player.transform.position;
        yield return new WaitForSeconds(RateOfFire);
        TryToLead();
        transform.LookAt(PredictedPlayerPosition);
        //spawn the bullet
        Instantiate(Bullet, BulletSpawn.position, transform.rotation);
        StartCoroutine(ShootTimer());
    }
}

Now the only thing I need to do is figure out how to calculate LeadFactor based on distance, plane speed and bullet speed. Since I’ve switched to Rigidbody.AddForce for the bullets, I’m going to have to dive into some physics equations to get this done. At least I’m doing this in a space scene with no gravity, so that should simplify things.

The fact that I just got a B in my physics course shows :frowning:

There’s probably a better way, but possibly:
T0 = Time for bullet to reach current position (P0)
P1 = Projected position at T0
A1 = Angle to hit P1
T1 = Time for bullet to reach P1
P2 = Projected position at T1
A2 = Angle to hit P2

If A1 and A2 are close enough, then fire. Otherwise, set T0 to T1 and recalculate. If the numbers are diverging further instead of converging, then the target is moving faster than the bullet speed I think, so you couldn’t shoot it anyway.

Edit: Realized that this isn’t right for all cases - if the player flies past very quickly and very close, it will miss the opportunity to shoot them at the closest point, instead firing after them as they’re leaving. The faster the bullets fly relative to the ship speed, the less of a factor this is.

So far this seems to work so-so. The hardest challenge is to calculate LeadFactor in the script. Since this is an AA gun, I don’t need it too be too accurate, but in the future I’ll probably need to do more work on this script.

using UnityEngine;
using System.Collections;

public class MachineGun : MonoBehaviour
{

    public float RateOfFire;
    public GameObject Player;
    public GameObject Bullet;
    public Transform BulletSpawn;
    public int BottomRandom;
    public int TopRandom;
    public Vector3 OldPlayerPosition;
    public Vector3 PredictedPlayerPosition;
    public float LeadFactor;
    public PlaneControllerV2 PlaneScript;
    public GameObject PredictedPositionObject;
    private Ray ray;
    private RaycastHit rayhit;
    public float TimeOfBulletTravel;
    // Use this for initialization
    void Start()
    {
        //good coding practices to find gameobjects on start
        Player = GameObject.FindWithTag("Player");
        StartCoroutine(ShootTimer());
    }

    void Update()
    {
        Debug.DrawLine(BulletSpawn.transform.position, PredictedPlayerPosition, Color.blue);
    }

    void TryToLead()
    {
        //boring raycasting...
        ray = new Ray(BulletSpawn.transform.position, PredictedPlayerPosition);
        if (Physics.Raycast(ray, out rayhit))
        {
            //FOR EXAMPLE bullet speed is about 20 units per sec
            TimeOfBulletTravel = rayhit.distance / 20;
            //here's where the magic happens. I'll need to do this line better
            LeadFactor = TimeOfBulletTravel * PlaneScript.CurForwardSpeed;
        }
        PredictedPlayerPosition = Player.transform.position + (Player.transform.position - OldPlayerPosition) * LeadFactor;
        //moves a sphere in-game so I can see where it's aiming.
        PredictedPositionObject.transform.position = PredictedPlayerPosition;
    }

    IEnumerator ShootTimer()
    {
        OldPlayerPosition = Player.transform.position;
        yield return new WaitForSeconds(RateOfFire);
        TryToLead();
        transform.LookAt(PredictedPlayerPosition);
        //spawn the bullet
        Instantiate(Bullet, BulletSpawn.position, transform.rotation);
        //do it again, and again and yet again
        StartCoroutine(ShootTimer());
    }
}

On 1 end, I’m glad I got it all down to calculating the LeadFactor and for an AA gun the script should be OK. On the other end, I’m pretty disgusted at my scripting skills.