Projectile Motion

Published December 20, 2013
Advertisement
YEAH!!! I got it! I got my math down for projectile motion in 3D space and it works!

Alright, so, let me explain what I was trying to do and how I figured it out.

Problem description: I am a wizard standing at any given position in 3D space. This position is given by the coordinates X, Y, Z. I am launching a fireball projectile at another position in 3D space. My fireball *must* pass through this point. Fireballs are a bit magical as well, so when I launch the fireball, it should always have the same speed for every fireball. I also want to launch my fireball at an arbitrary angle, but for now I am using 45 degrees (pi/4). If I wish, I should be able to increase or decrease the speed of my fireball and still get a perfect firing solution.
Shot_Part1.png

Extra problems: The target may be hiding behind a hill. If this is the case, (to borrow an artillery term) we need to perform a high angle fire mission to hit the target in defilade. Somehow, we will need to detect this situation.

Bonus points: We want to minimize the time of flight for our projectile by choosing the firing angle which has the shortest flight time (ie, a direct shot vs. a long arching shot).

Solution:
I am going with basic, newtonian projectile motion in euclidean space. I am going to ignore the effects of air resistance, wind direction, air density, temperature, etc. The only force which will act on our projectile in flight is going to be gravity. Now, gravity is typically a constant for projectile motion equations. However, since our angle and projectile speed are already constant, we're going to have to change gravity in order to actually hit our target. If we left everything alone, our projectile would follow the same path, regardless of where we want it to go.

So, let's represent our projectile with a 3D coordinate which indicates its position in coordinate space by using a Vector3. Let's also remember from our calculus that velocity is just a change in position over time (note: velocity is a speed and direction). Acceleration is just a change in velocity over time. Here are some select snippets of code to illustrate how I represent physical objects:public abstract class Physical : GenericGameObject, IRenderable{protected Vector3 m_position;protected Vector3 m_lastPosition;/// /// note that velocity may be distinct from orientation (ie. facing forwards but moving backwards)/// protected Vector3 m_velocity;protected Vector3 m_acceleration;public virtual void Update(GameTime gameTime) { m_lastPosition = m_position; m_velocity += m_acceleration * (float)(gameTime.ElapsedGameTime.TotalSeconds); m_position += m_velocity * (float)(gameTime.ElapsedGameTime.TotalSeconds); m_boundingSphere.Center = m_position; }}
This is all we need to move the physical object in an interesting way. If we give the object a velocity, it will move along that velocity vector at the magnitude of that vector. If we give it an acceleration value (such as a gravity force), the velocity will change. Lastly, we keep track of the last position of the object before we update its current position. This is so that we can do collision checks (a really fast moving bullet might pass through a thin object without being detected, or a ball may move into the ground before you bounce it back, etc).

Anyways, let's get back to the mathematics. I found this super handy website at Georgia State University about trajectory motion with a few physics equations. Just as a sanity check, I checked other sites and they used the same math equations (so, we can be sure that we won't get bugs from flawed math & physics, just flawed code). Here is the physics equation for projectile motion:
Shot_Part2.png

So, this equation will tell us the X and Y position of a projectile at any point in time. That's handy. However, we already know where we want or projectile to be. We want it to be on our target! We know the X and Y position of our target and we know our initial velocity, so we have a few knowns! If we can solve for time, we can solve for our gravity constant! Here is the math for it:
Shot_Part3.png

All we have to do is try to solve for the knowns and we can figure out the necessary gravity constant. Here is an illustration I made with some arbitrarily picked values:
Shot_Part4.png

We already know our gun position and the target position, so it's pretty easy to gather the X and Y values. The Y value is just going to be the difference in elevation between the gun and the target. The X value is going to be the length of the line on the X/Z plane between the gun and the target. The position of the projectile will travel along this line at a constant lateral speed. This is super simple to find, we just find the direction vector from the gun to the target like so:Vector3 FireVector = Target.Position - Gun.Position;
Since this is in 3D space, and our physics representations are in 2D space, we're going to want to work in 2D space. We want to use the X/Z plane as our X axis, so we just find the length of our X/Z portion of the firing vector by applying the distance formula:float tgtDist = (float)Math.Sqrt(FireVector.X * FireVector.X + FireVector.Z * FireVector.Z);
We're done with the firing vector now, so now we want to use it for actually moving the projectile along it. First, we just have to normalize the vector, then I apply an elevation angle of 45 degrees (pi/4) and then I scale the magnitude of the vector by the speed I want to give my projectile.FireVector.Normalize();FireVector.Y = MathHelper.PiOver4;FireVector *= 10;
Oh look, now we have our initial firing velocity! It has a direction and speed, exactly what we need for the equations above. Well, we can figure out the speed we're moving along the X axis by taking a distance of the velocity projected onto the X/Z plane by measuring its distance:float Vx = (float)Math.Sqrt(FireVector.X * FireVector.X + FireVector.Z * FireVector.Z);float Vy = FireVector.Y;
Now we have enough known values to plug into our equations above:float T = tgtDist / Vx;float Y = Target.Position.Y - Gun.Position.Y;float G = (2.0f * (Y - Vy * T)) / (T * T);
And there we have it! We now know how much "gravity" to apply to a projectile in flight so that it hits its target! The rest is trivial:fireball.Accelleration = new Vector3(0, G, 0);fireball.Spawn(Gun.Position, FireVector);fireball.Effect = Gun.Effect;m_sceneDB.AddPhysical(fireball);//etc, etc
Now, we can send a projectile to hit any target and use a constant initial velocity, such that shooting at nearby targets and distant targets don't create a wild variation! An archer looses an arrow at the same speed, they just adjust their elevation to hit more distant targets. A catapult works the same way. Both have range limitations, so they can use the standard gravitational constant. However, a fireball is a "magical" projectile which isn't influenced by real gravity. We just want to create a flying projectile which has a constant speed and always gets to it's target.

Extra problem: Well, what if the target is in defilade, such as behind a hill or wall? To detect this problem, we try to shoot an intersection ray between the gun and the target. We already know the distance between the gun and the target, so if the intersection ray hits something closer than this distance, we know that we've got an object in the way. Now, we've got a real problem! We have to get our shot over that obstacle by increasing our elevation. There's also a potential chance that there simply isn't a firing solution to hit the target (ie, it could be in a cave!). From my artillery days, our technique was to just use high angle fire and adjust onto target. Can that work here? I haven't tried it, but the idea is that you use a "best guess" for your elevation angle, fire a virtual shot and see where it lands. Does it land before or after the target? Measure its position on the X/Z line, then fire a second, adjusted shot with the intention of getting on the other side of the target, but as close as you can. Record the shot landing position relative to the first shot and the target. The line between the two shots is your range line and the distance between the target and either of those two positions is the proportion of your adjusted amount you need to correct by. Simply perform a linear interpolation with this adjusted amount and try again. I *think* you'll hit the target every time on your third shot. If you don't, the target is probably in a cave and no firing solution is possible.

Demo: Here is a very short youtube video demonstration to show the action in 3D space.
5 likes 4 comments

Comments

jbadams

Very interesting -- I think this would make a great article if you have time to submit it to the system! Thanks for sharing! :)

December 20, 2013 09:14 AM
slayemin

Thanks, I'll keep that in mind. I think I'd have to spend a bit more time and effort editing for clarity and content if I was going to submit it as an article. This topic also seems a bit too simplistic? I'm not sure.

December 20, 2013 08:46 PM
jbadams

Remember that what seems simple to you may be exactly what a beginner or simply someone inexperienced with projectile motion is looking for! :)

December 21, 2013 06:23 AM
Navyman

I think this would make a great article as well.

December 22, 2013 11:17 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Profile
Author
Advertisement
Advertisement