Moving with Time
Time−based motion doesn't just apply to animation. Movement is also a major
part of your game, and basing movement on time guarantees that all systems will
run your game consistently, regardless of how fast or how slow they are.
The most common use for time−based movement is when you want to move an
object a set distance over a period of time. For example, suppose a player moves
his joystick to the right, so your game responds by moving the on−screen game
character to the right a little bit−let's say 64 units over a period of one
second, which equates to 0.064 units of movement per millisecond.
Using a small function, you can calculate the number of units (as a
floating−point value) to move an object based on the elapsed time between
frames.
float CalcMovement(DWORD ElapsedTime, float
PixelsPerSec)
{
return (PixelsPerSec / 1000.0f * (float)ElapsedTime);
}
As you can see in the CalculateMovement function, you are using the following
calculation:
PixelsPerSec / 1000.0f * ElapsedTime;
The PixelsPerSec variable contains the number of units you want to move over
the period of a second. The 1000.0 value means 1000 milliseconds. Basically,
you're breaking down the number of units to move per millisecond. Finally, you
need to multiply by ElapsedTime to calculate the total movement to apply.
This sort of movement based on time is very basic, but it should not be
overlooked. Knowledge of this function of time−based movement is essential to
using more advanced features, such as smoothly moving objects along a
pre−determined path.
Moving along Paths
As you read in the previous section, time−based movement is determined by
taking the distance to travel, dividing it by 1,000, and multiplying the result
by the elapsed time. In that section, I used an example in which a player
pressed right on the joystick, and his character moved right a set amount of
units. But what about those times when you want an object to move without user
intervention? For instance, suppose a player pushes a button and bullets fly out
of the big gun he is carrying. Those bullets travel along a set path at a set
speed. You can set a velocity for each of those bullets, negating the need to
use paths, but what about those super−bullets in your game that can swoop
through parts of your level, perhaps along a pre−set path?
Those special instances require you to set up the coordinates of the travel
paths in advance, and to do some quick calculations to determine where an object
can be placed inside those paths. And what about moving objects such as
characters, power−ups, and platforms? You guessed it−using paths is the perfect
solution for all your movement needs!
I am going to discuss two of the most frequently used types of paths−straight
and curved. I will start by explaining how to use straight paths.
Following Straight Paths
A straight path is just that−straight. The path moves from the starting point
to the ending point with no breaks or turns. Generally, you define a straight
line using a pair of coordinates−the starting point and the ending point. To
follow a straight path, you only need to walk along the line from Point A to
Point B.
To move an object along a straight path, you must calculate the coordinates
of a point along the line using some simple formulas. For instance, as Figure
2.2 illustrates, to calculate a point at the midpoint of the line using a scalar
value (ranging from 0 to 1), you calculate the difference in the endpoint's
coordinates, multiply by the scalar value, and add the result to the starting
point's coordinates.
// Define starting and ending points of straight path
// Scalar = position to calculate (0 to 1)
D3DXVECTOR3 vecStart = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vecEnd = D3DXVECTOR3(10.0f, 20.0f, 30.0f);
D3DXVECTOR3 vecPos = (vecEnd − vecStart) * Scalar + vecStart;
If you were to set Scalar to 0.5, then vecPos would contain the coordinates
5.0, 10.0, 15.0, which happen to be the midpoint of the path. Now suppose you
don't want to use a scalar value. What about using 3D units instead? For
example, instead of using a scalar value of 0.5, suppose you want to know the
coordinates of a point that is 32 units from the starting coordinates.
To calculate the coordinates using 3D units as a measurement, calculate the
length of the path using the D3DXVec3Length function, and then divide the
position you want to use by the resulting value to obtain a scalar value to use
in the previous calculations.
For example, to find the coordinates of the point that is 32 units into the
path defined previously, you can use the following code:
// Pos = position (in 3−D units) of point in path to
calculate
// Define starting and ending points of straight path
D3DXVECTOR3 vecStart = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
D3DXVECTOR3 vecEnd = D3DXVECTOR3(10.0f, 20.0f, 30.0f);
// Get the length of the path
float Length = D3DXVec3Length(&(vecEnd−vecStart));
// Calculate the scalar by dividing pos by length
float Scalar = Pos / Length;
// Use scalar to calculate coordinates
D3DXVECTOR3 vecPos = (vecEnd − vecStart) * Scalar + vecStart;
Now that you can calculate the exact position of any point along the path,
you can use this knowledge to move an object along the path. Following the
time−based theory of movement, suppose you want to move an object from one point
to another over a period of 1,000 milliseconds. The following code (processed
once per frame) will accomplish this, continuously looping back from the end to
the start of the path in an endless cycle.
// vecPoints[2] = path's starting and ending coordinate
vectors
// Every frame, use the following code to position an object along the straight
path based on the current time.
float Scalar = (float)(timeGetTime() % 1001) / 1000.0f;
D3DXVECTOR3 vecPos = (vecPoints[1] − vecPoints[0]) *
Scalar + vecPoints[0];
// Use vecPos.x, vecPos.y, and vecPos.z coordinates for
object