Learning and Applying Mathematics using Computing

They’ve got atan, You Want atan2

This post is about turning towards a point, or: more uses for trigonometry (sine, cosine, tangent, etc).

A common feature in games is the capability to gradually move towards a given position. You may want some AI enemies that chase the player, or a character that moves towards the mouse cursor. Today we’re going to build a quick game with a cat that chases a laser pointer (which will be controlled by the mouse — the computer peripheral, that is!). How do you accomplish this? The two steps are firstly, calculating the angle at which to move (the subject of this post) and, secondly, moving progressively at that angle (covered in our previous post).

So we want to calculate the direction needed to aim at a given point. A good place to start with any geometry problems like this is to draw a sketch. Here’s the problem:

As noted in our previous example, the X-Y axes form a right-angle, so we always have a right-angled triangle in these problems. Last time, we knew the angle and one side, and we wanted to find the other two sides, so we used sine and cosine. This time, we know two of the sides (the horizontal and vertical sides, which are the X and Y distances between us and our target), but we want to know the angle. To do this, we can use the tangent function. Wikipedia tells us:

$\tan \alpha = \displaystyle\frac{\textrm{opposite}}{\textrm{adjacent}}$

Recall from last time that adjacent and opposite refer to the lengths of the sides that are adjacent and opposite to the angle of interest. So the adjacent is the horizontal distance, and the opposite is the vertical distance. In our example, we have:

$\tan(\textrm{angle}) = \displaystyle\frac{\textrm{pointerY} - \textrm{catY}}{\textrm{pointerX} - \textrm{catX}}$

We need to do something to both sides of the equation (standard mathematical operating procedure!) to help us work out the angle: we need to take the inverse tangent of both sides. This is called inverse tangent, or arctangent, and we’ll write it as $\arctan$:

$\arctan(\tan(\textrm{angle})) = \arctan\left(\displaystyle\frac{\textrm{pointerY} - \textrm{catY}}{\textrm{pointerX} - \textrm{catX}}\right)$

The left-hand side simplifies, because taking the arctangent of a tangent cancels out (by definition). So we have:

$\textrm{angle} = \arctan\left(\displaystyle\frac{\textrm{pointerY} - \textrm{catY}}{\textrm{pointerX} - \textrm{catX}}\right)$

So that tells us how to get the angle. But there’s a complication once we come to program this. You might be tempted to do this for your cat:

double distX = pointer.getX() - getX();
double distY = pointer.getY() - getY();

double angleRadians = Math.atan(distY / distX);

That looks fine. But here’s a useful mathematical programming tip for you: whenever you use the division operator, /, always think “can the divisor [number on the right-hand side of the division operator] be zero here?” And if that number can be zero, you will be in trouble! That is the case here. In fact, there are multiple problems with using atan (division by zero, and a problem with quadrants), which I’m not going to explain at length in this post. The short version is, in programming, almost never use atan. There’s almost always a function called atan2, which you should use instead as it circumvents the problems with atan. The atan2 function takes the X and Y distances as separate parameters — in Java, this takes the Y coordinate as the first parameter, and the X coordinate as the second, like so:

int distX = pointer.getX() - getX();
int distY = pointer.getY() - getY();

setRotation(angleDegrees);

We have to do a little juggling at the end to convert the radians back into degrees, but that’s the core code done. I’ve filled in the rest of the scenario to make it playable: you can play with the scenario online, or you can download it and look at the source code in Greenfoot. The laser pointer has some very simple code that moves to the last known mouse position, and the cat turns towards the location of the laser pointer and then moves towards it.