This post is about moving at a given angle, or: what use are sine and cosine anyway?
Imagine you have a game where you control a spaceship in 2D, flying it around the screen. Positions on the screen in a 2D system use X and Y coordinates, so any movement on the screen is simply an alteration of the X and/or Y coordinates.
Let’s say that the spaceship has a fixed speed of 5 units per frame. If it’s pointing exactly to the right, that’s really easy to implement. You just move 5 units to the right, which means adding 5 to its X coordinate. In Greenfoot, this is:
setLocation(getX() + 5, getY());
Similarly, if the spaceship is pointing upwards, the code is simple:
setLocation(getX(), getY() - 5);
(Remember that while in maths, positive values on the Y-axis are upwards, in computing they are generally downwards. See this note.) But what if the spaceship is pointing at a diagonal angle somewhere between right and upwards, let’s say 20 degrees from horizontal. How many units do you move then?
This problem is a common application of sine and cosine (which fall under the umbrella of trigonometry). First, let’s draw a little diagram of our problem:
(NB: one thing computing does much better than mathematics is use meaningful variable names, and we will do the same.) So we have a starting position for the spaceship labelled (oldX, oldY) in the bottom left, and a position we want to move to, which will be (oldX + moveX, oldY + moveY). We know the angle (20 degrees in this example) and the distance in a straight line (5, our movement speed). What we need to know are the amounts to move: moveX and moveY.
Calculating the X and Y distances when moving at an angle is where sine and cosine come in useful. Sine and cosine are functions that take an angle, and give back a ratio between two sides of the corresponding right-angle triangle. No matter which two points you are interested in on an X-Y grid system, you can always form a right-angled triangle by drawing connecting lines parallel to the X and Y axes. This is key for much of the use of trigonometry in games and simulations.
Back in maths class, you might have tried to memorise which sides sine and cosine refer to, using an acronym for the letter jumble SOHCAHTOA. Nowadays you can google it, or just go straight to wikipedia (score one for computing!). Here is the relevant sidebox from the current wikipedia article:
A few notes. The hypotenuse is always the side that is opposite the right-angle: in all our diagrams, this is the slanted diagonal line between the two points we’re interested in. Of the other two sides, the adjacent is the one that is adjacent to the angle you’re interested in (labelled with the greek letter alpha, , above), and the opposite is the one opposite the angle you’re interested in. So let’s match these two diagrams:
We can see that the hypotenuse is 5, and alpha is 20 degrees. We want to know the length of the adjacent side (the horizontal line, labelled moveX) and the opposite side (the vertical line, labelled moveY). We can adapt the equation from wikipedia to our setting:
The cosine equation is similar, but rather than opposite divided by hypotenuse, it’s adjacent divided by hypotenuse, which in our setting is:
The tiniest bit of re-arrangement in each equation gives:
You can bang that into your calculator right now (being wary of degrees vs radians), to get moveX=4.70 (to 2dp) and moveY=1.71 (to 2dp). You can always sanity check those figures using good old pythagoras: it should be the case that (or near enough, given that we’ve rounded).
That’s the calculation for our specific example, but we need to put it in our code. For this, we use Java’s standard maths libraries. Our code for moving at an angle is:
double radians = Math.toRadians(getRotation()); double moveSpeed = 5; double moveX = (int)(Math.cos(radians) * moveSpeed); double moveY = (int)(Math.sin(radians) * moveSpeed); setLocation(getX() + (int)moveX, getY() + (int)moveY);
For our rocket ship, that’s all you need to move at an angle. This function is so useful that in Greenfoot it’s now provided for you: you can call
move(5);, which will perform the above calculations for you, and move 5 units at the current rotation. But don’t feel cheated: understanding this use of sine and cosine is necessary for us to build more interesting games in future posts.
I’ve made a quick scenario to demonstrate it in action, combining the above code with the usual Greenfoot code for turning based on keyboard control. You can have a play (use the left and right arrow keys to turn), and download the project to look at the code in Greenfoot.