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

m_pOwner->GetSteering()->WanderOn();
}
The Activate method simply turns on the wander steering behavior (see
Chapter 3 if you need a refresher) and sets the goal’s status to
active.
int Goal_Wander::Process()
{
//if status is inactive, call Activate() and set status to active
ActivateIfInactive();
return m_Status;
}
Goal_Wander::Process is just as straightforward. ActivateIfInactive is
called at the beginning of every goal’s
Process logic. If a goal’s status is
inactive (as it always will be the first time Process is called because
m_Status is set to inactive in the constructor), the Activate method is
called, thereby initializing the goal.
Finally, the
Terminate method switches the wander behavior off.
void Goal_Wander::Terminate()
{
m_pOwner->GetSteering()->WanderOff();
}
Now let’s examine a more complex atomic goal.
Goal_TraverseEdge
This directs a bot along a path edge and continuously monitors its progress
to ensure it doesn’t get stuck. To facilitate this, along with a local copy of
the path edge, it owns data members for recording the time the goal is acti
-
vated and the time the bot is expected to take to traverse the edge. It also
owns a Boolean data member to record whether the edge is the last in the
path. This value is needed to determine what steering behavior should be
used to traverse the edge (seek for normal path edges, arrive for the last).
Here’s its declaration:
class Goal_TraverseEdge : public Goal<Raven_Bot>
{
private:
//the edge the bot will follow
PathEdge m_Edge;
//true if m_Edge is the last in the path.
bool m_bLastEdgeInPath;
//the estimated time the bot should take to traverse the edge
double m_dTimeExpected;
//this records the time this goal was activated
double m_dStartTime;
388 | Chapter 9
Examples of Goals Used by Raven Bots
//returns true if the bot gets stuck
bool isStuck()const;
public:
Goal_TraverseEdge(Raven_Bot* pBot,
PathEdge edge,
bool LastEdge);
//the usual suspects
void Activate();
int Process();
void Terminate();
};
Prior to determining the estimated time required to traverse it, the Activate
method queries the flag field of the graph edge to ascertain if any special
terrain type is associated with it — mud, snow, water, etc. — and the bot’s
behavior is changed accordingly. (This feature is not used by Raven but I
wanted to show you how to handle it should your game use specific terrain
types.)
The method ends with code for activating the appropriate steering
behavior. Here’s the source:
void Goal_TraverseEdge::Activate()
{
m_Status = active;
//the edge behavior flag may specify a type of movement that necessitates a
//change in the bot's behavior as it follows this edge
switch(m_Edge.GetBehaviorFlag())
{
case NavGraphEdge::swim:
{
m_pOwner->SetMaxSpeed(script->GetDouble("Bot_MaxSwimmingSpeed"));
//set appropriate animation
}
break;
case NavGraphEdge::crawl:
{
m_pOwner->SetMaxSpeed(script->GetDouble("Bot_MaxCrawlingSpeed"));
//set appropriate animation
}
break;
}
//record the time the bot starts this goal
m_dStartTime = Clock->GetCurrentTime();
Goal-Driven Agent Behavior
| 389
Examples of Goals Used by Raven Bots
//calculate the expected time required to reach this waypoint. This value
//is used to determine if the bot becomes stuck
m_dTimeExpected =
m_pOwner->CalculateTimeToReachPosition(m_Edge.GetDestination());
//factor in a margin of error for any reactive behavior. 2 seconds
//should be plenty
static const double MarginOfError = 2.0;
m_dTimeExpected += MarginOfError;
//set the steering target
m_pOwner->GetSteering()->SetTarget(m_Edge.GetDestination());
//Set the appropriate steering behavior. If this is the last edge in the path
//the bot should arrive at the position it points to, else it should seek
if (m_bLastEdgeInPath)
{
m_pOwner->GetSteering()->ArriveOn();
}
else
{
m_pOwner->GetSteering()->SeekOn();
}
}
Once the goal has been activated it’s a straightforward matter to process it.
Each time the
Process method is called, the code tests to see if the bot has
become stuck or reached the end of the edge and sets
m_Status accordingly.
int Goal_TraverseEdge::Process()
{
//if status is inactive, call Activate()
ActivateIfInactive();
//if the bot has become stuck return failure
if (isStuck())
{
m_Status = failed;
}
//if the bot has reached the end of the edge return completed
else
{
if (m_pOwner->isAtPosition(m_Edge.GetDestination()))
{
m_Status = completed;
}
}
return m_Status;
}
390 | Chapter 9
Examples of Goals Used by Raven Bots

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