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

Miner Bob and Elsa Communicate
For the purposes of this chapter, I’ve kept the communication between
Miner Bob and Elsa simple. They only have two messages they can use,
and they are enumerated as:
enum message_type
{
Msg_HiHoneyImHome,
Msg_StewReady
};
The gold miner will send Msg_HiHoneyImHome to his wife to let her know
he’s back at the shack.
Msg_StewReady is utilized by the wife to let herself
know when to take dinner out of the oven and for her to communicate to
Miner Bob that food is on the table.
The new state transition diagram for Elsa is shown in Figure 2.6.
Before I show you how telegram events are handled by an agent, let me
demonstrate how they are created, managed, and dispatched.
Message Dispatch and Management
The creation, dispatch, and management of telegrams is handled by a class
named
MessageDispatcher. Whenever an agent needs to send a message, it
calls
MessageDispatcher::DispatchMessage with all the necessary informa
-
tion, such as the message type, the time the message is to be dispatched,
the ID of the recipient, and so on. The
MessageDispatcher uses this infor
-
mation to create a
Telegram, which it either dispatches immediately or
stores in a queue ready to be dispatched at the correct time.
State-Driven Agent Design | 71
Adding Messaging Capabilities to Your FSM
Figure 2.6. Elsa’s new state transition diagram
Before it can dispatch a message, the MessageDispatcher must obtain a
pointer to the entity specified by the sender. Therefore, there must be some
sort of database of instantiated entities provided for the
MessageDispatcher
to refer to — a sort of telephone book where pointers to agents are cross-
referenced by their ID. The database used for the demo is a singleton class
called
EntityManager. Its declaration looks like this:
class EntityManager
{
private:
//to save the ol’ fingers
typedef std::map<int, BaseGameEntity*> EntityMap;
private:
//to facilitate quick lookup the entities are stored in a std::map, in
//which pointers to entities are cross-referenced by their identifying
//number
EntityMap m_EntityMap;
EntityManager(){}
//copy ctor and assignment should be private
EntityManager(const EntityManager&);
EntityManager& operator=(const EntityManager&);
public:
static EntityManager* Instance();
//this method stores a pointer to the entity in the std::vector
//m_Entities at the index position indicated by the entity's ID
//(makes for faster access)
void RegisterEntity(BaseGameEntity* NewEntity);
//returns a pointer to the entity with the ID given as a parameter
BaseGameEntity* GetEntityFromID(int id)const;
//this method removes the entity from the list
void RemoveEntity(BaseGameEntity* pEntity);
};
//provide easy access to the instance of the EntityManager
#define EntityMgr EntityManager::Instance()
When an entity is created it is registered with the entity manager like so:
Miner* Bob = new Miner(ent_Miner_Bob); //enumerated ID
EntityMgr->RegisterEntity(Bob);
A client can now request a pointer to a specific entity by passing its ID to
the method
EntityManager::GetEntityFromID in this way:
Entity* pBob = EntityMgr->GetEntityFromID(ent_Miner_Bob);
72 | Chapter 2
Adding Messaging Capabilities to Your FSM
The client can then use this pointer to call the message handler for that par
-
ticular entity. More on this in a moment, but first let’s look at the way
messages are created and routed between entities.
The MessageDispatcher Class
The class that manages the dispatch of messages is a singleton named
MessageDispatcher. Take a look at the declaration of this class:
class MessageDispatcher
{
private:
//a std::set is used as the container for the delayed messages
//because of the benefit of automatic sorting and avoidance
//of duplicates. Messages are sorted by their dispatch time.
std::set<Telegram> PriorityQ;
//this method is utilized by DispatchMessage or DispatchDelayedMessages.
//This method calls the message handling member function of the receiving
//entity, pReceiver, with the newly created telegram
void Discharge(Entity* pReceiver, const Telegram& msg);
MessageDispatcher(){}
public:
//this class is a singleton
static MessageDispatcher* Instance();
//send a message to another agent.
void DispatchMessage(double delay,
int sender,
int receiver,
int msg,
void* ExtraInfo);
//send out any delayed messages. This method is called each time through
// the main game loop.
void DispatchDelayedMessages();
};
//to make life easier...
#define Dispatch MessageDispatcher::Instance()
The MessageDispatcher class handles messages to be dispatched immedi
-
ately and time stamped messages, which are messages to be delivered at a
specified time in the future. Both these types of messages are created and
managed by the same method:
DispatchMessage. Let’s go through the
source. (In the companion file this method has some additional lines of
code for outputting some informative text to the console. I’ve omitted them
here for clarity.)
State-Driven Agent Design | 73
Adding Messaging Capabilities to Your FSM

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