Projectile Motion - Arrow

In a 2D game, I simply want to draw the trajectory of an arrow in flight. With the code below, the trajectory (the parabola) looks right, but the angle (or rotation) or the arrow isn't.

``````float g = -9.8f;
float x = (launchVelocity * time);
float y = (launchVelocity * time) + (0.5f * g * (float)Math.Pow(time, 2));
float angle = (float)Math.Tanh(y / x);
``````

What am I missing? Thanks.

-
A screenshot may help – doppelgreener Jun 20 '11 at 9:09

`Arctanh` gives you the tangent for the hyperbolic curve! As far as I know your parabola isn't a hyperbola.

But we have good news: finding the tangent for your parabola is easier. The equation is

Where s is your `launchVelocity`. Now the slope of your arrow is:

You can safety use `Arctan` now if you like.

The approximate trajectory you are simulating applies to the center of mass of your arrow. When you say "position" (x,y) you are talking about the center of mass' position. The center of mass for an arrow is slightly forward from the midpoint and you should take that into account if you are going to draw the arrow.

Keep in mind that you are not considering the inertial momentum of the arrow (which can vary a lot if you are firing a giant ballista) and you are not considering the arrow's fluid dynamics: bow arrow flight won't follow a parabolic path!

-
Thank you Fxlll. Any idea where I could get the formulas which apply to the physic of an arrow? – Martin Jun 20 '11 at 13:01
I think you mean: ![&part;y/&part;x = g/(2s&sup2;)&middot;x+1][2] but in any case I think I recommended a better approach below. For one thing, you didn't explain about separating the x and y components, so this is hardcoded to an arbitrary 45 degree angle, with launchVelocity not being truly launchVelocity, but the component in both x and y – Dov Jun 20 '11 at 14:23
One can compute the moments of inertia with ease. These are two for rods, one for the rotation about its center of mass and the other for the rotation about the rod's axis. Superposition principle applies for moments of inertia so the arrow can be splitted into three part: feathers,body and tip. – FxIII Jun 20 '11 at 15:10
The problem is that the sole momentum is easy to compute is the one due the angle variation (you can see that deriving twice a parabola just a constant term remains). The other is caused by the spinning due the back feather. Here feather drags and friction are involved both converting kinetic energy into spinning, slowing down the arrow but adding gyroscopic effect. This influences the trajectory and is quite difficult to model – FxIII Jun 20 '11 at 15:13
Anyway if you can relate the momentum to the speed given a feather setup, all can be computed thorough integration but i'm not sure you can have a closed form for the equations of motion (i.e. you can get an integration algorithm but not a parametric equation). – FxIII Jun 20 '11 at 15:18

You want the angle of the arrow at any point in time. You remembered that in order to compute an angle, there's a tangent. But here's where your thinking started to go wrong:

1. What you want is delta y / delta x, because the slope is the rate of change (mentioned in one of the other answers). Note that x is just the position where you are at any moment in time, not dx.

Ok, so if you neglect air friction, then the x-velocity of the arrow is a constant.

First, decompose velocity into x and y components. You could be shooting at an angle of 45 degrees or 60 degrees. So you need launchVelocity and an angle, it's not a scalar.

Second, compute everything as double, not float. You're not numerically sophisticated enough to know when roundoff error won't kill you, so don't try. It's not a great time saver in any case.

Third, don't use Math.pow, it's slow and not as accurate as multiplying for integer powers. Also you can save a lot of time by using Horner's form (see below)

``````final double DEG2RAD = Math.PI/180;
double ang = launchAngle * DEG2RAD;
double v0x = launchVelocity * cos(ang); // initial velocity in x
double v0y = launchVelocity * sin(ang); // initial velocity in y

double x = (v0x * time);
// double y = (v0y * time) + (0.5 * g * (float)Math.Pow(time, 2));
double y = (0.5 * g * time + v0y) * time
``````

If you are desperate for performance, you can even precompute 0.5*g, but the above code will take you 90% of the way there without doing anything too crazy. Benchmark doing this 10 million times if you like, it's admittedly not a huge amount of time but percentage-wise it's pretty big -- libraries are very slow in Java

So, if you wanted the angle at which the arrow should go, what you want is

``````atan(dy/dx)
``````

And in this case, that would work because dx is a constant. But in general, dx can be zero, so you usually want to use:

``````atan2(dy, dx)
``````

which is a function specifically designed for this job.

But as I said, library functions in Java are hideously slow, and in this case there's a better way of doing it without as alluded to by @FxIII above.

If the horizontal velocity is always v0x, and the vertical velocity is:

``````double vy = v0y - 0.5 * g * time;
``````

You don't need the angle. If you wanted to draw an arrow, use something nominally like:

plot( x, y, x+vx, y+vy);

I don't know what you're drawing, so if you do need the angle to rotate it (like you're using JOGL) then sure, use the angle.

Don't forget if you are using opengl to turn the angle back into degrees, because ATAN2 returns radians:

``````final double RAD2DEG = 180 / Math.PI;
double ang = Math.atan2(vy,vx); // don't forget, vy first!!!
double deg = ang * RAD2DEG;
``````
-

Tanh() (hyperbolic tangent) takes an angle as a parameter, but you've fed it the ratio of the sides.

What you really want is to use the hyperbolic arctangent, which takes the ratio of the sides as a parameter, and returns the angle. (Naming on this may be "atanh", "atanh2", "arctanh", or something else similar; seems to vary a lot between different maths libraries)

-
No you don't want hyperbolic anything – Dov Jun 20 '11 at 14:28
Gah, you're absolutely right. I immediately picked up on the "usage of basic trigonometry" error, and missed that the function he was using was completely incorrect for the rest of his approach. – Trevor Powell Jun 20 '11 at 22:18
Tan() takes an angle. Atan takes a triangle side ratio (sin/cos). – David Lively Jun 23 '11 at 0:12