Trigonometry: Part 2



This article originally appeared in Dev.Mag Issue 12, released in July 2007

Previously…

Last issue we covered the core principles of trigonometry and learned how to use these to calculate unknown sides in a triangle. Well, I hope you’re rested and ready Commander, because we’ll be jumping straight back in with another essential technique – calculating unknown angles.

Angling 101

Mystery Angle θ

As always, let’s start with a triangle. This one has two known sides (the hypotenuse and one other), but the angle, theta (θ), is unknown. Bummer. Let’s fix that.

Okay, let’s use our super trig techniques from last time. Last time we learned to solve sides by substituting into the magic formula:

$$\text{ratio angle} = \frac{\text{unknown side}}{\text{known side}}$$

“But, hold on”, you might think, “we’re not solving for an unknown side this time!” You’re absolutely right. When solving for angles we need to perform a little tweak to the formula, but the core principle remains the same. Watch and be amazed:

$$\text{ratio unknown angle} = \frac{\text{known side 1}}{\text{known side 2}}$$

Still looks familiar, right? Let’s see how it works. Since we’re solving for θ, we’ll name our sides relative to that angle in the usual way. This leaves us with h equal to 9 units, and a equal to 3 units. Now we need to set our ratio. As you can see in our adjusted formula, we’ll be using our known sides for this. However, if you study the ratios from last month, you’ll realize that there are two possibilities for the ratio depending on which side of the division line we choose to put our numbers. For instance, if we choose:

$$\text{ratio } \theta = \frac{a}{h}$$

we would use cos(θ) as our ratio. However, if we choose:

$$\text{ratio } \theta = \frac{h}{a}$$

our ratio would be sec(θ). The choice is really up to you, but since cos is generally the easiest and most frequently used ratio, that’s the one I’ll be working with. While we’re at it, I’ll plug the numbers in as well.

$$\cos \theta = \frac{3}{9}$$

Now we need to solve for θ. That means that we’ll have to cancel cos out to get θ on its own. For this, we’ll use the arccos(θ) trig function.

Huh?

Inverted (arc) functions are essentially the antimatter of the trigonometry world. By applying the inverted version of a function (arcsin, arccos, arctan, etc.), we can cancel it out to obtain an angle on its own. However, in accordance with the algebraic method, this means that we also have to apply it to the numbers on the right hand side of the equation. As a result, our example will play out a little something like this:

$$\arccos(\cos \theta) = \arccos\frac{3}{9} = 70.529$$

thus,

$$\arccos = 70.529$$

Note: In the Windows Calculator, using inverted functions is as easy as selecting the “Inv” checkbox in the upper left of the screen before clicking a function button.

There’s our answer! Angle θ is roughly 70.5 degrees.

By now you should have a fairly good idea of how to calculate both unknown sides and unknown angles in a triangle. Soon you’ll be ready to learn how to apply all of this to your games, but first…

Degrees and Radians

Before we begin with game applications, one last tidbit of essential information that will make or break your ability to use trig in your games – are you aware that there are two units of measure for angles? Most people are taught to measure angles in degrees, and for this reason I’ve been using degrees as the angular unit in all of the calculations so far. Now, while there’s technically nothing wrong with degrees as a unit of measure, radians are considered to be more mathematically correct. For this reason they are the primary unit of angular measurement in science and engineering. Luckily, most decent programming languages have trig functions built into them by default. Unluckily, they mostly operate using angles measured in radians. Because of this, you’ll need to be able to convert between the two.

Radians are not difficult to understand. Degrees are based on one full rotation being measurable in 360 units. Radians are based on one full rotation being measurable in 2π (2π = 2 × 3.142 = 6.283) units. Therefore, conversion is just a matter of looking at the angles proportionally. Don’t panic! It’s as easy as using these two formulae:

$$\text{angle in radians} = \pi \times \frac{\text{angle in degrees}}{180}$$

$$\text{angle in degrees} = 180 \times \frac{\text{angle in radians}}{\pi}$$

Game Maker saves you from all this mathematical nastiness by compressing all of it into two simple functions: degtorad(angle) and radtodeg(angle). Just be sure that you plug the right measurement into the right formula!

Let the games begin!

At long last! We’re going to learn how to use trig in our games! We’re going to start with one of the most essential trig applications – accurate angular movement.

Imagine that we have a player character that we want to move at a rate of 2 units per frame. We go ahead and program the game to move the character at that rate when the movement keys are pressed: move +/- 2 pixels vertically when the up/down keys are pressed, and +/- 2 pixels horizontally when the left/right keys are pressed. When we press vertical and horizontal keys together, the player simultaneously moves 2 pixels vertically and 2 pixels horizontally. Diagonal movement is sorted, right? Well, you would think so, but you’d be wrong. If you implemented your movement that way (and many people do), you’d probably notice that something was a little off… To elaborate, let’s take a look at your player movement in terms of a triangular arrangement.

Pythagoras > j00

Pythagoras > j00

See the hypotenuse? That represents your diagonal movement rate. When we apply the theorem of Pythagoras, we get the amount that you see in the diagram. No, neither the numbers nor your eyes deceive you – moving diagonally with this method, the character is travelling nearly 50% faster than it would normally! Definitely not too good for our gameplay. And what if we didn’t just want to travel in cardinal directions? What if we wanted to move at any angle? We’d have to find a way to map hundreds of key combinations with different horizontal and vertical movement rates, which is hardly practical.

What can save us in our time of need? Trig to the rescue! Firstly, we’ll need to reconsider how our movement system works, since we know that this one is fundamentally broken. Well, we know that our speed is a constant 2 pixels/frame. We also know what our eight angles of movement would be (0, 45, 90, 135, 180, etc. degrees). This situation can be represented by this diagram:

Reworked movement system

Reworked movement system

Taking the above arrangement into account and applying our trig theorems, we can calculate accurate horizontal and vertical displacements for that angle. You’ll notice that when converting trig statements into algorithms we forego all the algebraic number juggling and only use the final arrangement of numbers and variables for our calculations. As a result, our algorithm will look a little something like this:

$$\text{horizontal displacement} = \text{speed} \times \cos (\text{direction of movement}) $$

$$\text{vertical displacement} = \text{speed} \times \sin (\text{direction of movement})$$

All you need to do is alter the direction (in radians, remember!) and speed of movement, and these two algorithms will happily give you the correct movement rate/displacement for each of the two axes. As you can see, this means that you can move an object accurately in any direction at any speed. This is especially useful for elements such as bullets or other projectiles which you want to fly in all directions!

Although the above is very easily implemented via GML scripts, Game Maker has built-in speed and direction variables in every object you create. All you need to do is assign number to these, and GM automatically does all the calculations and movement for you! Hax!

That’s all folks!

Yes, we’re done – for this month, at least. Hopefully the little taste of applied trig has made you hungry for more, and the fun doesn’t stop here! I still have a few trig tricks to show you: tricks such as relative angle calculation, circular/elliptical movement and positioning and wave/helix movement. Until next issue, happy coding!