This article originally appeared in Dev.Mag Issue 16, released in August 2007
Welcome back, fellow coders! Prepare yourselves for the final, bumper episode of the Trig Trilogy, where I cover a whole lot of awesome trig techniques for your game-making pleasure. We’ve got a lot to cover, so let’s jump right in. It’s time for my first trick! Now, as you can see, there is nothing up my sleeve…
Relative Angling 101
No matter what kind of game you’re working on, more often than not you’ll need to calculate the angle between two objects for some reason. It’s time to look at a chunk of code that can make your life very easy in this regard:
Angle = arctan((Y2 – Y1) / (X2 – X1))
What this code does is to first calculate the difference in x and y coordinates between the two objects, then divides as per the arctan ratio. X1 and Y1 represent the coordinates of the object you want to calculate the angle from. X2 and Y2 are, obviously, the coordinates of the object whose angle you want to measure relative to the first object.
We perform the subtraction because all trig calculations assume that our origin is at point (0,0) on the Cartesian plane. As a result, we need to use relative measures, essentially making point (X1, Y1) our origin. If this all seems really confusing to you, worry not. I’ve summed it up for you in these two diagrams. As you can see, there’s a massive difference between the two depending on whether you subtract or not!
One final thing to note is that this algorithm will give you the angle in radians, as was discussed last month. Remember to convert the answer to degrees if that’s what your game requires!
And there you have it: all you need to know about calculating the relative angle between two objects! That wasn’t so complicated, was it?
There are several functions you can use in GM to calculate your angles. You can use the same algorithm as above or use the slightly altered arctan2(y,x) function, meaning you don’t have to divide first. By far the easiest, however, is to use the point_direction(x1,y1,x2,y2) function, which also converts the angle to degrees for you as a little added extra! The choice is yours.
Goin’ Round in Circles
One of the most prevalent problems that first-time coders have is making objects follow a circular path. There could be any number of uses for this – having a targeting reticle orbit around your game character, or perhaps a spinning-blade-on-a-chain trap for hapless players to wander into. Either way, if you’ve ever wanted to have one thing spinning around another, this is exactly what you need!
Fortunately, circular positioning is not at all difficult to do for us mighty trig-wielders. As always, let’s look at exactly what we’re trying to accomplish here in the form of a handy diagram.
Figure 1 depicts what is commonly referred to as a “circle”. This shape is notable because it has a constant radius. That means that regardless of where you are on the circumference, you’ll be exactly the same distance away from the center. This gives us an important clue.
Figure 2 depicts our situation more clearly. What we really want to do in code terms is to calculate the x and y coordinates of an object while ensuring that, regardless of its angle to the center, it always remains within a set distance of that center. We can do that using the following two algorithms:
x = CenterX + Radius × cos(Angle)
y = CenterY + Radius × sin(Angle)
“CenterX” and “CenterY” refer to the coordinates of the position/object we want to rotate around. It’s important that we have these, otherwise (as with our angle calculations) the object is going to rotate around the screen/level origin (0,0) instead of the center we want. “Radius” is obviously the radius of our circle, and “Angle” is the relative angle between the center and the orbiting object. Changing the Angle will rotate the object around the center. Increasing or decreasing the Radius will move the object further from or closer to the center. Changing the CenterX and CenterY coordinates will, of course, move the center and the spinning object with it. That’s all there is to it!
A final note. If you’re implementing a spinning object where you keep increasing the angle to create rotation, it’s wise to reset the Angle to 0 once it exceeds the measure for one full rotation (either 360 degrees or 2? radians). Although it shouldn’t make much difference to your calculations, it certainly keeps things cleaner, and ensures that you know exactly what range of numbers your code is working with. This can make debugging a lot easier.
Game Maker makes things easy for you again by providing the lengthdir_x(len,dir) andlengthdir_y(len,dir) functions. These take your circle/ellipse radius (len) and direction (dir) and calculate the correct x and y values for you just like we did above. No mess, no fuss, and you can use them any time you need to resolve an angled line to x and y coordinates, not just with circles. Just remember to add the CenterX and CenterY coordinates to the final answers!
What’s this? There’s more?
Yes! Sometimes you may want an object to move in an elliptical fashion, especially if you’re creating an isometric game where the perspective means that you need ellipses to represent circles and circular movement. Fear not, good people! By altering the radius values in the circular positioning algorithms, we can make objects follow an elliptical path.
Ellipses gone wild
The trick is to realize that the radius in an ellipse is not constant – the radius for its vertical proportions is different from the radius for its horizontal proportions. For instance, the first ellipse in Figure 4 has a vertical radius of 2, which is half of its horizontal radius of 4. We need to alter the radius values in our algorithms to reflect this distorted relationship. For the first ellipse, our expressions would look like this:
x = CenterX + 4 × cos(Angle)
y = CenterY + 2 × sin(Angle)
lol
For the second ellipse, our expressions would look like this:
x = CenterX + 2 × cos(Angle)
y = CenterY + 5 × sin(Angle)
I’m sure that the relationships between the radii that you specify and the ellipse’s proportions are becoming crystal clear to you. Other than that, the code works in exactly the same way as with a regular circle.
Wave Motion – Cowabunga!
For our final trick, we’re going to cover a very useful function of trig – creating wave motion (or oscillation), for game objects. Ever played a scrolling shooter where the enemy ships move in a wavy pattern across the screen, or stood amazed at the helical particle trail left by a weapon? Well, here’s how it’s done!
All the time that we’ve been doing trig, you’ll have noticed that we’ve been using two algorithms – one to calculate the X (horizontal) coordinate, and another to calculate the Y (vertical). As you learned while we were covering circles and ellipses, we can use the two to create rotation around an object. Altering the radius values for either equation lets us warp the circle’s proportions, creating elliptical motion.
Have you asked yourself what would happen if we left one of the calculations out? What if we neglected to calculate the X value? What if we left the Y value out in the cold? Well, we’d still get movement, but it would be exclusively on the axis for which we calculated it. That’s the magic behind wave motion. To demonstrate, yet another diagram:
The arrows in figure 5 illustrate the rotational motion. You should notice that as we reduce the horizontal component’s radius to zero, the rotation becomes less “circular” and more “wavy”, wobbling between the radius points and the center. The point that I’m trying to convey is that, used alone, a trig function can be used to introduce oscillation to an object’s movement.
Here are the algorithms you would use:
Displacement = [CenterX or CenterY] + (Radius × sin(Angle))
OR
Displacement = [CenterX or CenterY] + (Radius × cos(Angle))
These algorithms are actually very similar to the ones we use for circular positioning. As before, Radius will determine the maximum distance from the center our object can move, and Angle will determine at what point in the wave our object is. However, an essential thing to note is that we no longer have to use Sinfor vertical displacement and Cos for horizontal displacement, because we’re only displacing along one axis now.
Let’s back up a bit. If we don’t have to care about using Sin or Cos, what’s with the big “OR” in there? Well, it depends on how you want your wave to behave. Oscillation using Sin or Cos results in slightly different wave patterns:
The Sin graph begins its motion at a displacement of 0. This means that using Sin as your trig function will result in your object’s motion starting at the center point you specify. Likewise, the Cos graph begins its motion at the maximum point, so using Cos as your function for oscillation will result in your object starting at the maximum radius you’ve set.
So how does this translate to our code? Well, for example, CenterX + (Radius × sin(Angle)) will result in oscillation along the x axis starting at the center, while Center Y+ (Radius × cos(Angle)) will result in oscillation along the y axis starting at the radius. Clear?
One final thing: you may already have gathered this, but it is possible to alter the speed of oscillation by multiplying or dividing your Angle variable. You can use this to easily change how quickly the object moves along the wave. Multiplication will make your object oscillate faster, while division will slow it down. This can result in quite a few interesting special effects or challenges in your game.
It’s Over!
This marks the end of this little introduction to game programming with trigonometry. As you’ve seen over the past three installments, trig has an incredibly wide range of effective uses when programming games. Hopefully you’ve not only grasped the techniques covered here, but you’ve also gained the ability to apply trig to whatever problems you may come across when coding in the future. Properly applied, trig is a highly flexible, incredibly powerful tool in your programming arsenal, especially if you plan on creating 3D games later on. Good luck, and happy coding!
That’s not a sine wave. This is http://www.sprags.com/images/mainpower_sine_wave.jpg
You’ve drawn two semi-circles together.