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

elements sorted in order of precedence. I have utilized a std::set as the
priority queue in this example because it automatically discards duplicate
telegrams.
Telegrams are sorted with respect to their time stamp and to this effect,
if you take a look at Telegram.h, you will find that the
< and == operators
have been overloaded. Also note how telegrams with time stamps less than
a quarter of a second apart are to be considered identical. This prevents
many similar telegrams bunching up in the queue and being delivered en
masse, thereby flooding an agent with identical messages. Of course, this
delay will vary according to your game. Games with lots of action produc
-
ing a high frequency of messages will probably require a smaller gap.
The queued telegrams are examined each update step by the method
DispatchDelayedMessages. This function checks the front of the priority
queue to see if any telegrams have expired time stamps. If so, they are dis
-
patched to their recipient and removed from the queue. The code for this
method looks like this:
void MessageDispatcher::DispatchDelayedMessages()
{
//first get current time
double CurrentTime = Clock->GetCurrentTime();
//now peek at the queue to see if any telegrams need dispatching.
//remove all telegrams from the front of the queue that have gone
//past their sell-by date
while( (PriorityQ.begin()->DispatchTime < CurrentTime) &&
(PriorityQ.begin()->DispatchTime > 0) )
{
//read the telegram from the front of the queue
Telegram telegram = *PriorityQ.begin();
//find the recipient
Entity* pReceiver = EntityMgr->GetEntityFromID(telegram.Receiver);
//send the telegram to the recipient
Discharge(pReceiver, telegram);
//and remove it from the queue
PriorityQ.erase(PriorityQ.begin());
}
}
A call to this method must be placed in the game’s main update loop to
facilitate the correct and timely dispatch of any delayed messages.
Message Handling
Once a system for creating and dispatching messages is in place, the han
-
dling of them is relatively easy. The
BaseGameEntity class must be modified
so any subclass can receive messages. This is achieved by declaring
another pure virtual function,
HandleMessage, which all derived classes
State-Driven Agent Design | 75
Adding Messaging Capabilities to Your FSM
must implement. The revised BaseGameEntity base class now looks like
this:
class BaseGameEntity
{
private:
int m_ID;
/* EXTRANEOUS DETAIL REMOVED FOR CLARITY*/
public:
//all subclasses can communicate using messages.
virtual bool HandleMessage(const Telegram& msg)=0;
/* EXTRANEOUS DETAIL REMOVED FOR CLARITY*/
};
In addition, the State base class must also be modified so that a
BaseGameEntitys states can choose to accept and handle messages. The
revised
State class includes an additional OnMessage method as follows:
template <class entity_type>
class State
{
public:
//this executes if the agent receives a message from the
//message dispatcher
virtual bool OnMessage(entity_type*, const Telegram&)=0;
/* EXTRANEOUS DETAIL REMOVED FOR CLARITY*/
};
Finally, the StateMachine class is modified to contain a HandleMessage
method. When a telegram is received by an entity, it is first routed to the
entity’s current state. If the current state does not have code in place to deal
with the message, it’s routed to the entity’s global state’s message handler.
You probably noticed that
OnMessage returns a bool. This is to indicate
whether or not the message has been handled successfully and enables the
code to route the message accordingly.
Here is the listing of the
StateMachine::HandleMessage method:
bool StateMachine::HandleMessage(const Telegram& msg)const
{
//first see if the current state is valid and that it can handle
//the message
if (m_pCurrentState && m_pCurrentState->OnMessage(m_pOwner, msg))
{
return true;
}
//if not, and if a global state has been implemented, send
//the message to the global state
76 | Chapter 2
Adding Messaging Capabilities to Your FSM
if (m_pGlobalState && m_pGlobalState->OnMessage(m_pOwner, msg))
{
return true;
}
return false;
}
And here’s how the Miner class routes messages sent to it:
bool Miner::HandleMessage(const Telegram& msg)
{
return m_pStateMachine->HandleMessage(msg);
}
Figure 2.7 shows the new class architecture.
State-Driven Agent Design | 77
Adding Messaging Capabilities to Your FSM
Figure 2.7. The updated design incorporating messaging

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