Learning and Applying Mathematics using Computing

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, \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:

\sin(20^\circ) = \displaystyle\frac{\textrm{moveY}}{5}

The cosine equation is similar, but rather than opposite divided by hypotenuse, it’s adjacent divided by hypotenuse, which in our setting is:

\cos(20^\circ) = \displaystyle\frac{\textrm{moveX}}{5}

The tiniest bit of re-arrangement in each equation gives:

5 \times \cos(20^\circ) = \textrm{moveX}    and    5 \times \sin (20^\circ) = \textrm{moveY}

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 4.70^2 + 1.71^2 = 5^2 (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.

Comments on: "Sine on the Dotted Line" (6)

  1. Sharmistha Mukherjee said:

    I like it. Only negative criticism that I have is : the line 5 x cos 20 = moveX and 5 x sin 20 = moveY. In these lines the subjects of the equations are on the right hand side. This may be acceptable in maths, but not desirable, in programming terms it is wrong. I have a big problem with students where they make this mistake when writing assignment statements. My point is that ‘5 x cos 20 = moveX’ kind of encourages this misconception.

    • Thanks for your comment. Something I haven’t explicitly addressed on this blog is the notation that overlaps between maths and computing. Students wanting to learn maths and computing will need to adapt to the different meanings of the equals sign between the two disciplines.

      The equations are maths equations — I think it’s not true to say that in programming they are wrong: Java programming has no direct equivalent of mathematical equality as used here. The maths equation is 5 x cos 20 = moveX. If you want to translate that into a programming statement that “double moveX = 5 * Math.cos(Math.toRadians(20));” then that’s fine, but they’re different beasts. Thinking of the maths solution as being translatable into assignment gets into problems when you get answers like “x = +-2” for “x^2=4”. Maybe that deserves a post of its own…

  2. teraknor said:

    an old trick i used with less able students was to get them to create sine waves in excel …. using the chart wizard, you could easily copy the formula to show amplitude and frequency

    • I think that’s useful for teaching them about the sine wave, but do you have a feel for if they were able to connect the sine wave to the use of sine in trigonometry? I feel like it’s best to view the graph of sine and its properties as a separate topic from sine for trigonometry because I think trying to fuse the topics leads to confusion, but that may be wrong.

  3. Hello Neil,

    I understand that setLocation (x , y) method does not accept parameters of the type double. But what exactly JAVA does when you use (int) on such expression as double moveX = (int) (Math.cos(radians) * moveSpeed); ? Is JAVA rounding the double to the next int value?

    Thank you.

    • It discards the decimal part to convert to an integer (which has the effect of rounding towards 0). It is more correct to use Math.round to round to the nearest integer, but in practice it makes little noticeable difference which rounding method is chosen.

Leave a reply to Tales Cancel reply