Solving Ballistic Trajectories
June 1, 2016
A common problem in video games is to calculate the angle to launch a projectile to hit a target. It’s so common I’ve written code to solve it on literally every game I’ve ever worked on.
Each time this problem comes up I tend to grab a pen and pad to re-solve it from scratch. I’m tired of doing that. To save future-self some time I’m going to put the solution on the internet. I’m also going to share a twist that I often prefer for design purposes.
Want to skip the words and see the final result? Fine. I don’t blame you. Even if it makes me a little sad.
WebGL Unity Demo
Equations of Motion
The problem always starts the same. Given a launcher and a target, at what angle must the projectile launch to hit the target?
There are four basic equations of motion. Today we’ll only be using one of them.
In plain English, the final position EQUALS initial position PLUS velocity times time PLUS one-half acceleration times time-squared. This simple equation, a little algebra, and a few trigonometric identities is all we need.
Here’s a quick refresher before we get started.
Given a projectile with fixed speed S and launch angle θ (theta) we can compute the x and y components of velocity. Or if we have S and can otherwise determine y then we can solve for θ and x.
We’ll be using some Algebra.
We’ll be making heavy use of the quadratic formula.
For video games we probably want to know the maximum range of a projectile. AI needs to know how close to move. Players need clear visual indicators showing danger zones.
There is a very simple equation for maximum range on a flat surface. But we’re going to jump off the deep end and start with the generalized form.
Given a projectile with fixed speed (S) and gravity (G) what is it’s max range?
- Substitute known variables (y0, S, G) into our basic equation of motion.
- Apply quadratic formula. Ignore smaller term.
- Plug t back into x = S*cos θ*t and simplify.
For testing and to provide visualizations I created a Unity demo. It involves teapots shooting teapots. Pew pew!
Demo: WebGL Unity Demo
The demo has a handful of sliders. Here we see the range indicator for a teapot turret. As speed goes up range goes up. As gravity goes up range goes down. Pretty simple.
Firing Angle to Hit Stationary Target
Now the fun part.
Given a projectile with fixed speed (S) and gravity (G) at what angle should it be fired to hit a stationary target?
Phew. We have two equations and two unknowns. Let’s break it down.
- First equation, two unknowns (t, θ)
- Second equation, two unknowns (t, θ)
- Solve (1) for t
- Plug (3) into (2)
- Trig substitution: sin θ/cosθ = tanθ
- Trig substitution: 1/(cos θ)^2 = 1 + (tan θ)^2
- Expand and rearrange
- Quadratic formula
- Multiply top/bottom by -S^2/x. Move S^4/x^2 into root
- Apply arctan to each side
Tada! This results in two angles. One low and one high. Here’s what it looks like in practice.
Take a look at the gif above. When the teapot first starts shooting it looks pretty good. The high arc looks nice and pretty. The low arc feels crisp and efficient.
When range increases it doesn’t look so great. The low arc is almost flat. The high arc is comically high. This is the problem with a fixed speed projectile. It only looks good when the target is at the outskirts of its range.
What if there were a better way?
I often prefer to define projectile speed laterally. Only on the ground plane. I then explicitly define arc height. Which means vertical velocity and gravity become variable.
This has several advantages. First, it always looks good!
Second, it’s more intuitive for design. Designers don’t care about absolute speed. They care that a turret has a range of 20 meters and projectiles take 1 second to travel that distance. They shouldn’t need a graphing calculator to change balance numbers. Nor should artistic tweaks affect gameplay mechanics.
Third, it’s easier to hit a moving target. We’ll cover this in more detail in a bit.
Here’s what it looks like.
Solving with Lateral Speed
Given a projectile with lateral speed (S) and peak height (y_peak) what is velocity, gravity to hit a stationary target?
- Basic equation of motion
- Solve (1) for 2
- Define y_peak (user constant) to occur at (1/2)t
- Define y_end (target height) to occur at t
- More magic!
- Firing vector is (S, v.y) with gravitational acceleration g
Presto! Hey, wait a second. Magic? That’s cheating! Yes, but for a reason.
Steps (3) and (4) is another two equations with two unknowns. I’m lazy and don’t want to write it out. Plus I’d screw up and flip a sign. So I let a computer solve it for me.
More specifically, I used Wolfram Alpha. I recommend everyone have Wolfram in their toolbox. It’s quite handy.
If a+c == 2b then y0, y_peak, and y_end are collinear. Which means you’re firing in a straight line.
Lateral Speed with Moving Target
At this point we have two different trajectory solutions. But enemies do not tend to stay in place. They move around. We need to solve the trajectory to hit a moving target.
This is where lateral speed shines. By defining speed on the ground plane it’s super easy to solve for a moving target.
- Where X is the target position and V is target velocity
- Square both sides.
- Re-arrange into quadratic
- Apply quadratic formula
For steps 5 through 9 refer to the previous section.
It makes me giggle with glee. Pew pew pew!
Fixed Speed with Moving Target
What if we wanted to hit a moving target with a fixed speed projectile? Oh boy. This is a can of worms! You don’t even know.
I’ve never actually shipped this in my career. Generally speaking games don’t want pinpoint artillery. It’s not fun! Instead you ballpark the future position and aim at a random point nearby. When players think of artillery they think of dumb shells raining around. Not perfect, laser guided death.
Writing this blog post I found the solution to a fixed speed projectile and moving target isn’t readily found on the internet. I’ll caveat this by saying you probably don’t want to do this in your game. But I spent a lot of time working this out. And damnit I don’t want that time to be for naught!
The reason you don’t want to do this is quartics. The answer fundamentally involves a quartic.
Quadratics have an easy, elegant solution through the quadratic formula. Cubics are solvable a few different ways. Quartics are a bitch.
Solving quartics is well beyond the scope of this article. To be honest it’s beyond the scope of my mathematics ability. Fortunately for us, Graphics Gems I from 1990 has code for solving quartics. It’s available here. I used this code for my demo. I can not speak to it’s accuracy or numerical stability. Proceed with extreme caution.
Alright then. Let’s solve it. What is the angle to fire a projectile with fixed speed at a moving target? This method comes from a 2007 blog post by James McNeill. With reinforcement Ryan Juckett.
- Where P is target position and V is target velocity
- Square both sides
- Re-arrange terms
- Calculate quartic coefficients and plug into SolveQuartic
- Use t to calculate target position then calculate trajectory to stationary point.
This works. The heavy lifting is in SolveQuartic. We then use the stationary target solver from earlier in the post.
Before discovering method one I derived the solution a different way. It involves way, way more steps. But I find the end result more elegant. Plus I used like 8 pages of papers and I don’t want those trees to have sacrificed themselves in vain.
Holy crap. Thirty two steps!? It’s worse than it looks.
1–7; Declare variables.
8–11; Declare system of equations. Four equations, four unknowns - d, e, f, t.
12–15; Solve (8) for d. Multiply out d^2 for later.
16–19; Solve (10) for f. Multiply out f^2 for later.
20–24; Solve (9) for e. Multiply out e^2 for later.
25–27; Solve (11) for e^2. Substitute d^2 and f^2.
28–30; Set (27) equal to (24). Multiply by t^2 and arrange into quartic.
31; Plug coefficients into SolveQuartic.
32; Plug positive, real roots into (14), (18), (23) for d, e, f.
The code is quite short. There are more lines devoted to variable declaration than actual math! Aside from SolveQuartic of course.
Code written for this test has not been battle tested. Nor has this post been peer reviewed. There are probably a handful typos, errors, and mishandled cases. If you find such a mistake please let me know. Discretely so no one knows my shame. 😁
In the meantime, treat this not as a finished solution but a solid starting point.
I used a handful of tools in the creation of this post. Several of which were new to me.
- Unity for the demo.
- Paper, Affinity Designer, and MSPaint for images.
- Arachnid Latex + MathJax for LaTeX formulas.
- FFmpeg for turning sequential screenshots into a movie.
- Gfycat for embedding movies.
- The Utah Teapot. Pew pew!
LaTeX syntax is awful. Hideous and hard to learn. All LaTeX formulas can be found here. Here’s an example.
That just about does it. I spent way more time on this post than I expected. I solved a case I’d never solved before and I learned a few new tools. A worthwhile endeavor.
Nothing in this post is new or original. I tried to explain things thoroughly without being overly verbose. I feel pretty good having complete descriptions all in one place. Hopefully this is helpful to a few folks out there.
WebGL Unity Demo
Thanks for reading.