Recent Posts

Those who are free of resentful thoughts surely find peace. - Buddha

State Driven Agent Design

Posted on 19th Dec 2016


<-Back to Blogs

1. Why to use Finite State Machine(FSM) Driven approach?

It is because of the following reasons:
a) They are quick and simple to code.
b)They can be easily debug.
c)They have little computational overhead.
d)They are intuitive.
e) They are flexible.

2. What exactly is FSM?

A finite state machine is a device, or a model of a device, which has a finite number of states it can be in at any given time and can operate on input to either make transitions from one state to another or to cause an output or action to take place. A finite state machine can only be in one state at any moment in time.
Here are few examples how FSM have been used in games:

a)The ghosts’ behavior in Pac-Man is implemented as a finite state machine. There is one Evade state, which is the same for all ghosts, and then each ghost has its own Chase state, the actions of which are implemented differently for each ghost. The input of the player eating one of the power pills is the condition for the transition from Chase to Evade. The input of a timer running down is the condition for the transition from Evade to Chase.

b)The NPCs (non-player characters) in RTSs (real-time strategy games) such as Warcraft make use of finite state machines. They have states such as MoveToPosition, Patrol, and FollowPath.

3. Implementing a Finite State Machine.

There are a number of ways of implementing finite state machines.

a) A naive approach is to use a series of if-then statements or the slightly tidier mechanism of a switch statement. Using a switch with an enumerated type to represent the states.

Pros: It is easier to write in if-then-else branching.
Cons: Not suitable to write/design a framework of FSM as it became irritating with many nested if-then-else statement.

b) State Transition Table.

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Current State x Condition x State Transition
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
1. Runaway x Safe x Patrol
2. Attack x Weaker Than Enemy x Run Away
3. Patrol x Threatened AND Stronger than Enemy x Attack
4. Patrol x Threatened AND Weaker than Enemy x Run Away
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Imagine a robot kitten. It’s shiny yet cute, and has wire for whiskers and a slot in its stomach where cartridges — analogous to its states — can be plugged in. Each of these cartridges is programmed with logic, enabling the kitten to perform a specific set of actions. Each set of actions encodes a different behavior; for example, “play with string,” “eat fish,” or “poo on
carpet.” Without a cartridge stuffed inside its belly the kitten is an inanimate metallic sculpture, only able to sit there and look cute… in a Metal Mickey kind of way.

The kitten is very dexterous and has the ability to autonomously exchange its cartridge for another if instructed to do so. By providing the rules that dictate when a cartridge should be switched, it’s possible to string together sequences of cartridge insertions permitting the creation of all sorts of interesting and complicated behavior. These rules are programmed
onto a tiny chip situated inside the kitten’s head, which is analogous to the state transition table we discussed earlier. The chip communicates with the kitten’s internal functions to retrieve the information necessary to process the rules (such as how hungry Kitty is or how playful it’s feeling). As a result, the state transition chip can be programmed with rules like:

IF Kitty_Hungry AND NOT Kitty_Playful
SWITCH_CARTRIDGE eat_fish

All the rules in the table are tested each time step and instructions are sent to Kitty to switch cartridges accordingly. This type of architecture is very flexible, making it easy to expand the
kitten’s repertoire by adding new cartridges. Each time a new cartridge is added, the owner is only required to take a screwdriver to the kitten’s head in order to remove and reprogram the state transition rule chip. It is not necessary to interfere with any other internal circuitry.

c)Embedded Rules

An alternative approach is to embed the rules for the state transitions
within the states themselves.

For instance, the cartridge for “play with string” can monitor the kitty’s level of hunger and instruct it to switch cartridges for the “eat fish” cartridge when it senses hunger rising. In turn the “eat fish” cartridge can monitor the kitten’s bowel and instruct it to switch to the “poo on carpet”cartridge when it senses poo levels are running dangerously high.

Although each cartridge may be aware of the existence of any of the other cartridges, each is a self-contained unit and not reliant on any external logic to decide whether or not it should allow itself to be swapped for an alternative. As a consequence, it’s a straightforward matter to add states or even to swap the whole set of cartridges for a completely new set (maybe ones that make little Kitty behave like a raptor). There’s no need to take a screwdriver to the kitten’s head, only to a few of the cartridges themselves.

Let’s take a look at how this approach is implemented within the context of a video game. Just like Kitty’s cartridges, states are encapsulated as objects and contain the logic required to facilitate state transitions. In addition, all state objects share a common interface: a pure virtual class named State.

class State
{

public:
virtual void Execute (Troll* troll) = 0;
};

Now imagine a Troll class that has member variables for attributes such ashealth, anger, stamina, etc., and an interface allowing a client to query and adjust those values.

A Troll can be given the functionality of a finite state machine by adding a pointer to an instance of a derived object of the State class, and a method permitting a client to change the instance the pointer is pointing to.

class Troll
{
/* ATTRIBUTES OMITTED */
State* m_pCurrentState;
public:
/* INTERFACE TO ATTRIBUTES OMITTED */
void Update()
{
m_pCurrentState->Execute(this);
}
void ChangeState(const State* pNewState)
{
delete m_pCurrentState;
m_pCurrentState = pNewState;
}
};

When the Update method of a Troll is called, it in turn calls the Execute method of the current state type with the this pointer. The current state may then use the Troll interface to query its owner, to adjust its owner’s attributes, or to effect a state transition. In other words, how a Troll behaves when updated can be made completely dependent on the logic in its current state.

Let’s create a couple of states to enable a troll to run away from enemies when it feels

threatened and to sleep when it feels safe.

//----------------------------------State_RunAway
class State_RunAway : public State
{
public:
void Execute(Troll* troll)
{
if (troll->isSafe())
{
troll->ChangeState(new State_Sleep());
}
else
{
troll->MoveAwayFromEnemy();
}
}
};

//----------------------------------State_Sleep
class State_Sleep : public State
{
public:
void Execute(Troll* troll)
{
if (troll->isThreatened())
{
troll->ChangeState(new State_RunAway())
}
else
{
troll->Snore();
}
}
};

As you can see, when updated, a troll will behave differently depending on which of the states m_pCurrentState points to. Both states are encapsulated as objects and both provide the rules effecting state transition. All very neat and tidy.

This architecture is known as the state design pattern and provides an elegant way of implementing state-driven behavior. Although this is a departure from the mathematical formalization of an FSM, it is intuitive, simple to code, and easily extensible.

It also makes it extremely easy to add enter and exit actions to each state; all you have to do is create Enter and Exit methods and adjust the agent’s ChangeState method accordingly.

Case Study: The West World Project

As a practical example of how to create agents that utilize finite state machines, we are going to look at a game environment where agents inhabit an Old West-style gold mining town named West World. Initially there will only be one inhabitant — a gold miner named Miner Bob — but later his wife will also make an appearance.

Any state changes or output from state actions will be sent as text to the console window. I’m using this plain text-only approach as it demonstrates clearly the mechanism of a finite state machine without adding the code clutter of a more complex environment.

There are four locations in West World: a gold mine, a bank where Bob can deposit any nuggets he finds, a saloon in which he can quench his thirst, and home-sweet-home where he can sleep the fatigue of the day away. Exactly where he goes, and what he does when he gets there, is determined by Bob’s current state. He will change states depending on variables like thirst, fatigue, and how much gold he has found hacking away down in the gold mine.

Before we delve into the source code, check out the following sample

output from the WestWorld1 executable.

Miner Bob: Pickin' up a nugget
Miner Bob: Pickin' up a nugget
Miner Bob: Ah'm leavin' the gold mine with mah pockets full o' sweet gold
Miner Bob: Goin' to the bank. Yes siree
Miner Bob: Depositin’ gold. Total savings now: 3
Miner Bob: Leavin' the bank
Miner Bob: Walkin' to the gold mine
Miner Bob: Pickin' up a nugget
Miner Bob: Ah'm leavin' the gold mine with mah pockets full o' sweet gold
Miner Bob: Boy, ah sure is thusty! Walkin' to the saloon
Miner Bob: That's mighty fine sippin liquor
Miner Bob: Leavin' the saloon, feelin' good
Miner Bob: Walkin' to the gold mine
Miner Bob: Pickin' up a nugget
Miner Bob: Pickin' up a nugget
Miner Bob: Ah'm leavin' the gold mine with mah pockets full o' sweet gold
Miner Bob: Goin' to the bank. Yes siree
Miner Bob: Depositin' gold. Total savings now: 4
Miner Bob: Leavin' the bank
Miner Bob: Walkin' to the gold mine
Miner Bob: Pickin' up a nugget
Miner Bob: Pickin' up a nugget
Miner Bob: Ah'm leavin' the gold mine with mah pockets full o' sweet gold
Miner Bob: Boy, ah sure is thusty! Walkin' to the saloon
Miner Bob: That's mighty fine sippin' liquor
Miner Bob: Leavin' the saloon, feelin' good
Miner Bob: Walkin' to the gold mine
Miner Bob: Pickin' up a nugget
Miner Bob: Ah'm leavin' the gold mine with mah pockets full o' sweet gold
Miner Bob: Goin' to the bank. Yes siree
Miner Bob: Depositin' gold. Total savings now: 5
Miner Bob: Woohoo! Rich enough for now. Back home to mah li'l lady
Miner Bob: Leavin' the bank
Miner Bob: Walkin' home
Miner Bob: ZZZZ...
Miner Bob: ZZZZ...
Miner Bob: ZZZZ...
Miner Bob: ZZZZ...
Miner Bob: What a God-darn fantastic nap! Time to find more gold

In the output from the program, each time you see Miner Bob change location he is changing state. All the other events are the actions that take place within the states.

Let us examine a little about the code structure of the demo.

1. The BaseGameEntity class declaration looks like this:

class BaseGameEntity
{
private:

//every entity has a unique identifying number
int m_ID;

//this is the next valid ID. Each time a BaseGameEntity is instantiated
//this value is updated
static int m_iNextValidID;

//this is called within the constructor to make sure the ID is set
//correctly. It verifies that the value passed to the method is greater
//or equal to the next valid ID, before setting the ID and incrementing
//the next valid ID
void SetID(int val);

public:
BaseGameEntity(int id)
{
SetID(id);
}

virtual ~BaseGameEntity(){}

//all entities must implement an update function
virtual void Update()=0;

int ID( )const{return m_ID;}
};

For reasons that will become obvious later in the chapter, it’s very important for each entity in your game to have a unique identifier. Therefore, on instantiation, the ID passed to the constructor is tested in the SetID method to make sure it’s unique. If it is not, the program will exit with an assertion failure. In the example given in this post, the entities will use an enumerated
value as their unique identifier. These can be found in the file EntityNames.h as ent_Miner_Bob and ent_Elsa.

2. The Miner class

The Miner class is derived from the BaseGameEntity class and contains data members representing the various attributes a Miner possesses, such as its health, its level of fatigue, its position, and so forth.
Like the troll example shown earlier, a Miner owns a pointer to an instance of a State class in addition to a method for changing what State that pointer points to.

class Miner : public BaseGameEntity
{
private:
//a pointer to an instance of a State
State* m_pCurrentState;
// the place where the miner is currently situated
location_type m_Location;
//how many nuggets the miner has in his pockets
int m_iGoldCarried;
//how much money the miner has deposited in the bank
int m_iMoneyInBank;
//the higher the value, the thirstier the miner
int m_iThirst;
//the higher the value, the more tired the miner
int m_iFatigue;
public:
Miner(int ID);
//this must be implemented
void Update();
//this method changes the current state to the new state
void ChangeState(State* pNewState);
/* bulk of interface omitted */
};

/*
The Miner::Update method is straightforward; it simply increments the m_iThirst value before calling the Execute method of the current state. It looks like this:
*/
void Miner::Update()
{
m_iThirst += 1;

if (m_pCurrentState)
{
m_pCurrentState->Execute(this);
}

}


Now that you’ve seen how the Miner class operates, let’s take a look at each of the states a miner can find itself in.

2.1 The Miner States

The gold miner will be able to enter one of four states. Here are the names of those states followed by a description of the actions and state transitions that occur within those states:

1. EnterMineAndDigForNugget: If the miner is not located at the gold mine, he changes location.
If already at the gold mine, he digs for nuggets of gold. When his pockets are full, Bob changes state to VisitBankAndDepositGold, and if while digging he finds himself thirsty, he will stop and change state to QuenchThirst.

2. VisitBankAndDepositGold: In this state the miner will walk to the bank and deposit any nuggets he is carrying. If he then considers himself wealthy enough, he will change state to GoHomeAnd-
SleepTilRested. Otherwise he will change state to EnterMine-AndDigForNugget.

3. GoHomeAndSleepTilRested: In this state the miner will return to his shack and sleep until his fatigue level drops below an acceptable level. He will then change state to EnterMineAndDigForNugget.

4. QuenchThirst: If at any time the miner feels thirsty (diggin’ for gold is thirsty work, don’t ya know), he changes to this state and visits the saloon in order to buy a whiskey. When his thirst is quenched, he changes state to EnterMineAndDigForNugget.

Sometimes it’s hard to follow the flow of the state logic from reading a text description like this, so it’s often helpful to pick up pen and paper and draw a state transition diagram for your game agents. Figure below shows the state transition diagram for the gold miner.

The bubbles represent the individual states and the lines between them the available transitions.
A diagram like this is better on the eyes and can make it much easier to spot any errors in the logic flow.

 


Mine State

The State Design Pattern Revisited

You saw a brief description of this design pattern a few pages back, but it won’t hurt to recap.

Each of a game agent’s states is implemented as a unique class and each agent holds a pointer to an instance of its current state.

/----------------------------------State_RunAway
class State_RunAway : public State
{
public:
void Execute(Troll* troll)
{
if (troll->isSafe())
{
troll->ChangeState(new State_Sleep());
}
else
{
troll->MoveAwayFromEnemy();
}
}
};

//----------------------------------State_Sleep
class State_Sleep : public State
{
public:
//overriding the Execute of State class.
void Execute(Troll* troll)
{
if (troll->isThreatened())
{
troll->ChangeState(new State_RunAway())
}
else
{
troll->Snore();
}
}
};

An agent also implements a ChangeState member function that can be called to facilitate the switching of states whenever a state transition is required.

class Troll
{
/* ATTRIBUTES OMITTED */
State* m_pCurrentState;
public:
/* INTERFACE TO ATTRIBUTES OMITTED */
void Update()
{
m_pCurrentState->Execute(this);
}
void ChangeState(const State* pNewState)
{
delete m_pCurrentState;
m_pCurrentState = pNewState;
}
};

The logic for determining any state transitions is contained within each State class. All state classes are derived from an abstract base class, thereby defining a common interface.

class State
{

public:
virtual void Execute (Troll* troll) = 0;
};

So far so good. You know this much already.

Earlier in the chapter it was mentioned that it’s usually favorable for each state to have associated enter and exit actions.

This permits the programmer to write logic that is only executed once at state entry or exit and
increases the flexibility of an FSM a great deal. With these features in mind, let’s take a look at an enhanced State base class.

class State
{
public:
virtual ~State(){}
//this will execute when the state is entered
virtual void Enter(Miner*)=0;
//this is called by the miner’s update function each update step
virtual void Execute(Miner*)=0;
//this will execute when the state is exited
virtual void Exit(Miner*)=0;
}

These additional methods are only called when a Miner changes state.

When a state transition occurs, the Miner::ChangeState method first calls the Exit method of the current state, then it assigns the new state to the current state, and finishes by calling the Enter method of the new state (which is now the current state).

I think code is clearer than words in this instance, so here’s the listing for the ChangeState method:

void Miner::ChangeState(State* pNewState)
{
//make sure both states are valid before attempting to
//call their methods
assert (m_pCurrentState && pNewState);
//call the exit method of the existing state
m_pCurrentState->Exit(this);
//change state to the new state
m_pCurrentState = pNewState;
//call the entry method of the new state
m_pCurrentState->Enter(this);
}

Notice how a Miner passes the this pointer to each state, enabling the state to use the Miner interface to access any relevant data.

$$TIP: The state design pattern is also useful for structuring the main components
of your game flow. For example, you could have a menu state, a save state, a
paused state, an options state, a run state, etc.

Each of the four possible states a Miner may access are derived from the State class, giving us these concrete classes: EnterMineAndDigForNugget, VisitBankAndDepositGold, GoHomeAndSleepTilRested, and QuenchThirst.

The Miner::m_pCurrentState pointer is able to point to any of these states.

When the Update method of Miner is called, it in turn calls the Execute method of the currently active state with the this pointer as a parameter.

These class relationships may be easier to understand if you examine the simplified UML class diagram shown in Figure below.


UML Diagram of Miner Bob's state machine implementation.

Each concrete state is implemented as a singleton object. This is to ensure that there is only one instance of each state, which agents share(those of you unsure of what a singleton is, please read the below). Using singletons makes the design more efficient because they remove the need to allocate and deallocate memory every time a state change is made. This is particularly important if you have many agents sharing a complex FSM and/or you are developing for a machine with limited resources.

NOTE!! I prefer to use singletons for the states for the reasons I’ve already given, but there is one drawback. Because they are shared between clients, singleton states are unable to make use of their own local, agent-specific data.
For instance, if an agent uses a state that when entered should move it to an arbitrary position, the position cannot be stored in the state itself (because the position may be different for each agent that is using the state).

Instead, it would have to be stored somewhere externally and be accessed by the state via the agent’s interface. This is not really a problem if your states are accessing only one or two pieces of data, but if you find that the states you have designed are repeatedly accessing lots of external data, it’s probably worth considering disposing of the singleton design and writing a few lines of code to manage the allocation and deallocation of state memory.

The Singleton Design Pattern

Often it’s useful to guarantee that an object is only instantiated once and/or that it is globally accessible.

For example, in game designs that have environments consisting of many different entity types — players,monsters, projectiles, plant pots, etc. — there is usually a “manager” object that handles the creation, deletion, and management of such objects.

It is only necessary to have one instance of this object — a singleton— and it is convenient to make it globally accessible because many other objects will require access to it.

The singleton pattern ensures both these qualities. There are many ways of implementing a singleton (do a search at google.com and you’ll see what I mean).

I prefer to use a static method, Instance, that returns a pointer to a static instance of the class. Here’s an example:

/* ------------------ MyClass.h -------------------- */

#ifndef MY_SINGLETON
#define MY_SINGLETON
class MyClass
{
private:
// member data
int m_iNum;

//constructor is private
MyClass(){}

//copy ctor and assignment should be private
MyClass(const MyClass &);

MyClass& operator=(const MyClass &);

public:
//strictly speaking, the destructor of a singleton should be private but some
//compilers have problems with this so I’ve left them as public in all the
//examples in this book
~MyClass();

//methods
int GetVal( ) const{return m_iNum;}

static MyClass* Instance();
};
#endif


/* -------------------- MyClass.cpp ------------------- */
//this must reside in the cpp file; otherwise, an instance will be created
//for every file in which the header is included
MyClass* MyClass::Instance()
{
static MyClass instance;
return &instance;
}

Member variables and methods can now be accessed via the Instance method like so:

int num = MyClass::Instance()->GetVal();

Because I’m lazy and don’t like writing out all that syntax each time I want to access a singleton, I usually #define something like this:

#define MyCls MyClass::Instance()

Using this new syntax I can simply write:

int num = MyCls->GetVal();

Much easier, don’t you think?

NOTE!! If singletons are a new concept to you, and you decide to search the Internet for further information, you will discover they fuel many a good argument about the design of object-oriented software.

Oh yes, programmers love to argue about this stuff, and nothing stokes a dispute better than the discussion of global variables or objects that masquerade as globals, such as singletons.

My own stance on the matter is to use them wherever I think they provide a convenience and, in my opinion, do not compromise the design. I recommend you read the arguments for and against though, and come to your own conclusions. A good starting place is here:
http://c2.com/cgi/wiki?SingletonPattern

Okay, let’s see how everything fits together by examining the complete code for one of the miner states.

1. EnterMineAndDigForNugget State

In this state the miner should change location to be at the gold mine. Once at the gold mine he should dig for gold until his pockets are full, when he should change state to VisitBankAndDepositNugget.

If the miner gets thirsty while digging he should change state to QuenchThirst.
Because concrete states simply implement the interface defined in the virtual base class State, their declarations are very straightforward:


Mine States


class EnterMineAndDigForNugget : public State
{
private:
EnterMineAndDigForNugget(){}
/* copy ctor and assignment op omitted */
public:
//this is a singleton
static EnterMineAndDigForNugget* Instance();

virtual void Enter(Miner* pMiner);
virtual void Execute(Miner* pMiner);
virtual void Exit(Miner* pMiner);
};

As you can see, it’s just a formality. Let’s take a look at each of the methods in turn.

a. EnterMineAndDigForNugget::Enter
The code for the Enter method of EnterMineAndDigForNugget is as follows:

void EnterMineAndDigForNugget::Enter(Miner* pMiner)
{
//if the miner is not already located at the gold mine, he must
//change location to the gold mine
if (pMiner->Location() != goldmine)
{
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "Walkin' to the gold mine";
pMiner->ChangeLocation(goldmine);
}
}

This method is called when a miner first enters the EnterMineAndDig-ForNugget state. It ensures that the gold miner is located at the gold mine.

An agent stores its location as an enumerated type and the ChangeLocation method changes this value to switch locations.

b.EnterMineAndDigForNugget::Execute

The Execute method is a little more complicated and contains logic that can change a miner’s state. (Don’t forget that Execute is the method called each update step from Miner::Update.)

void EnterMineAndDigForNugget::Execute(Miner* pMiner)
{
//the miner digs for gold until he is carrying in excess of MaxNuggets.
//If he gets thirsty during his digging he stops work and
//changes state to go to the saloon for a whiskey.

pMiner->AddToGoldCarried(1);

//diggin' is hard work
pMiner->IncreaseFatigue();
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "Pickin' up a nugget";

//if enough gold mined, go and put it in the bank

if (pMiner->PocketsFull())
{
pMiner->ChangeState(VisitBankAndDepositGold::Instance());
}
//if thirsty go and get a whiskey

if (pMiner->Thirsty())
{
pMiner->ChangeState(QuenchThirst::Instance());
}
}


Note here how the Miner::ChangeState method is called using QuenchThirst’s or VisitBankAndDepositGold’s Instance member, which provides a pointer to the unique instance of that class.

c.EnterMineAndDigForNugget::Exit

The Exit method of EnterMineAndDigForNugget outputs a message telling us that the gold miner is leaving the mine.

void EnterMineAndDigForNugget::Exit(Miner* pMiner)
{
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "Ah'm leavin' the gold mine with mah pockets full o' sweet gold";

}

I hope an examination of the preceding three methods helps clear up anyconfusion you may have been experiencing and that you can now see how each state is able to modify the behavior of an agent or effect a transition into another state.

You may find it useful at this stage to load up the WestWorld1 project into your IDE and scan codes here.

In particular, check out all the states in MinerOwnedStates.cpp and examine the Miner class to familiarize yourself with its member variables. Above all else, make sure you understand how the state design pattern works before you read any further.

If you are a little unsure, please take the time to go over the previous few pages until you feel comfortable with the concept.

You have seen how the use of the state design pattern provides a very flexible mechanism for state-driven agents. It’s extremely easy to add additional states as and when required. Indeed, should you so wish, you can switch an agent’s entire state architecture for an alternative one. This can
be useful if you have a very complicated design that would be better organized as a collection of several separate smaller state machines.

For example, the state machine for a first-person shooter (FPS) like Unreal 2 tends to be large and complex. When designing the AI for a game of this sort you may find it preferable to think in terms of several smaller state machines representing functionality like “defend the flag” or “explore
map,” which can be switched in and out when appropriate. The state design pattern makes this easy to do.

 


UML Diagram of Miner Bob's state machine implementation.


1. BaseGameEntity
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
BaseGameEntity.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifndef ENTITY_H
#define ENTITY_H
//------------------------------------------------------------------------
//
// Name: BaseGameEntity.h
//
// Desc: Base class for a game object
//
// Author: Mat Buckland 2002 (fup@ai-junkie.com)
//
//------------------------------------------------------------------------


class BaseGameEntity
{

private:

//every entity must have a unique identifying number
int m_ID;

//this is the next valid ID. Each time a BaseGameEntity is instantiated
//this value is updated
static int m_iNextValidID;

//this must be called within the constructor to make sure the ID is set
//correctly. It verifies that the value passed to the method is greater
//or equal to the next valid ID, before setting the ID and incrementing
//the next valid ID
void SetID(int val);

public:

BaseGameEntity(int id)
{
SetID(id);
}

virtual ~BaseGameEntity(){}

//all entities must implement an update function
virtual void Update()=0;

int ID( )const{return m_ID;}
};

#endif

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
BaseGameEntity.cpp
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#include "BaseGameEntity.h"
#include <cassert>

int BaseGameEntity::m_iNextValidID = 0;

//----------------------------- SetID -----------------------------------------
//
// this must be called within each constructor to make sure the ID is set
// correctly. It verifies that the value passed to the method is greater
// or equal to the next valid ID, before setting the ID and incrementing
// the next valid ID
//-----------------------------------------------------------------------------
void BaseGameEntity::SetID(int val)
{
//make sure the val is equal to or greater than the next available ID
assert ( (val >= m_iNextValidID) && "<BaseGameEntity::SetID>: invalid ID");

m_ID = val;

m_iNextValidID = m_ID + 1;
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
2.Location
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Location.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

#ifndef LOCATIONS_H
#define LOCATIONS_H


enum location_type
{
shack,
goldmine,
bank,
saloon
};


#endif

UML Diagram of Miner Bob's state machine implementation.

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
3.State
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
State.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifndef STATE_H
#define STATE_H
//------------------------------------------------------------------------
//
// Name: State.h
//
// Desc: abstract base class to define an interface for a state
//
// Author: Mat Buckland 2002 (fup@ai-junkie.com)
//
//------------------------------------------------------------------------

class Miner;

class State
{
public:

virtual ~State(){}

//this will execute when the state is entered
virtual void Enter(Miner*)=0;

//this is the state's normal update function
virtual void Execute(Miner*)=0;

//this will execute when the state is exited. (My word, isn't
//life full of surprises... ;o))
virtual void Exit(Miner*)=0;

};


#endif


UML Diagram of Miner Bob's state machine implementation.

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
4.MinerOwnedStates
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
MinerOwnedStates.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifndef MINER_OWNED_STATES_H
#define MINER_OWNED_STATES_H
//------------------------------------------------------------------------
//
// Name: MinerOwnedStates.h
//
// Desc: All the states that can be assigned to the Miner class
//
// Author: Mat Buckland 2002 (fup@ai-junkie.com)
//
//------------------------------------------------------------------------
#include "State.h"

class Miner;

//------------------------------------------------------------------------
//
// In this state the miner will walk to a goldmine and pick up a nugget
// of gold. If the miner already has a nugget of gold he'll change state
// to VisitBankAndDepositGold. If he gets thirsty he'll change state
// to QuenchThirst
//------------------------------------------------------------------------
class EnterMineAndDigForNugget : public State
{
private:

EnterMineAndDigForNugget(){}

//copy ctor and assignment should be private
EnterMineAndDigForNugget(const EnterMineAndDigForNugget&);
EnterMineAndDigForNugget& operator=(const EnterMineAndDigForNugget&);

public:

//this is a singleton
static EnterMineAndDigForNugget* Instance();

virtual void Enter(Miner* miner);

virtual void Execute(Miner* miner);

virtual void Exit(Miner* miner);
};

//------------------------------------------------------------------------
//
// Entity will go to a bank and deposit any nuggets he is carrying. If the
// miner is subsequently wealthy enough he'll walk home, otherwise he'll
// keep going to get more gold
//------------------------------------------------------------------------
class VisitBankAndDepositGold : public State
{
private:

VisitBankAndDepositGold(){}

//copy ctor and assignment should be private
VisitBankAndDepositGold(const VisitBankAndDepositGold&);
VisitBankAndDepositGold& operator=(const VisitBankAndDepositGold&);

public:

//this is a singleton
static VisitBankAndDepositGold* Instance();

virtual void Enter(Miner* miner);

virtual void Execute(Miner* miner);

virtual void Exit(Miner* miner);
};


//------------------------------------------------------------------------
//
// miner will go home and sleep until his fatigue is decreased
// sufficiently
//------------------------------------------------------------------------
class GoHomeAndSleepTilRested : public State
{
private:

GoHomeAndSleepTilRested(){}

//copy ctor and assignment should be private
GoHomeAndSleepTilRested(const GoHomeAndSleepTilRested&);
GoHomeAndSleepTilRested& operator=(const GoHomeAndSleepTilRested&);

public:

//this is a singleton
static GoHomeAndSleepTilRested* Instance();

virtual void Enter(Miner* miner);

virtual void Execute(Miner* miner);

virtual void Exit(Miner* miner);
};


//------------------------------------------------------------------------
//
//------------------------------------------------------------------------
class QuenchThirst : public State
{
private:

QuenchThirst(){}

//copy ctor and assignment should be private
QuenchThirst(const QuenchThirst&);
QuenchThirst& operator=(const QuenchThirst&);

public:

//this is a singleton
static QuenchThirst* Instance();

virtual void Enter(Miner* miner);

virtual void Execute(Miner* miner);

virtual void Exit(Miner* miner);
};

#endif
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
5. EntityNames
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
EntityNames.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifndef NAMES_H
#define NAMES_H

#include <string>

enum
{
ent_Miner_Bob,

ent_Elsa
};

inline std::string GetNameOfEntity(int n)
{
switch(n)
{
case ent_Miner_Bob:

return "Miner Bob";

case ent_Elsa:

return "Elsa";

default:

return "UNKNOWN!";
}
}

#endif
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
6.MinerOwnedState.cpp
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#include "MinerOwnedStates.h"
#include "State.h"
#include "Miner.h"
#include "Locations.h"
#include "misc/ConsoleUtils.h"
#include "EntityNames.h"

#include <iostream>
using std::cout;

//define this to output to a file
#ifdef TEXTOUTPUT
#include <fstream>
extern std::ofstream os;
#define cout os
#endif

//--------------------------------------methods for EnterMineAndDigForNugget

EnterMineAndDigForNugget* EnterMineAndDigForNugget::Instance()
{
static EnterMineAndDigForNugget instance;

return &instance;
}

void EnterMineAndDigForNugget::Enter(Miner* pMiner)
{
//if the miner is not already located at the goldmine, he must
//change location to the gold mine
if (pMiner->Location() != goldmine)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Walkin' to the goldmine";

pMiner->ChangeLocation(goldmine);
}
}


void EnterMineAndDigForNugget::Execute(Miner* pMiner)
{
//the miner digs for gold until he is carrying in excess of MaxNuggets.
//If he gets thirsty during his digging he packs up work for a while and
//changes state to go to the saloon for a whiskey.
pMiner->AddToGoldCarried(1);

pMiner->IncreaseFatigue();

SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Pickin' up a nugget";

//if enough gold mined, go and put it in the bank
if (pMiner->PocketsFull())
{
pMiner->ChangeState(VisitBankAndDepositGold::Instance());
}

if (pMiner->Thirsty())
{
pMiner->ChangeState(QuenchThirst::Instance());
}
}


void EnterMineAndDigForNugget::Exit(Miner* pMiner)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "Ah'm leavin' the goldmine with mah pockets full o' sweet gold";
}

//----------------------------------------methods for VisitBankAndDepositGold

VisitBankAndDepositGold* VisitBankAndDepositGold::Instance()
{
static VisitBankAndDepositGold instance;

return &instance;
}


void VisitBankAndDepositGold::Enter(Miner* pMiner)
{
//on entry the miner makes sure he is located at the bank
if (pMiner->Location() != bank)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Goin' to the bank. Yes siree";

pMiner->ChangeLocation(bank);
}
}


void VisitBankAndDepositGold::Execute(Miner* pMiner)
{

//deposit the gold
pMiner->AddToWealth(pMiner->GoldCarried());

pMiner->SetGoldCarried(0);

SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "Depositing gold. Total savings now: "<< pMiner->Wealth();

//wealthy enough to have a well earned rest?
if (pMiner->Wealth() >= ComfortLevel)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "WooHoo! Rich enough for now. Back home to mah li'lle lady";

pMiner->ChangeState(GoHomeAndSleepTilRested::Instance());
}

//otherwise get more gold
else
{
pMiner->ChangeState(EnterMineAndDigForNugget::Instance());
}
}


void VisitBankAndDepositGold::Exit(Miner* pMiner)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Leavin' the bank";
}


//----------------------------------------methods for GoHomeAndSleepTilRested

GoHomeAndSleepTilRested* GoHomeAndSleepTilRested::Instance()
{
static GoHomeAndSleepTilRested instance;

return &instance;
}

void GoHomeAndSleepTilRested::Enter(Miner* pMiner)
{
if (pMiner->Location() != shack)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Walkin' home";

pMiner->ChangeLocation(shack);
}
}

void GoHomeAndSleepTilRested::Execute(Miner* pMiner)
{
//if miner is not fatigued start to dig for nuggets again.
if (!pMiner->Fatigued())
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "What a God darn fantastic nap! Time to find more gold";

pMiner->ChangeState(EnterMineAndDigForNugget::Instance());
}

else
{
//sleep
pMiner->DecreaseFatigue();

SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "ZZZZ... ";
}
}

void GoHomeAndSleepTilRested::Exit(Miner* pMiner)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Leaving the house";
}


//------------------------------------------------methods for QuenchThirst

QuenchThirst* QuenchThirst::Instance()
{
static QuenchThirst instance;

return &instance;
}

void QuenchThirst::Enter(Miner* pMiner)
{
if (pMiner->Location() != saloon)
{
pMiner->ChangeLocation(saloon);

SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Boy, ah sure is thusty! Walking to the saloon";
}
}

void QuenchThirst::Execute(Miner* pMiner)
{
if (pMiner->Thirsty())
{
pMiner->BuyAndDrinkAWhiskey();

SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "That's mighty fine sippin liquer";

pMiner->ChangeState(EnterMineAndDigForNugget::Instance());
}

else
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\nERROR!\nERROR!\nERROR!";
}
}

void QuenchThirst::Exit(Miner* pMiner)
{
SetTextColor(FOREGROUND_RED| FOREGROUND_INTENSITY);
cout << "\n" << GetNameOfEntity(pMiner->ID()) << ": " << "Leaving the saloon, feelin' good";
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
7.ConsoleUtils
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
ConsoleUtils.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifndef CONSOLE_UTILS_H
#define CONSOLE_UTILS_H
//------------------------------------------------------------------------
//
// Name: ConsoleUtils.h
//
// Desc: Just a few handy utilities for dealing with consoles
//
// Author: Mat Buckland (fup@ai-junkie.com)
//
//------------------------------------------------------------------------
#include <windows.h>
#include <conio.h>
#include <iostream>

//default text colors can be found in wincon.h
inline void SetTextColor(WORD colors)
{
HANDLE hConsole=GetStdHandle(STD_OUTPUT_HANDLE);

SetConsoleTextAttribute(hConsole, colors);
}

inline void PressAnyKeyToContinue()
{
//change text color to white
SetTextColor(FOREGROUND_BLUE| FOREGROUND_RED | FOREGROUND_GREEN);

std::cout << "\n\nPress any key to continue" << std::endl;

while (!kbhit()){}

return;
}
#endif
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
8. Miner
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Miner.h
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#ifndef MINER_H
#define MINER_H
//------------------------------------------------------------------------
//
// Name: Miner.h
//
// Desc: A class defining a goldminer.
//
// Author: Adarsh Kumar Maurya
//
//------------------------------------------------------------------------
#include <string>
#include <cassert>

#include "BaseGameEntity.h"
#include "Locations.h"

class State;
//the amount of gold a miner must have before he feels comfortable
const int ComfortLevel = 5;
//the amount of nuggets a miner can carry
const int MaxNuggets = 3;
//above this value a miner is thirsty
const int ThirstLevel = 5;
//above this value a miner is sleepy
const int TirednessThreshold = 5;

class Miner : public BaseGameEntity
{
private:
State* m_pCurrentState;
//the enum location_type from Location.h
location_type m_Location;
//how many nuggets the miner has in his pockets
int m_iGoldCarried;
int m_iMoneyInBank;
//the higher the value, the thirstier the miner
int m_iThirst;
//the higher the value, the more tired the miner
int m_iFatigue;

public:
Miner(int id);
//this must be implemented
void Update();

//this method changes the current state to the new state. It first
//calls the Exit() method of the current state, then assigns the
//new state to m_pCurrentState and finally calls the Entry()
//method of the new state.
void ChangeState(State* new_state);

location_type Location( )const{return m_Location;}
void ChangeLocation(const location_type loc){m_Location=loc;}

int GoldCarried()const{return m_iGoldCarried;}
void SetGoldCarried(const int val){m_iGoldCarried = val;}
void AddToGoldCarried(const int val);
bool PocketsFull()const{return m_iGoldCarried >= MaxNuggets;}

bool Fatigued()const;
void DecreaseFatigue(){m_iFatigue -= 1;}
void IncreaseFatigue(){m_iFatigue += 1;}

int Wealth()const{return m_iMoneyInBank;}
void SetWealth(const int val){m_iMoneyInBank = val;}
void AddToWealth(const int val);

bool Thirsty()const;
void BuyAndDrinkAWhiskey(){m_iThirst = 0; m_iMoneyInBank-=2;}

};
#endif
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Miner.cpp
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
#include "Miner.h"
#include "MinerOwnedStates.h"

Miner::Miner(int id):BaseGameEntity(id),
m_Location(shack),
m_iGoldCarried(0),
m_iMoneyInBank(0),
m_iThirst(0),
m_iFatigue(0),
m_pCurrentState(GoHomeAndSleepTilRested::Instance())

{}

//--------------------------- ChangeState -------------------------------------
//-----------------------------------------------------------------------------
void Miner::ChangeState(State* pNewState)
{
//make sure both states are both valid before attempting to
//call their methods
assert (m_pCurrentState && pNewState);

//call the exit method of the existing state
m_pCurrentState->Exit(this);

//change state to the new state
m_pCurrentState = pNewState;

//call the entry method of the new state
m_pCurrentState->Enter(this);

//-----------------------------------------------------------------------------
void Miner::AddToGoldCarried(const int val)
{
m_iGoldCarried += val;

if (m_iGoldCarried < 0) m_iGoldCarried = 0;
}


//-----------------------------------------------------------------------------
void Miner::AddToWealth(const int val)
{
m_iMoneyInBank += val;

if (m_iMoneyInBank < 0) m_iMoneyInBank = 0;
}


//-----------------------------------------------------------------------------
bool Miner::Thirsty()const
{
if (m_iThirst >= ThirstLevel){return true;}

return false;
}


//-----------------------------------------------------------------------------
void Miner::Update()
{
m_iThirst += 1;

if (m_pCurrentState)
{
m_pCurrentState->Execute(this);
}
}


//-----------------------------------------------------------------------------
bool Miner::Fatigued()const
{
if (m_iFatigue > TirednessThreshold)
{
return true;
}

return false;
}
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
PART II

Making the State Base Class Reusable

As the design stands, it’s necessary to create a separate State base class for each character type to derive its states from. Instead, let’s make it reusable by turning it into a class template.

template <class entity_type>
class State
{
public:
virtual void Enter(entity_type*)=0;
virtual void Execute(entity_type*)=0;
virtual void Exit(entity_type*)=0;
virtual ~State(){}
};

The declaration for a concrete state — using the EnterMineAndDigFor-
Nugget miner state as an example — now looks like this:

class EnterMineAndDigForNugget : public State<Miner>
{
public:
/* OMITTED */

};

This, as you will see shortly, makes life easier in the long run.

Global States and State Blips

More often than not, when designing finite state machines you will end up with code that is duplicated in every state. For example, in the popular game The Sims by Maxis, a Sim may feel the urge of nature come upon it and have to visit the bathroom to relieve itself. This urge may occur in any state the Sim may be in and at any time.

Given the current design, to bestow the gold miner with this type of behavior, duplicate conditional logic would have to be added to every one of his states, or alternatively, placed into the Miner::Update function. While the latter solution is acceptable, it’s better to create a global state that is called every time the FSM is updated. That way, all the logic for the FSM is contained within the states and not in the agent class that owns the FSM. To implement a global state, an additional member variable is required:

//notice how now that State is a class template we have to declare the entity type

State<Miner>* m_pGlobalState;

In addition to global behavior, occasionally it will be convenient for an agent to enter a state with the condition that when the state is exited, the agent returns to its previous state. I call this behavior a state blip. For example, just as in The Sims, you may insist that your agent can visit the bathroom at any time, yet make sure it always returns to its prior state.

To give an FSM this type of functionality it must keep a record of the previous state so the state blip can revert to it. This is easy to do as all that is required is another member variable and some additional logic in the Miner::ChangeState method.

By now though, to implement these additions, the Miner class has acquired two extra member variables and one additional method. It has ended up looking something like this (extraneous detail omitted):

class Miner : public BaseGameEntity
{
private:
State<Miner>* m_pCurrentState;
State<Miner>* m_pPreviousState;
State<Miner>* m_pGlobalState;
...
public:
void ChangeState(State<Miner>* pNewState);
void RevertToPreviousState();
...
};

Creating a State Machine Class:

The design can be made a lot cleaner by encapsulating all the state related data and methods into a state machine class. This way an agent can own an instance of a state machine and delegate the management of current states, global states, and previous states to it.

With this in mind take a look at the following StateMachine class template.

template <class entity_type>
class StateMachine
{
private:
//a pointer to the agent that owns this instance.
entity_type* m_pOwner;

State<entity_type>* m_pCurrentState;

//a record of the last state the agent was in
State<entity_type>* m_pPreviousState;

//this state logic is called every time the FSM is updated
State<entity_type>* m_pGlobalState;

public:
StateMachine(entity_type* owner):m_pOwner(owner),
m_pCurrentState(NULL),
m_pPreviousState(NULL),
m_pGlobalState(NULL)
{}

//use these methods to initialize the FSM
void SetCurrentState(State<entity_type>* s){m_pCurrentState = s;}
void SetGlobalState(State<entity_type>* s) {m_pGlobalState = s;}
void SetPreviousState(State<entity_type>* s){m_pPreviousState = s;}

//call this to update the FSM
void Update()const
{
//if a global state exists, call its execute method
if (m_pGlobalState) m_pGlobalState->Execute(m_pOwner);

//same for the current state
if (m_pCurrentState) m_pCurrentState->Execute(m_pOwner);
}

//change to a new state
void ChangeState(State<entity_type>* pNewState)
{
assert(pNewState && "<StateMachine::ChangeState>: trying to change to a null state");

//keep a record of the previous state
m_pPreviousState = m_pCurrentState;<


<-Back to Blogs

Categories

Good, better, best. Never let it rest. Untill your good is better and your better is best. - St. Jerome

© SOFTHINKERS 2013-18 All Rights Reserved. Privacy policy