O'Reilly logo

Programming Game AI by Example by Mat Buckland

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

A pursuers prey is set by passing the relevant method a pointer to the
target in question. To set up a situation similar to the demo for this behav
-
ior you’d create two agents, one to pursue and the other to wander, just like
this:
Vehicle* prey = new Vehicle(/* params omitted */);
prey->Steering()->WanderOn();
Vehicle* predator = new Vehicle(/* params omitted */);
predator->Steering()->PursuitOn(prey);
Got that? Okay, let’s move on to pursuits opposite: evade.
Evade
Evade is almost the same as pursuit except that this time the evader flees
from the estimated future position.
Vector2D SteeringBehaviors::Evade(const Vehicle* pursuer)
{
/* Not necessary to include the check for facing direction this time */
Vector2D ToPursuer = pursuer->Pos() - m_pVehicle->Pos();
//the look-ahead time is proportional to the distance between the pursuer
//and the evader; and is inversely proportional to the sum of the
//agents' velocities
double LookAheadTime = ToPursuer.Length() /
(m_pVehicle->MaxSpeed() + pursuer->Speed());
//now flee away from predicted future position of the pursuer
return Flee(pursuer->Pos() + pursuer->Velocity() * LookAheadTime);
}
Note that it is not necessary to include the check for facing direction this
time.
Wander
You’ll often find wander a useful ingredient when creating an agent’s
behavior. It’s designed to produce a steering force that will give the impres
-
sion of a random walk through the agent’s environment.
A naive approach is to calculate a random steering force each time step,
but this produces jittery behavior with no ability to achieve long persistent
turns. (Actually, a rather nifty sort of random function, Perlin noise, can be
used to produce smooth turning but this isn’t very CPU friendly. It’s still
something for you to look into though if you get bored on a rainy day —
Perlin noise has many applications.)
Reynolds’ solution is to project a circle in front of the vehicle and steer
toward a target that is constrained to move along the perimeter. Each time
step, a small random displacement is added to this target, and over time it
moves backward and forward along the circumference of the circle,
96 | Chapter 3
The Steering Behaviors
creating a lovely jitter-free alternating motion. This method can be used to
produce a whole range of random motion, from very smooth undulating
turns to wild Strictly Ballroom type whirls and pirouettes depending on the
size of the circle, its distance from the vehicle, and the amount of random
displacement each frame. As they say, a picture is worth a thousand words,
so it’s probably a good idea for you to examine Figure 3.4 to get a better
understanding.
Let me take you through the code step by step. First there are three member
variables wander makes use of:
double m_dWanderRadius;
This is the radius of the constraining circle.
double m_dWanderDistance;
This is the distance the wander circle is projected in front of the agent.
double m_dWanderJitter;
Finally, m_dWanderJitter is the maximum amount of random displacement
that can be added to the target each second. Now for the method itself:
SVector2D SteeringBehaviors::Wander()
{
//first, add a small random vector to the target’s position (RandomClamped
//returns a value between -1 and 1)
m_vWanderTarget += SVector2D(RandomClamped() * m_dWanderJitter,
RandomClamped() * m_dWanderJitter);
m_vWanderTarget is a point constrained to the parameter of a circle of radius
m_dWanderRadius, centered on the vehicle (m_vWanderTargets initial posi
-
tion is set in the constructor of
SteeringBehaviors). Each time step, a small
random displacement is added to the wander target’s position. See Figure
3.5A.
//reproject this new vector back onto a unit circle
m_vWanderTarget.Normalize();
How to Create Autonomously Moving Game Agents | 97
The Steering Behaviors
Figure 3.4
//increase the length of the vector to the same as the radius
//of the wander circle
m_vWanderTarget *= m_dWanderRadius;
The next step is to reproject this new target back onto the wander circle.
This is achieved by normalizing the vector and multiplying it by the radius
of the wander circle. See Figure 3.5B.
//move the target into a position WanderDist in front of the agent
SVector2D targetLocal = m_vWanderTarget + SVector2D(m_dWanderDistance, 0);
//project the target into world space
SVector2D targetWorld = PointToWorldSpace(targetLocal,
m_pVehicle->Heading(),
m_pVehicle->Side(),
m_pVehicle->Pos());
//and steer toward it
return targetWorld - m_pVehicle->Pos();
}
Finally, the new target is moved in front of the vehicle by an amount equal
to
m_dWanderDistance and projected into world space. The steering force is
then calculated as the vector to this position. See Figure 3.5C.
98 | Chapter 3
The Steering Behaviors
Figure 3.5. Steps toward calculating the wander behavior

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required