0
votes

Plenty of questions asked around the same topic, but nothing seems to be working for me.

The problem is simple, a player and an enemy are on the x,y plane. I want to launch my projectile at a calculated angle in such way that the projectile will hit the enemy at it's coordinates.

I've tried implementing both

Angle of Reach and Angle required to hit x,y

Both of these implementation end up doing the same for me; Shooting but not hitting the target in this manner

Any help or pointers would be much appreciated! Thank you

Here is the code:

public Rigidbody projectile;
public float projectileSpeed;
public float Firerate = 9f;
private float nextfire;

private GameObject enemy;
private float gravity = Physics.gravity.y;
private Vector3 directionalVector;

// Start is called before the first frame update
void Start()
{
    enemy = GameObject.FindGameObjectWithTag("enemy");

}


void Update()
{
    directionalVector = enemy.transform.position - transform.position;

}
void FixedUpdate()
{

    nextfire = Time.time + (1 / Firerate);

    float projectileSpeed2 = projectileSpeed * projectileSpeed;
    float projectileSpeed4 = projectileSpeed2 * projectileSpeed2;
    float x = enemy.transform.position.x;
    float y = enemy.transform.position.y;
    float x2 = x * x;



    float theta = Mathf.Atan(projectileSpeed2-Mathf.Sqrt(projectileSpeed4-gravity*(gravity*x2+2*y*projectileSpeed2))/gravity*x);
    print(theta);

    Vector3 releaseVector = (Quaternion.AngleAxis(theta, Vector3.up) * directionalVector).normalized;

    Debug.DrawRay(transform.position, releaseVector, Color.red,0.5f);
    Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation) as Rigidbody;
    instantiatedProjectile.velocity = releaseVector * projectileSpeed;
}

}

2

2 Answers

0
votes

Why not avoid the problem of finding the angle, and just move the bullet based on the direction on where it first saw the enemy.

(target.transform.position - transform.position).normalized;

It will return a Vector direction to the target.

When the projectile moves, just move it based on this direction.
No headache needed in calculating angles :)

Edit

I made a function before to 'convert' an angle to direction:

protected Vector2 DetermineBulletMoveDirection(float shootingAngle) {
    // Determine the direction of the bullet travel on the x and y axis.
    float bulletDirectionX = transform.position.x + Mathf.Sin((shootingAngle * Mathf.PI) / 180);
    float bulletDirectionY = transform.position.y + Mathf.Cos((shootingAngle * Mathf.PI) / 180);

    // Determines the direction this bullet should be moving.
    Vector2 bulletDirection = new Vector2(bulletDirectionX, bulletDirectionY);
    return (bulletDirection - (Vector2)transform.position).normalized;
}

It takes in an angle, and converts it into a direction based on where the shooter is currently at.
The angle should start from Vector.down, and rotates clockwise.

The next problem is to find out the angle between you and the enemy.
This is the simplest solution I could think of, here is a diagram first:

Diagram

Notice that you can use TOACAHSOH on this?

So all you have to do, is to 'virtually' align the Y axis of the shooter to the origin.
(Apply the movement to the shooter too!)

Do the same thing for the shooter, but on the x-axis this time.

And you would be able to achieve that state where you have a triangle with a 90-degree.
From there on, you can calculate the angle to rotate from Vector.down to the enemy.

Just make sure you move both of the objects back to it's initial position.

0
votes

After fighting this for a while I found a solution.

In the end I ended up using the Angle of Reach. The second error was that Mathf.Atan returns radians and not degrees, while Quantion.AngleAxis takes in angles. The third and the final one was the fact that Unity uses left hand coordinate system as opposed to the usual right hand system which I was used to.

Here is the final piece of code:

public class TargetAndShoot : MonoBehaviour
{
    public Rigidbody projectile;
    public float projectileSpeed;
    public float firerate;
    private float nextfire;

    private GameObject enemy;
    private float gravity = Physics.gravity.y;


    // Start is called before the first frame update
    void Start()
    {
        enemy = GameObject.FindGameObjectWithTag("enemy");

    }


    void Update()
    {
        if (Time.time >= nextfire)
        {
            nextfire = Time.time + (1 / firerate);
            float distance = enemy.transform.position.x - transform.position.x;
            Vector3 directionalVector = enemy.transform.position - transform.position;

            float v2 = projectileSpeed * projectileSpeed;
            float v4 = v2 * v2;

            float x = enemy.transform.position.x;
            float x2 = x * x;
            float y = enemy.transform.position.y;

            float theta = 0.5f*Mathf.Asin((gravity * distance) / (projectileSpeed * projectileSpeed));
            Vector3 releaseVector = (Quaternion.AngleAxis(theta * Mathf.Rad2Deg, -Vector3.forward) * directionalVector).normalized;
            Debug.DrawRay(transform.position, releaseVector*5, Color.cyan, 0.5f);

            Rigidbody instantiatedProjectile = Instantiate(projectile, transform.position, transform.rotation) as Rigidbody;
            instantiatedProjectile.velocity = releaseVector * projectileSpeed;
        }
    }

}