You Spin Me Round
Often when working with 3D objects like characters and space ships you will need to point one object at the other. In this post we will cover two ways you can point objects at each other. The first way creates a matrix to turn your object towards the other. The second covers a way to determine the direction your object needs to turn.
Before we start we will cover some vector operations or what they represent. So here are a couple of the operations we will be using.
The vector cross product of X and Y creates a third vector Z that is orthogonal to both of them. Here are two resources for further information.
http://mathworld.wolfram.com/CrossProduct.html
http://en.wikipedia.org/wiki/Cross_product
The vector dot product represents a couple of things. Geometrically it represents the projection of X onto the unit length vector of Y. So you get the length of the X vector in the Y direction. This will be very useful for us later.
http://mathworld.wolfram.com/DotProduct.html
http://en.wikipedia.org/wiki/Dot_product
In order to turn the character towards another object (in this case, a sphere), we first create a vector between the two objects. Because we are in a right handed coordinate system we will be using –Z as our forward direction. To get the new forward vector of our object we need to take our position and subtract the position of the object.
characterLocalMatrix.Forward = characterLocalMatrix.Translation
- spherePosition;
Now we need to normalize the forward vector because we want our local matrix to be orthonormalized. If the object had been scaled this would be a good time to add the scale back to the object by multiplying the forward vector by the objects scale in the Z direction.
characterLocalMatrix.Forward = Vector3.Normalize(
characterLocalMatrix.Forward);
Next we need to create a new right vector for the object. To do this we are going to cross the new forward vector with the world up vector. We do this because we don’t yet know what the up vector is going to be so we will come back later and fix the up vector. Note that if your objects have abnormal orientations this will not work because it assumes that world up is close to your eventual up vector. The new right vector also needs to be normalized.
characterLocalMatrix.Right = Vector3.Cross(characterLocalMatrix.Forward,
Vector3.Up);
characterLocalMatrix.Right = Vector3.Normalize(characterLocalMatrix.Right);
Finally we need to create our up vector by crossing the new right and forward vectors.
characterLocalMatrix.Up = Vector3.Cross(characterLocalMatrix.Right,
characterLocalMatrix.Forward);
characterLocalMatrix.Up = Vector3.Normalize(characterLocalMatrix.Up);
Our characters local matrix is now ready to use. If you are using the BasicEffect your draw code will look something like this.
effect.World = characterLocalMatrix;
effect.View = viewMatrix;
effect.Projection = projectionMatrix;
Now that you know how to turn directly towards another object, what happens when you want to know what direction your AI should turn in order to face you? We need a way to determine if it is better to turn left or right. We will use the dot product to determine if we should turn left or right. Like the last example the first thing we need to do is to create a vector to the target. We also want the vector to be unit length so we also normalize the vector.
Vector3 turnAt = spherePosition - characterLocalMatrix.Translation;
turnAt.Normalize();
Next we calculate the dot product with our right vector. You might wonder why the right vector when we are trying to turn our forward vector. The reason is because of the behavior of the dot product. By using the right vector the result of the dot product with the turnAt vector will result in the projection of the turnAt vector in the right direction. This will get of the length of the turnAt in the right direction. If the result is positive it means we should turn left and if the result is negative we should turn right.
// Determine direction we need to turn using right vector
float dotResult = Vector3.Dot(turnAt, characterLocalMatrix.Right);
// Turn left
if (dotResult > 0.1f)
{
characterLocalMatrix *= Matrix.CreateRotationY(turnSpeed *
gameTime.ElapsedGameTime.Milliseconds);
}
// Turn right
else if (dotResult < -0.1f)
{
characterLocalMatrix *= Matrix.CreateRotationY(-turnSpeed *
gameTime.ElapsedGameTime.Milliseconds);
}
You will notice that we don’t check dotResult with 0. This is because the character will turn past the object and then turn the other direction. This cycle will go back and forth causing the character to shake. So this technique uses a dead zone. Another option if to turn directly at the target when you get in this zone.
Well that’s the end of my first post. While the information here was geared towards people new to 3D graphics I hope it helped everyone.