Quick Tip : How to choose which way to turn?
Often situations arise in game coding – particularly in AI – where you need to be able to calculate which way to turn in order to reach a certain angle.
Certain types of motion in Unity allow you to avoid this question altogether, such as Slerping (a.k.a. using spherical interpolation) from one rotation to another. Sometimes you need to be more explicit in your code however, and in these cases you often need to deal with and make decisions based on the angles themselves.
Some situations involve completely free-moving objects that need to turn in any direction, such as a spaceship that can fly in any direction in 3d space. Other times, you have more constrained situations (eg, a boat, car, or any other object which is turning but constrained to some kind of surface) where you simply want to decide between a clockwise or anticlockwise direction when turning.
This tip tackles the latter of the two, where you need to make a decision about turning clockwise or anticlockwise based on arbitrary rotations or directions as input.
An example of this would be an AI controlled car, which might need to turn towards a target (another car, or the next waypoint, for example). You don’t want to use interpolation, because that would preclude realistic car physics. You wouldn’t want it making a 270 degree turn to reach a target that was 90 degrees to the left, but how to determine which way to turn?
These situations usually boil down to one of the following cases, where you need to find:
- the angle between two rotations,
- the angle between a rotation and a target position,
- the angle between a rotation and a target direction, or
- the angle between two direction vectors
The key to solving anyof the above lies in the fact that whichever case you have, you can convert it to the last case – where you simply have two directional vectors to compare – and from there you can use a few simple math functions to return the angle you want. Unity provides some very useful functions in its API which help along the way.
Starting at case 1, you have two rotations (which in Unity are represented by Quaternions).
A rotation can be converted to a directional vector by multiplying the quaternion by the world-relative “forward” direction. Forward in Unity is represented by the positive direction along the Z axis (i.e. 0,0,1) and there is a handy alias to this value on the Vector3 class called “.forward“, so assuming “rotationA” and “rotationB” are our quaternions, we can get our two directional vectors like this:
// convert both rotations to direction vectors: var forwardA = rotationA * Vector3.forward; var forwardB = rotationB * Vector3.forward;
It’s also worth noting that in most cases in Unity, when you’re dealing with a rotation, it is very likely to have come from a GameObject with a transform. If this is the case, there’s an easier method of getting the forward vector, which is to use the built-in variable: transform.forward which directly gives you an object’s forward direction as a vector:
var forwardA = objectA.transform.forward; var forwardB = objectB.transform.forward;
Now looking at case 2, where we have a rotation and a target position. Assuming we’re working with gameobjects, we can use the transform.forward of our object that is trying to turn towards the target (eg, the car) for the forward direction, and if this script is placed on the car itself, it’s as simple as this:
var forwardA = transform.forward;
To get the direction vector towards the target (case 3), we just need to subtract the target’s position from the car’s position, like this:
var forwardB = target.position - transform.position;
You should now have two directional vectors whose angles you want to compare to each other.
The final part of the puzzle lies in the fact that you want a signed angle (i.e. either a positive or negative angle). That is, you want to know more than just the numeric difference in angles, you want to know whether to turn clockwise or anticlockwise to get there. To get a signed angle doesn’t really make a lot of sense in 3D space, because the direction of rotation from one direction to another could be in any 3d direction (not just in one of two ‘flat’ clockwise/anticlockwise directions).
For this reason, it usually makes sense to compare your vectors on a certain chosen 2D plane, and ignore the 3rd axis. For example, in the case of cars you’d probably want to compare the “Top Down” directions as if on a map, ignoring the inclines of hills. This would be the X-Z plane in Unity.
To convert these vector directions to numeric angles in this way, you can use the Atan2 function, like this:
// get a numeric angle for each vector, on the X-Z plane (relative to world forward) var angleA = Mathf.Atan2(forwardA.x, forwardA.z); var angleB = Mathf.Atan2(forwardB.x, forwardB.z);
However, this function returns its result in radians, which is just a different scale for measuring angles, and can be converted back to degrees very simply by multiplying the result by a built-in value called Rad2Deg in Unity which is provided for just this purpose, so to have the result in degrees, the above lines would end up looking like this:
// get a numeric angle for each vector, on the X-Z plane (relative to world forward) var angleA = Mathf.Atan2(forwardA.x, forwardA.z) * Mathf.Rad2Deg; var angleB = Mathf.Atan2(forwardB.x, forwardB.z) * Mathf.Rad2Deg;
And finally, Unity provides a simple function for getting the signed difference between two numeric angles in degrees (which is what we’ve been working towards!) called DeltaAngle, which you would use like this:
// get the signed difference in these angles var angleDiff = Mathf.DeltaAngle( angleA, angleB );
You now have a single numeric value which should be in the range of -180 to 180. Negative values indicates your object should turn to its left, and positive, to its right.
Hope this comes in handy out there in Unityland!