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
|
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;}
};
|
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 */
};
|
void Miner::Update()
{
m_iThirst += 1;
if (m_pCurrentState)
{
m_pCurrentState->Execute(this);
}
}
|
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;
}
|
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);
}
|
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.
提示:状态设计模式对于游戏主流程的组织也是非常有用的,例如,你可能有菜单状态、保存状态、暂停状态、设置状态和运行状态等。
|
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.
注意:我乐于使用单件的原因在上文已经给出,但这也有一个缺陷。因为他们由客户共享,单件状态不能使用他们自有的,特定智能体的数据。例如,当某一处于某状态的智能体移动到某一位置时,他不能把这一位置存储在状态内(因为这个状态可能与其它正处于这一状态的智能体不同)。它只能把它存储在其它地方,然后由状态机通过智能体的接口来存取。如果你的状态只有一两个数据要存取,那这也不是什么大问题,但如果你在很多外部数据,那可能就值得考虑放弃单件设计,而转而写一代码来管理状态内存的申请与释放了。
|
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);
};
|
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)
{
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 stops work and
//changes state to go to the saloon for a beer.
pMiner->AddToGoldCarried(1);
//digging 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 beer
if (pMiner->Thirsty())
{
pMiner->ChangeState(QuenchThirst::Instance());
}
}
|
void EnterMineAndDigForNugget::Exit(Miner* pMiner)
{
cout << "/n" << GetNameOfEntity(pMiner->ID()) << ": "
<< "Ah'm leavin' the goldmine with mah pockets full o' sweet gold";
}
|