状态驱动的游戏智能体设计(下)

25 篇文章 2 订阅
23 篇文章 0 订阅

 

本文由恋花蝶最初发表于 http://blog.csdn.net/lanphaday,欢迎转载,但必须保持全文完整,也必须包含本声明。
译者并示取得中文版的翻译授权,翻译本文只是出于研究和学习目的。任何人不得在未经同意的情况下将英文版和中文版用于商业行为,转载本文产生的法律和道德责任由转载者承担,与译者无关。
State-Driven Game Agent Design
 
 
状态驱动的游戏智能体设计(下)
Mat Buckland
(续上篇)
――――――――――――――――――――――――――――――――――
Making the State Base Class Reusable
编写可重用的 State 基类
  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.
作为立足之本,有必要构造一个独立的 State 基类,以供每一个角色类类型获得自身的状态。我们可以通过类模板来使得它可重用:
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 EnterMineAndDigForNugget miner state as an example — now looks like this:
下面是 Miner 类的 EnterMineAndDigForNugget 状态:
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 accept- able, 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.
通常当设计有限状态机的时候,你最后都会在所有状态中出现重复代码。例如,在 Maxis 开发的流行游戏《 The Sims (第二人生)》中, Sim 可以感受到内急等生理需要,必须去洗手间解决。无论 Sim 在哪里、在什么时间,内急都可能发生。根据当前的设计,给淘金者加上这样一种行为,重复的条件逻辑就可能增加到每一个状态,或者放到 Miner::Update 函数里。下面介绍一个可接受的解决方案,它增加了一个全局状态——供 FSM 更新的时候调用。这样, FSM 的所有的逻辑都包含在状态内,而不在智能体类的 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.
有时智能体从一个状态进入另一个状态,当它退出这个状态时需要回到它的前一个状态,我将之称为状态闪动。例如就像《 The Sims 》中你可能必须让你的智能体能够在任何时间进入洗手间,之后再回到之前的状态。要实现这样的功能,就必须记录前一个状态,以便在状态闪动时返回。这可以容易地通过增加成员变量和对 Miner::ChangeState 方法增加一些额外逻辑来实现。
[ 译注:状态闪动这一概念的确比较难以理解。所以我画了下面这一张图来帮助理解 ]
译注图 1 状态闪动示意图
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):
到现在,为了完成这些额外功能, Miner 类已经增加了两个成员变量和一个方法。它最后看起来就像这样(忽略无关元素):
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();
  ...
};
  Hmm, looks like it’s time to tidy up a little.
  嗯,的确需要整理一下。
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.
现在来看看 StateMachine 模板类。
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;
 
    //call the exit method of the existing state
    m_pCurrentState->Exit(m_pOwner);
 
    //change state to the new state
    m_pCurrentState = pNewState;
 
    //call the entry method of the new state
    m_pCurrentState->Enter(m_pOwner);
  }
 
  //change state back to the previous state
  void  RevertToPreviousState()
  {
    ChangeState(m_pPreviousState);
  }
 
  //accessors
  State<entity_type>*  CurrentState()  const{return m_pCurrentState;}
  State<entity_type>*  GlobalState()   const{return m_pGlobalState;}
  State<entity_type>*  PreviousState() const{return m_pPreviousState;}
 
  //returns true if the current state’s type is equal to the type of the
  //class passed as a parameter.
  bool  isInState(const State<entity_type>& st)const;
};
  Now all an agent has to do is to own an instance of a StateMachine and implement a method to update the state machine to get full FSM functionality.
现在所有的智能体都能够拥有一个 StateMachine 实例,需要做的就是实现一个方法来更新状态机以获得完整的 FSM 功能。
本文由恋花蝶最初发表于 http://blog.csdn.net/lanphaday,欢迎转载,但必须保持全文完整,也必须包含本声明。
译者并示取得中文版的翻译授权,翻译本文只是出于研究和学习目的。任何人不得在未经同意的情况下将英文版和中文版用于商业行为,转载本文产生的法律和道德责任由转载者承担,与译者无关。
The improved Miner class now looks like this:
新实现的 Miner 类看起来就是这样的:
class Miner : public BaseGameEntity
{
private:
 
  //an instance of the state machine class
  StateMachine<Miner>*  m_pStateMachine;
 
  /* EXTRANEOUS DETAIL OMITTED */
 
public:
 
  Miner(int id):m_Location(shack),
                m_iGoldCarried(0),
                m_iMoneyInBank(0),
                m_iThirst(0),
                m_iFatigue(0),
                BaseGameEntity(id)
                              
  {
    //set up state machine
    m_pStateMachine = new StateMachine<Miner>(this);
   
    m_pStateMachine->SetCurrentState(GoHomeAndSleepTilRested::Instance());
    m_pStateMachine->SetGlobalState(MinerGlobalState::Instance());
  }
 
  ~Miner(){delete m_pStateMachine;}
 
  void Update()
  {
    ++m_iThirst;
    m_pStateMachine->Update();
  }
 
  StateMachine<Miner>* GetFSM()const{return m_pStateMachine;}
 
  /* EXTRANEOUS DETAIL OMITTED */
};
  Notice how the current and global states must be set explicitly when a StateMachine is instantiated. The class hierarchy is now like that shown in Figure 2.4.
注意 StateMachine 实例化后如何正确设计当前和全局状态。图 2.4 是现在的类层次结构图。
Figure 2.4. The updated design
Introducing Elsa
介绍 Elsa
  To demonstrate these improvements, I’ve created another project: WestWorldWithWoman. In this project, West World has gained another inhabitant, Elsa, the gold miner’s wife. Elsa doesn’t do much; she’s mainly preoccupied with cleaning the shack and emptying her bladder (she drinks way too much cawfee). The state transition diagram for Elsa is shown in Figure 2.5.
为了验证这些改进,我创建了一个新的项目—— WestWorldWithWoman 。在这个项目里, WestWorld 多了一个人物—— Elsa ,她是淘金者 Bob 的妻子。 Elsa 做的事不多,主要是打扫房子和上洗手间(她喝了太多咖啡)。图 2.5 Elsa 的状态转换图。
Figure 2.5. Elsa’s state transition diagram. The global state is not shown in the figure because its logic is effectively implemented in any state and never changed.
When you boot up the project into your IDE, notice how the VisitBathroom state is implemented as a blip state (i.e., it always reverts back to the previous state). Also note that a global state has been defined, WifesGlobalState , which contains the logic required for Elsa’s bathroom visits. This logic is contained in a global state because Elsa may feel the call of nature during any state and at any time.
当你把这个项目导入到你的 IDE 的时候,注意 VisitBathroom 状态是如何以状态闪动的形式实现的(也就是它如何返回到前一个状态),同样值得注意的是定义了一个全局状态—— WifesGlobalState ,它包含了 Elsa 上洗手间所需的逻辑。在全局状态中包含这一逻辑是因为 Elsa 可能在任何时候都会感到内急,这是天性,哈哈。
Here is a sample of the output from WestWorldWithWoman.
这里是 WestWorldWithWoman 项目的输出示例。
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
Elsa:  Walkin'  to  the  can.  Need  to  powda  mah  pretty  li'l  nose
Elsa:  Ahhhhhh!  Sweet  relief!
  Elsa:  Leavin'  the  john
Miner  Bob:  Depositin'  gold.  Total  savings  now:  4
Miner  Bob:  Leavin'  the  bank
Miner  Bob:  Walkin'  to  the  gold  mine
Elsa:  Walkin'  to  the  can.  Need  to  powda  mah  pretty  li'l  nose
Elsa:  Ahhhhhh!  Sweet  relief!
Elsa:  Leavin'  the  john
Miner  Bob:  Pickin'  up  a  nugget
Elsa:  Moppin'  the  floor
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
Elsa:  Moppin'  the  floor
Miner  Bob:  That's  mighty  fine  sippin'  liquor
Miner  Bob:  Leavin'  the  saloon,  feelin'  good
Miner  Bob:  Walkin'  to  the  gold  mine
Elsa:  Makin'  the  bed
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
Elsa:  Walkin'  to  the  can.  Need  to  powda  mah  pretty  li'l  nose
Elsa:  Ahhhhhh!  Sweet  relief!
Elsa:  Leavin'  the  john
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
Elsa:  Walkin'  to  the  can.  Need  to  powda  mah  pretty  li'l  nose
Elsa:  Ahhhhhh!  Sweet  relief!
Elsa:  Leavin'  the  john
Miner  Bob:  ZZZZ...
 
Well, that's it folks. The complexity of the behavior you can create with finite state machines is only limited by your imagination. You don’t have to restrict your game agents to just one finite state machine either. Sometimes it may be a good idea to use two FSMs working in parallel: one to control a character’s movement and one to control the weapon selection, aiming, and firing, for example. It’s even possible to have a state itself contain a state machine. This is known as a hierarchical state machine. For instance, your game agent may have the states Explore , Combat , and Patrol . In turn, the Combat state may own a state machine that manages the states required for combat such as Dodge , ChaseEnemy , and Shoot .
夸张点说,这相当了不起啊。你能够用有限状态机创造非常复杂的行为,到底有多复杂仅受限于你的相像力。你无需限制你的游戏智能体只能有一个有限状态机,有时候你可以使用两个 FSM 来并行工作:一个控制角色的移动,而另一个控制武器选择、瞄准和开火。你甚至可以创造包含状态机的状态机,即分级状态机。例如你的游戏智能体可能有 Explore (探测)、 Combat (战斗)和 Patrol (逻辑)等状态,而 Combat (战斗)状态又可以拥有一个状态机来管理 Dodge (躲避)、 ChaseEnemy (追逃)和 Shoot (射击)等战斗时需要的状态。
 
本文由恋花蝶最初发表于 http://blog.csdn.net/lanphaday,欢迎转载,但必须保持全文完整,也必须包含本声明。
译者并示取得中文版的翻译授权,翻译本文只是出于研究和学习目的。任何人不得在未经同意的情况下将英文版和中文版用于商业行为,转载本文产生的法律和道德责任由转载者承担,与译者无关。
 
全文完
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值