Let's rotate stuff!
Rotate to point at other stuff!

Sometimes in games you'll have a happy flower that wants to look at the sun, an eye you want to follow you, or a gun you want to lead towards a victim. In any of these cases what you're essentially talking about is rotating object 1 so that it is pointing towards object 2. The target doesn't even have to be an object. It can be a mouse position (think your character walking towards where you've clicked in an adventure game, a Diablo-like, or an MMO) or AI driven target as well. Luckily doing this, while dependent on somewhat complex math concepts, does not actually require much code. So it's a typically lightweight and cost-effective operation you can do without slowing down your program too much.

Many software libraries and engines will refer to this as a kind of "Look At" method.

Now the initial examples of this process will be in 2D. Unlike something like finding a distance between 2 points we can't just add another line or two to go from a 2D operation to a 3D operation. In this case a 3D space "lookat" this is probably best done using matrices.

So we'll be starting with the simple idea of turn an object to face on this page but we'll add onto it to include smoothing of rotating objects and the ability to move objects in the direction they are facing as well.

Turn to Point at Target

One thing to always keep in mind when calculating an objects "direction" is that the returned value will depend on what is "forward" in your engine / programming environment. In JavaScript a direction of "0" degrees is actually directly to the right by default and not towards the top of your screen as in some other environments.

Furthermore some systems, like JavaScript, don't use 0 to 360 as the full measurement of rotation. They instead use a pairing of 0 to *positive* 180 and 0 to *negative* 180 to measure rotation. So turning clockwise from zero will result in a positive rotation while rotating counter clockwise will result in a negative rotation. This is easier to visualize in the example below.

In such a system conversions should be simple enough.

  • Want the negative, counter clockwise, values to go up to 360 degrees? Simple add 360 degrees if the rotation is below 0. "-10" becomes "350", "-90" becomes "270", and so forth.
  • Want the "up" direction to be considered the "0"? Add or remove 90 in the desired direction.

The process will be similar regardless of your coding environment.

  1. Find the difference between the target coordinates and the coordinates of the object to be rotated.
  2. Find the "arctangent" of the quotient of the x difference and the y difference. Shit just got REAL.
  3. Divide the result by PI over 180.

As usual, a few modifications might be needed depending on the system, but the end result is illustrated in the example below.



/*
Let's examine the actual code involved.
To create the example above, code similar to this is simply run every frame.
The mouse cursor is used as the target of the object.  Keep in mind that the
example's source code, how it is drawn, and how it finds the mouse cursor,
are not part of the code below.  This code pertains only to the rotation of the
object.
*/

function rotate_object(){

	// We'll be finding the distances between the rotating
	// object and the target, we'll store them here.
	var difference_x;
	var difference_y;
	
	/*
	Of course if you're tracking something else, a waypoint
	for an AI for instance, you'd simple change the required
	field below.  In this case "mouseX" and "mouseY".
	*/
	difference_x = mouseX - object.x;
	difference_y = mouseY - object.y;
	
	// And then some accursed freaky trig that I'm 
	// sorry I can't explain...
	object.rotation = Math.atan2( difference_y, difference_x ) 
							  / ( Math.PI / 180 );

}

// And that's it!

So about that "atan2" function. The W3 schools website has a quick descriptor...

"The atan2() method returns the arctangent of the quotient of its arguments, as a numeric value between PI and -PI radians.

The number returned represents the counterclockwise angle in radians (not degrees) between the positive X axis and the point (x, y)."

And according to the Actionscript livedoc site the Math method "atan2"...

"Computes and returns the angle of the point y/x in radians, when measured counterclockwise from a circle's x axis (where 0,0 represents the center of the circle). The return value is between positive pi and negative pi. Note that the first parameter to atan2 is always the y coordinate."

Essentially the "atan2" is what returns the angle you're looking for.

It is important to not forget the "2" because normal "Atan" is a different funciton entirely in most math libraries. Make sure you use the right one.

So yes, as you can see, "atan2" typically deals in radians, and will need converting if you are in a system using euler angles (degrees).

This is the point of the "Math.PI / 180" portion of the previous line. This is the conversion of radians to degrees.

If we don't have access to a math library that has a PI constant, though that would be kind of surprising, we can use a kind of shorthand to convert the measurement to degrees with a single value.

Such a line would be written like so...


rotation =
	Math.atan2( difference_y, difference_x )
	/ 0.0174532925;

Finally, if you're using Unity 3D, your code will be something like this...



using UnityEngine;
using System.Collections;

public class LookAt : MonoBehaviour {

	// Some working variables to find distance.
	float difference_x;
	float difference_z;

	// A reference to the target object.
	GameObject target;
	
	// A result rotation (in euler angles).
	Vector3 result;

	// Use this for initialization
	void Start () {

		// Instantiate required fields.
		target = GameObject.Find("object_name");		
		result = new Vector3(0,0,0);
	}

	// Update is called once per frame
	void Update () {
	
		// Find the difference.
		difference_x = target.transform.position.x - this.transform.position.x;
		difference_z = target.transform.position.z - this.transform.position.z;
		
		// In unity specifically I want the "forward" direction to be the "+z"
		// direction of the object.  For that the "Atan2" arguments need to be
		// x, then z, otherwise it would rotate in the opposite direction.
		result.y = Mathf.Atan2 ( difference_x, difference_z ) / ( Mathf.PI / 180 );
		
		// Apply the final rotation (in euler angles).
		this.transform.eulerAngles = result;

	}

}

Turn to Look at Target (Smooth)

Now there are some times when you won't necessarily want the object that you're rotating to just instantly "snap" to the direction of the target.

Such a case would be if, in a game, an enemy monster has noticed the player and is turning to attack them. You may not want the monster to instantly turn around, that would break a degree of naturalism, but instead want them to actually "turn" before they can take an action. For that we need to turn slowly.



// For this example I've done something more practical and divided the code 
// into different functions.  As you can see we can then call those functions
//  from the unique "update" funciton that all programs should have.

function update(){		
	turn_to_linear( p1, mouse_x_div, mouse_y_div );
	turn_to_easing( p2, mouse_x_div, mouse_y_div );
}

// The first function is for the box that turns at a constant rate.

// *********************
// This function below is too complicated right now.
// Am looking to shorten it.
// It's long, and it points in the wrong direction.
// YEah...
// *********************

function turn_to_linear( object_, target_x_, target_y_ ) {

	// As you can see, the first part of this function 
	// is the same as the easing function.
	
	// It should also be noted that a lot of these variable declarations can be
	// done outside the function, and then assigned to form within the
	// function, to help optimize the code a bit.  But that's a needless
	// optimization at this point.
	
	var difference_x = object_.x - target_x_;
	var difference_y = object_.y - target_y_;
	
	// For this particular code we convert to radians first.
	
	var rotation_radians = object_.r_deg * DEG_TO_RAD;
	
	var rotation_target = Math.atan2( difference_y, difference_x );
		
	var rotation_dif = 
					Math.atan2(Math.sin(rotation_target - rotation_radians),
					Math.cos(rotation_target - rotation_radians));	
	
	var turn_speed = 1.5;
	
	// But now a check to see if the speed is greater than the current 
	// difference.  We don't want the rotation speed to take the final rotation
	// beyond the target rotation after all, so we basically do the same as the
	// instant rotation function does.
	if( Math.abs( rotation_dif ) < turn_speed * DEG_TO_RAD){		
		object_.r_deg = Math.atan2( difference_y, difference_x ) * RAD_TO_DEG;	
	}		
	// Otherwise,
	// If the short distance is clockwise of the target,
	// rotate to the left, else rotate right.
	else if( rotation_dif <= 0 ){
		object_.r_deg -= turn_speed;
	} else {
		object_.r_deg += turn_speed;
	}	
	
}

// Now for the function that turns the rectangle, but does allows it to drag
// in speed a bit so as to seem more "natural".

function turn_to_easing ( object_, target_x_, target_y_ ){

	// The speed of correction for the "lazy" square, this goes 
	// from 0 to 1, with 1 being instant snapping.
	var turn_speed = 0.03;
	
	// Get our differences, as usual.
	var difference_x = object_.x - target_x_;
	var difference_y = object_.y - target_y_;

	// We need to check to make sure we don't attempt to 
	// compute a length of zero.  So find it, and check it.
	var length = Math.sqrt( difference_x * difference_x + 
							difference_y * difference_y );					
		
	if( length ){
		// If we want the end result to target the opposite
		// side of our object, switch "-length" for just "length".
		difference_x /= -length;
		difference_y /= -length;
	} else {
		difference_x /= 1.0;
		difference_y /= 1.0;
	}
	
	// Get the directional lengths from cos and sin.
	var dirx = Math.cos( object_.r_deg * DEG_TO_RAD );
	var diry = Math.sin( object_.r_deg * DEG_TO_RAD );
	
	// And just ease the direction with a floating point number.
	dirx += ( difference_x - dirx ) * 0.1;
	diry += ( difference_y - diry ) * 0.1;

	object_.r_deg = Math.atan2(diry, dirx) * RAD_TO_DEG;
	
	// If you need the function to find and return Radians instead of degrees 
	// then just remove the "DEG_TO_RAD" sections from 
	// the previous 5 lines or so.
	
	// This commented section below is an alternate version that works,
	// but I think it can be shortened, we'll see.
	// I also haven't tested to see which is faster.
	/*	
	// The rotation that would look at the target.
	var rotation_target = Math.atan2( p2_difference_y, p2_difference_x );

	// As you can see, this is where it naturally chooses the shortest angle.
	// Though to get the X and Y argument, we need the ratios of the triangle 
	// sides as well (cos and sin).		
	var rotation_dif = 
		Math.atan2(Math.sin(  rotation_target - p2.r_deg * DEG_TO_RAD ),
							  Math.cos( rotation_target 
									  - p2.r_deg * DEG_TO_RAD ));
									   
	// Now we add it all up.  The new difference, times the ease percentage,
	// converted back into degrees and with the current rotation added in.
	p2.r_deg = rotation_dif * 
						0.05 * 
						RAD_TO_DEG +
						p2.r_deg;	
	*/
}

Rotated Movement

In most systems perfectly horizontal or vertical movement is easy to accomplish. Add a few integers to the "x" or the "y" value of your object and the position of it changes.

But what if you want a movement that's more dependent on the orientation and velocity of an object? What if you want to take into account the direction that an object is "facing"?

Let's start with the goal and then look at the code involved.



// In this example we'll make the needed variables outside the update 
// function since they might need to be accessed by multiple functions
// (and it's good for optimization as well).	

// The speed the object will move towards the target.
var speed = 1.5;

// For finding lookat point
var target_x_difference;
var target_y_difference;

// Movement direction.
var p1_dir_x = 0;
var p1_dir_y = 0;

// Update this specific example.
function update(){

	// For this example we use the gradual turning method.
	turn_to_easing( p1, mouse_x_div, mouse_y_div );
	
	// If we wanted the old linear method we would just use the
	// appropriate code.
	/*
	// Get difference when finding a look at point.
	target_x_difference = mouse_x_div - p1.x;
	target_y_difference = mouse_y_div - p1.y;

	// Look at the target point (if applicable)
	p1.r_deg = Math.atan2( target_y_difference, target_x_difference )
						   / (Math.PI/180);
	
	// Now that we have our (optional) rotation creation.
	*/
	
	// Get movement direction of the object
	p1_dir_x = Math.cos( p1.r_deg * 0.0174532925 );
	p1_dir_y = Math.sin( p1.r_deg * 0.0174532925 );
	
	// Essentially, velocity = speed x direction.
	
	// Move object in movement direction, times speed and delta.		
	p1.x += speed * p1_dir_x;
	p1.y += speed * p1_dir_y;

}

Of course in an actual game you won't want ALL of the monsters to automatically start moving towards your player as soon as the map loads. You'll want to insert "if" checks to see if the player is within a certain range / distance or else have them activate when the player intersects a particular volume (placed to be the same shape as the room the monsters are in for instance).

Both of those possibilites are easy enough to implement and use code covered elsewhere.

Again, if you're using Unity 3D, I'll just give you some C# equivalent code. Because I'm cool like that... (this doesn't include the easing section of the code).



using UnityEngine;
using System.Collections;

public class LookAt : MonoBehaviour {

	// A place to store the reference.
	public GameObject target;
	
	// The speed this object will move forward at.
	public float speed_move = 3;
	
	// Difference between object and target positions.
	float difference_x;
	float difference_z;
	
	// Distance objects will be moved (along rotation).
	float move_x;
	float move_z;

	// A result rotation (in euler angles).
	// Because unity wants to assign the entire Vector3.
	Vector3 result_rotation;
	
	// Position to be applied.
	Vector3 result_position;
	
	// Use this for initialization
	void Start () {	
		// Assign object to reference.
		target = GameObject.Find("Cylinder");
		result_rotation = new Vector3(0,0,0);
		result_position = new Vector3(0,0,0);
	}
	
	// Update is called once per frame
	void Update () {
	
		// Find difference in positions.		
		difference_x = target.transform.position.x - 
					   this.transform.position.x;
		difference_z = target.transform.position.z - 
					   this.transform.position.z;					   
		
		// Find the target rotation based on the
		// above differences.
		result_rotation.y = 
			Mathf.Atan2 ( difference_x, difference_z ) / ( Mathf.PI / 180 );
	
		// Apply the final rotation (in euler angles).
		this.transform.eulerAngles = result_rotation;
		
		// Find distance to be moved in each direction,
		// based on the newly found rotation.
		// Here we use the shorthand number for
		// converting Radians to degrees.
		move_x = Mathf.Sin ( result_rotation.y * 0.0174532925f );
		move_z = Mathf.Cos ( result_rotation.y * 0.0174532925f );
				
		// Assign movement values to new position.
		result_position.x += speed_move * move_x * Time.deltaTime;
		result_position.z += speed_move * move_z * Time.deltaTime;
		
		// Move this object in the direction of the rotation.
		this.transform.position = result_position;			
			
	}
	
}