Projectile trajectory accounting for gravity/velocity/mass/distance?

Hi,
It’s been 3 days since i’ve started trying to write a script that makes my tanks shoot projectiles to their targets. But it’s not working. It’s a 3d simulation tank game (more a testing bs game for the moment hehe). I have searched the internet and the Unity forums for a solution and all i’ve come up with are the following solution threads:

1.Calculating a projectiles angle required to hit object. - Questions & Answers - Unity Discussions
2.Trajectory of a projectile formula. Does anyone know maths? - Questions & Answers - Unity Discussions
3.Projectile motion - Wikipedia
4.Calculating trajectory angle to hit target position - Angle always -90? - Unity Engine - Unity Discussions
5.c# - Projectile motion based off angle - Game Development Stack Exchange

I have tried lowering the gravity to 0 and lowering mass of projectile to 0.00000001 but still the angle is always off.
Here is the script I use so far for the gun:
Vector3 cannon= gun.position;
Vector3 enemy = target.position;
Vector3 gun1 = cannon.position;
var x = target.position.x - cannon.position.x;
var xz = target.position.z - cannon.position.z;
//var xx = target.position.x - cannon.position.x;
//var x = Mathf.Sqrt(xx * xx + xz * xz);
var y = enemy.y - gun1.y;
//y = 0; <----when I turn off gravity on the projectile I use this----
var v = 500f; //m/s
var g = Physics.gravity.y; //m/s
var sqrt = (v * v * v * v) - (g * (g * (x * x) + 2 * y * (v * v)));
sqrt = Mathf.Sqrt(sqrt);
var angleInRadians = Mathf.Atan(((v * v) + sqrt) / (g * x));
var angleInRadians1 = Mathf.Atan(((v * v) - sqrt) / (g * x));
var angleInDegrees = angleInRadians * Mathf.Rad2Deg;
var angleInDegrees1 = angleInRadians1 * Mathf.Rad2Deg;
Vector3 gunrotation = enemy - gun1;
gunrotation.y += angleInDegrees;
gun.rotation = Quaternion.Slerp(gun.rotation, Quaternion.LookRotation(gunrotation), 1f);

The projectile script is basic where velocity of 500 is added to the rigidbody in the Vector3.forward direction. Can anybody help me. I am no mathematics savvy and no physics savvy either. Btw if I turn Gravity off on the projectile than it ALWAYS hits the target. But without gravity the Script could be as simple as getting the Hypothenuse (target.position.x-transform.position.x for side 1 then side 2 target.position.y - transform.position.y)and shooting in that direction just like in this snippet of code:

float OppositeSide = target.position.y - cannon.position.y;
float AdjacentSide = target.position.x - cannon.position.x;
float division = OppositeSide/ AdjacentSide;
float angleRad = Mathf.Tan(division);
float angleDeg = angleRad * Mathf.Rad2Deg;

Hey Ninecorn, I don’t see on your list a thread I contributed to, so I’d recommend checking it out. As well, you should also wrap you code using the [ code ] tags. This formats the code to be monospace and makes it look similar to how you’d see in Notepad or an IDE.

There are two calculations required to hit a moving target.

  1. FirstOrderIntercept. This is a calculation of how far ahead of the target you need to aim based on speed, projectile speed, and distance.
    //first-order intercept using absolute target position
    public static Vector3 FirstOrderIntercept
    (
        Vector3 shooterPosition,
        Vector3 shooterVelocity,
        float shotSpeed,
        Vector3 targetPosition,
        Vector3 targetVelocity
    )
    {
        Vector3 targetRelativePosition = targetPosition - shooterPosition;
        Vector3 targetRelativeVelocity = targetVelocity - shooterVelocity;
        float t = FirstOrderInterceptTime
        (
            shotSpeed,
            targetRelativePosition,
            targetRelativeVelocity
        );
        return targetPosition + t * (targetRelativeVelocity);
    }
    //first-order intercept using relative target position
    public static float FirstOrderInterceptTime
    (
        float shotSpeed,
        Vector3 targetRelativePosition,
        Vector3 targetRelativeVelocity
    )
    {
        float velocitySquared = targetRelativeVelocity.sqrMagnitude;
        if (velocitySquared < 0.001f)
            return 0f;

        float a = velocitySquared - shotSpeed * shotSpeed;

        //handle similar velocities
        if (Mathf.Abs(a) < 0.001f)
        {
            float t = -targetRelativePosition.sqrMagnitude /
            (
                2f * Vector3.Dot
                (
                    targetRelativeVelocity,
                    targetRelativePosition
                )
            );
            return Mathf.Max(t, 0f); //don't shoot back in time
        }

        float b = 2f * Vector3.Dot(targetRelativeVelocity, targetRelativePosition);
        float c = targetRelativePosition.sqrMagnitude;
        float determinant = b * b - 4f * a * c;

        if (determinant > 0f)
        { //determinant > 0; two intercept paths (most common)
            float t1 = (-b + Mathf.Sqrt(determinant)) / (2f * a),
                    t2 = (-b - Mathf.Sqrt(determinant)) / (2f * a);
            if (t1 > 0f)
            {
                if (t2 > 0f)
                    return Mathf.Min(t1, t2); //both are positive
                else
                    return t1; //only t1 is positive
            }
            else
                return Mathf.Max(t2, 0f); //don't shoot back in time
        }
        else if (determinant < 0f) //determinant < 0; no intercept path
            return 0f;
        else //determinant = 0; one intercept path, pretty much never happens
            return Mathf.Max(-b / (2f * a), 0f); //don't shoot back in time
    }

The second calculation is determine for bullet drop

    public static bool CalculateTrajectory(float TargetDistance, float ProjectileVelocity, out float CalculatedAngle)
    {
        CalculatedAngle = 0.5f * (Mathf.Asin ((-Physics.gravity.y * TargetDistance) / (ProjectileVelocity * ProjectileVelocity)) * Mathf.Rad2Deg);
        if(float.IsNaN(CalculatedAngle))
        {
            CalculatedAngle = 0;
            return false;
        }   
        return true;
    }

usage…

Transform Target;

void Fire()
{

float projectileVelocity = 150;
float distance = Vector3.Distance(transform.position, Target.position);
float trajectoryAngle;

Vector3 TargetCenter = FirstOrderIntercept(transform.position, Vector3.zero, projectileVelocity, Target.position, Target.rigidbody.velocity);


if(CalculateTrajectory(distance, projectileVelocity, out trajectoryAngle))
{
          float trajectoryHeight = Mathf.Tan(trajectoryAngle * Mathf.Deg2Rad) * Distance;
          TargetCenter.y += trajectoryHeight;
}

//fire at TargetCenter

}
3 Likes

Hi Iron-Warrior and JamesLeeNZ. Thank you for your fast responses. I’m sorry It took a long time for me to reply back.

Iron: I checked your thread and unfortunately I couldn’t get the script working. It gives me an error of ''couldn’t apply velocity to Rigidbody since it is (NaN,NaN,NaN). I must be inserting the wrong parameters or something. I’m quite newbie to the programming world. I’m going to try it again real soon.

James: Right before you posted your message I went on to buy an Asset from the Store that calculates the projectiles angles called “Ballistic Projectile Helper 1.01” for 5$. It’s working as I expected. There is two projectiles direction possibilities using his script and the first one is with Parabolic Arc descending on the target or a direct shot hitting the target with the less angle possible. Both used my input on the projectiles velocity. But I feel I am going to need your scripts and so I’m gonna let this thread sleep a while before I come back to it.

I’m sorry if you feel I wasted your time but I just got pushed back a few days of work after deciding to install Unity 5.4.0f3 stable… I used to run the Beta 5.4.0b24. Now my tank treads don’t roll properly so I’ve gotta restart the tanks setup all over again lol. but at least I got your stuff and the Projectile Helper script. Once I get back on track i am coming back to this thread.

Yes there are two variations of trajectory

High Arc (lob)… that’s the one you tried to implement. The formula is a whole bunch harder to implement but possible. I have a working version. I dont usually bother with that one, but It has its uses. Artillery strike for example.

Low Arc (line of sight). That’s the one I use mostly. More of a directly at target with enough extra height to make sure it doesnt hit dirt before the target. In the wiki, its listed as Angle of Reach.

Hopefully the pack you brought also includes the first order intercept stuff, otherwise youll never hit a moving target.

No wasted effort if it helps someone in the future.

2 Likes

When you apply the script, set the initial angle variable to something like 45 degrees. Then, set the target variable to any other object in the world. Make sure the object that has the script attached has a rigidbody. I just tested it, so let me know if it doesn’t work!

[/quote]
I’m sorry if you feel I wasted your time but I just got pushed back a few days of work after deciding to install Unity 5.4.0f3 stable… I used to run the Beta 5.4.0b24. Now my tank treads don’t roll properly so I’ve gotta restart the tanks setup all over again lol. but at least I got your stuff and the Projectile Helper script. Once I get back on track i am coming back to this thread.[/QUOTE]

Like James said above, don’t worry about wasting time on the forum, since it’s pretty likely other people will have the same or similar problems. The forums aren’t just for discussion, but also function as a archive of useful information.