Immediate
Mode: Character Movement
By: Jack Hoxley
Written: August 2000
Download:
IM_CharMove.Zip
(14kb)
Character movement is not technically
a Direct3D topic  it's actually 2D. It comes in here as it's a perfect model
of how 3D games and engines move the player around, and I'm getting lots of
messages from people wanting to know how this works...
In this sample there will be
no collision detection, and we will only be working in 2 dimensions (X and Y).
The problem is  "If my character is facing 312 degrees, what is the vector
to move it along a line of that angle?"
It basically involves some simple
maths, commonly referred to as the "Unit Circle". We know that if
that character is moving up the Y axis we have the vector [0,1] and if it is
going down [0,+1] Left and right are [1,0] and [+1,0] respectively. But what
if it's not going straight up/down/left/right?
If you look at this diagram I
will explain:
Okay, so we want
to find out the coordinates "X,Y" when we have the angle "A".
We use these two formula's:
X = r * Cos(A)
Y= r * Sin(A)
But because the
radius is always one (so we end up with normalized vectors) we can simplify
this as:
X=Cos(A)
Y=Sin(A)
That's all very
well, but when you're writing the code, you need to use radians rather than
angles  otherwise you get some truly weird results. In Code this will look
like:
Const Pi as
single = 3.14159
Const Rad as single = Pi / 180
Dim X as single,Y as single,A as integer, Theta as single
Theta = A * Rad
X = Cos(Theta)
Y = Sin(Theta) 



You can now get
a correct vector based on the angle that the player is at...
BUT, using Cos(
) and Sin( ) in your game loop is not good for speed  and it's a little awkward
to use. The easiest solution is to use a lookup table. On the first loop we
calculate all the possible angles, then fill an array with the vectors for [easy]
use later.
This code will generate
a lookup table:
'## These parts go in the
(declarations) section
Const Pi = 3.14159
Const Rad = Pi / 180
Dim VectorCircle(360) as D3DVECTOR
Sub GenerateVectors
Dim I as integer,Theta as Double
For I = 0 to 360
Theta = I * Rad
VectorCircle(I).X = Cos(Theta)
VectorCircle(I).Y = Sin(Theta)
'We dont need to use the .Z coordinate...
'You could use it if you want to move up and down slopes
Next I
End Sub 



So we now have a
table of all the vectors we need. Now we need something to use it with. We'll
use a D3DMatrix to translate an object around. Assuming that the player can
press Left/Right to alter the angle and Forward/Backward to move. In a first
person shooter (halflife/Quake) the mouse would control the rotation (left/right).
If you dont understand matrices read through the other tutorials on this page.
Dim matPlayer as D3DMATRIX,matTemp
as D3DMATRIX
Dim vPlayer as D3DVECTOR 'Holds it's position
Const Rad = 3.14159 / 180
'I am assuming that "PlayerRotation" is
a valid integer that represents a number 0  360
Dx.RotateYMatrix matPlayer, PlayerRotation * Rad
vPlayer.x = vPlayer.x + VectorCircle(PlayerRotation).x
vPlayer.z = vPlayer.z + VectorCircle(PlayerRotation).y
TranslateMatrix matTemp, vPlayer
Dx.MatrixMultiply matPlayer, matPlayer, matTemp 



Done. Assuming that
the user can alter the rotation and specify forward or backwards the player
will move smoothly and precisly across the world. You will need to alter the
"vPlayer.x = vPlayer.x +...." line so that it can have a "+"
for moving forward and a "" for moving backwards. Finally, you may
well have noticed that I'm using the vPlayer.z value rather than the vPlayer.y
value  this is because of the way the 3D world works  and Y is up/down. Change
it and see what happens....
You can download
a working example from the top of this page, or from the downloads
page.
