- Home /
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:
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.
You could or even should use Atan2. It's a bit saver and calculates the right quadrant (returns a value between [-Pi,Pi] ). Take a look at http://en.wikipedia.org/wiki/Atan2 And of course: http://unity3d.com/support/documentation/ScriptReference/$$anonymous$$athf.Atan2.html
Can you maybe put up your final code for those searching for a similar solution?
Sorry, I edited the question with the working code but that was probably somewhat unclear. I answered with my most up-to-date code including the other functions I ended up needing.
i am having problem getting above function work. can someone help? I posted a separate question here http://answers.unity3d.com/questions/633171/trajectory-of-a-projectile-formula-get-the-same.html
Answer by taoa · Feb 24, 2011 at 10:22 AM
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!
Thanks for the response. I updated per your suggestions but it still isn't working. The numbers it returns are in the area of +- 2 or lower so it's always pointing almost exactly straight ahead, regardless of projectile speed.
Never$$anonymous$$d. Atan returns radians. I knew it would be something stupid. Thanks again for all your help. :)
i am having problem getting above function work. can someone help? I posted a separate question here http://answers.unity3d.com/questions/633171/trajectory-of-a-projectile-formula-get-the-same.html do you know why this is happening?
Answer by Patyrn · Mar 14, 2011 at 11:52 PM
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;
}
Sorry for being a bit dim, but once you have all 3 floats from the methods - how do you use the values to gain motion? I couldn't find a simple enough equation on the wiki page to piece it all together! Thanks for posting the code though!
Answer by petrucio · Jul 03, 2019 at 04:03 AM
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:
(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)
Answer by Arowx · Dec 11, 2012 at 10:00 PM
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);
Answer by valentin56610 · Jan 19 at 09:52 AM
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;
}