Trajectory of a projectile formula. Does anyone know maths?

I found this handy page on wikipedia: http://en.wikipedia.org/wiki/Trajectory_of_a_projectile

In there is this formula:

To hit a target at range x and altitude y when fired from (0,0) and with initial velocity v the required angle(s) of launch are:

alt text

I gave it my best shot trying to convert this to code, but frankly it's way beyond my math abilities. It sort of works at long ranges, then goes crazy at short ranges. Here's my wack at it:

function CalculateProjectileFiringSolution2 ()
{
    var targetTransform =  target.transform.position;
    var barrelTransform = barrel.transform.position;
    var y = targetTransform.y - barrelTransform.y;
    targetTransform.y = barrelTransform.y = 0;
    var x = (targetTransform - barrelTransform).magnitude;
    var v = projectileSpeed;
    var g = Physics.gravity.y;
    var sqrt = (v*v*v*v) - (g * (g * (x*x) + 2 * y * (v*v)));
    if (sqrt<0) {
        Debug.Log("No Solution");
        return 0;
    }
    sqrt = Mathf.Sqrt(sqrt);
    var calculatedAnglePos = Mathf.Atan(((v*v) + sqrt) / (g*x));
    var calculatedAngleNeg = Mathf.Atan(((v*v) - sqrt) / (g*x));    
    return calculatedAnglePos * Mathf.Rad2Deg;
}

Can anyone help me out?

EDIT: Updated per suggestions. EDIT AGAIN: Works now.

This formula considers you shoot your projectile from (0,0), which means in your case that you must subtract your barrel position from your target position:

var y = targetTransform.y - barrelTransform.y; var x = targetTransform.x - barrelTransform.x;

Also, for the love of all that's worthy, do not EVER use Mathf.Pow(n,2) when all you want to do is n*n! Pow is a powerful (bwahaha) function that can take a float value as a second parameter, don't waste it on integer powers, as it's way more expensive that a n*n (even a n*n*n*n*n...).

If to_sqrt is <0 before processing it's square root, then it means there is no solution with real numbers, the velocity is insufficient to reach the desired position, so your gameplay code must handle that situation somehow (start at least with a Debug.Log). But do that BEFORE you carry on with your calculation of the angle. You can now drop the Mathf.Abs() part of Mathf.Sqrt(Mathf.Abs(to_sqrt)).

You don't handle the +/- part of the equation properly: there is either 0 (we covered it), 1 (special case where the use or + or - doesn't matter, same result), or two possible solutions to this equation, each given by using either + or -. They will both be correct, so it's up to you to choose which one. I suggest you toss a coin. Or you could simulate one, see if your projectile hits something on the way, then choose the other one if it's better. Your call.

The rest of your code seems fine to me. Except calculatedAngle * 90.0f. I have no idea what the hell you're trying to do here ^^

Best of luck!

As requested the final code I’m using for my projectile stuff:

float CalculateMaximumRange ()
{
    float g = Physics.gravity.y;
    float y = origin.position.y;
    float v = projectileSpeed;
    float a = 45 * Mathf.Deg2Rad;
    float vSin = v * Mathf.Cos(a);
    float vCos = v * Mathf.Sin(a);
    float sqrt = Mathf.Sqrt(vSin * vSin + 2 * g * y);
    return Mathf.Abs((vSin / g) * (vCos + sqrt));
}

float CalculateProjectileFiringSolution ()
{
    Vector3 targetTransform =  target.position;
    Vector3 barrelTransform = gunMuzzlePoint.position;
    float y = barrelTransform.y - targetTransform.y;
    targetTransform.y = barrelTransform.y = 0;
    float x = (targetTransform - barrelTransform).magnitude;
    float v = projectileSpeed;
    float g = Physics.gravity.y;
    float sqrt = (v*v*v*v) - (g * (g * (x*x) + 2 * y * (v*v)));
    // Not enough range
    if( sqrt<0 )
    {
        haveFiringSolution = false;
        return 0.0f;
    }
    haveFiringSolution = true;
    sqrt = Mathf.Sqrt(sqrt);
    // DirectFire chooses the low trajectory, otherwise high trajectory.
    if (directFire) return Mathf.Atan(((v*v) - sqrt) / (g*x));
    else return Mathf.Atan(((v*v) + sqrt) / (g*x));
}

float CalculateFlightTime ( float angle )
{
    Vector3 targetTransform =  target.position;
    Vector3 barrelTransform = gunMuzzlePoint.position;
    float x = (targetTransform - barrelTransform).magnitude;
    float v = projectileSpeed;
    angle = angle == 0 ? 45 : angle;
    float time = x / (v * Mathf.Cos(angle * Mathf.Deg2Rad));
    return time * .7f;
}

Putting it all together with proper formatting, less noise, and @Arowx 's fix:


`

bool CalculateThrowAngle(Vector3 from, Vector3 to, float speed, out float angle)
{
	float xx = to.x - from.x;
	float xz = to.z - from.z;
	float x = Mathf.Sqrt(xx * xx + xz * xz);

	float y = from.y - to.y;
	
	float v = speed;
	float g = Physics.gravity.y;
	
	float sqrt = (v*v*v*v) - (g * (g * (x*x) + 2 * y * (v*v)));
	
	// Not enough range
	if (sqrt < 0)
	{
		angle = 0.0f;
		return false;
	}
	
	angle = Mathf.Atan(((v*v) - Mathf.Sqrt(sqrt)) / (g*x));
	return true;
}

`


Usage:

`

	float throwAngle;
	if (CalculateThrowAngle(thing.position, at.position, maxSpeed, out throwAngle))
	{
		Vector3 throwDirection = (at.position - thing.position);
		throwDirection.y = 0;
		throwDirection = Vector3.RotateTowards(throwDirection, Vector3.up, throwAngle, throwAngle).normalized;
	}

	rigidbody.velocity = throwDirection * maxSpeed;

`


In action:

alt text

(yellow = line to target, red = calculated throw direction, green = path taken. Falls a tiny bit short because of drag, which is fine in my case)

Minor correction you are calculating the value x as the magnitude of the vector this will only give accurate results when the height difference is near zero.

You need to calculate x as the horizontal distance only e.g.

       float y = (barrelTransform.y - targetTransform.y);

        //targetTransform.y = barrelTransform.y = 0;

        float xx = targetTransform.x - barrelTransform.x;
        float xz = targetTransform.z - barrelTransform.z;
        float x = Mathf.Sqrt(xx * xx + xz * xz);

I would like to take a second to say that @petrucio 's solution didn’t really work for me, there was stuff missing before returning the angle. First, the results were in radians and not in real degrees, so I did the conversion from radians to degrees, and I also fixed the results that were wrong, this is ready to be used in your game as long as you use EulerAngles.
So here is the final working version:

    public static bool CalculateThrowAngle(Vector3 from, Vector3 to, float speed, out float angle)
    {
        float xx = to.x - from.x;
        float xz = to.z - from.z;
        float x = Mathf.Sqrt(xx * xx + xz * xz);
        float y = from.y - to.y;

        float v = speed;
        float g = Physics.gravity.y;

        float sqrt = (v * v * v * v) - (g * (g * (x * x) + 2 * y * (v * v)));

        // Not enough range
        if (sqrt < 0)
        {
            angle = 0.0f;
            return false;
        }

        angle = Mathf.Atan(((v * v) + Mathf.Sqrt(sqrt)) / (g * x));
        angle = (angle * 360) / (float)(2 * Math.PI); // Conversion from radian to degrees
        angle = 90 + angle; // Idk why but thats needed
        angle *= -1; // Unity negative is upward, positive is pointing downard
        return true;
    }

Hi All. Just found this… hopefully someone is still watching this thread.

I’d like to apply this to a character / Enemy jumping to a point. but I would like to vary the angle and velocity to be as small as possible. So Jumps are not extremely high. as I think as human want to do as little work as possible. Thus animation would be more realistic.
Apart from iterating through velocities to get a low angle is there a better solution? The math part of my brain melted years ago! Hahaha!