接上文 游戏开发中的人工智能(八):描述式 AI 及描述引擎
本文内容:有限状态机是游戏软件 AI 的基本要素。本章探讨有限状态机的基础,以及如何予以实现。
有限状态机
有限状态机是一种抽象机制,是处在各种不同的预定状态下的其中一种状态。有限状态机也可以定义一组条件,以确认何时应该改变状态。实际的状态会决定状态机的行为。
本章我们要讨论有限状态机的基础,教你如何实现它。
状态机的基本模型
有限状态机模型,如图9-1 所示。
如图9-1 所示,在有限状态机中,每个可能的状态都以圆圈表示。此图中有四种可能的状态:Si,S1,S2,S3**。每个有限状态机中,都需要采用一种方法使其从一种状态转换到另一种状态。就此图而言,转换函数以 t1、t2、t3、t4、t5 来表示。一开始初始状态是 Si,一直到 t1 转换函数提供刺激值时才会改变其状态。一旦提供了刺激值,状态就转换成 S1。即:从一种状态转换成另一种状态,要看是由哪个转换函数提供的刺激值。**
下面举一个“Pac-Man(小精灵)”游戏里的魔鬼的例子,鬼怪有限状态机如图9-2 所示:
图9-2 中,每个方块代表游戏 AI (即鬼怪)的可能的状态。就此例而言,有三种可能状态:游走(roam)、闪躲(evade)、追逐(chase)。箭头表示可能存在的转换,同时也显示出哪些条件下这几种状态可以改变或维持不变。
就此而言。鬼怪最初的状态是“游走”。有两个条件可以引发状态改变。第一个条件是“blue true 鬼怪变成蓝色”。当玩家吃了大力丸后,鬼怪就会变成蓝色,然后鬼怪就会从状态“游走”转换为“闪躲”。另一个可以改变状态的条件是“see true 看见玩家”,即当鬼怪看见玩家时,就会从状态“游走”转换成“追逐”。
此图还显示,当鬼怪是蓝色时,会持续保持“闪躲”状态。除此之外,如果看得见玩家,则状态会从“闪躲”转为“追逐”。如果在“追逐”中看不见玩家,则会转换到“游走状态”。
例9-1 是针对图9-2 的程序代码:
//例9-1:鬼怪的行为
switch(currentState)
{
case kRoam:
if(imBlue==true)
currentState=kEvade;
else if(canSeePlayer==true)
currentState=kChase;
else if(canSeePlayer==false)
currentState=kRoam;
break;
case kChase:
if(imBlue==true)
currentState=kEvade;
else if(canSeePlayer==false)
currentState=kRoam;
else if(canSeePlayer==true)
currentState=kChase;
break;
case kEvade:
if(imBlue==true)
currentState=kEvade;
else if(canSeePlayer==true)
currentState=kChase;
else if(canSeePlayer==false)
currentState=kRoam;
break;
}
设计有限状态机
我们将设计有限状态机分为两步:
- 讨论存储与游戏软件 AI 实体相关的数据的几种结构类型
- 讨论如何建立函数转换状态机的状态
有限状态机的结构和类
例9-2 说明了典型的游戏中,怎样以一种单一类结构,存储游戏软件 AI 实体的数据。
//例9-2:游戏软件 AI 结构
class AIEntity
{
public:
int type;
int state;
int row;
int column;
int health;
int strength;
int intelligence;
int magic;
};
例9-3 定义状态常量:
//例9-3:状态常量
#define kRoam 1
#define kEvade 2
#define kAttack 3
#define kHide 4
有限状态机行为及转换函数
实现有限状态机的下一步是提供函数,以决定 AI 实体的行为以及何时应该改变状态。如例9-4 所示。
//例9-4:游戏软件AI 转换函数
class AIEntity
{
public:
int type;
int state;
int row;
int column;
int health;
int strength;
int intelligence;
int magic;
int armed;
Boolean playerInRange();
int checkHealth();
}
在 AIEntity 类中我们新增了两个函数。例9-5 说明了如何利用这两个转换函数,改变状态机的状态。
//例9-5:改变状态
if( (checkHealth() < kPoorHealth) && (playerInRange()==false) )
state=kHide;
else if(checkHealth()<kPoorHealth)
state=kEvade;
else if(playerInRange())
state=kAttack;
else
state=kRoam;
例9-5 中第一个 if语句用来检查 AI 实体的健康值是否太低,以及玩家是否不在附近。如果这些条件都成立,则由此类结构所表示的生物,将进入“隐藏”状态。第二个 if 语句用来检查健康值下限。如果我们运行到这个 if 语句,就表示玩家在附近。因为如果不在附近,第一个 if 语句的评估结果就会成立。因为玩家可能会看见该生物,所以我们要转换成“闪躲”状态。第三个 if 语句用来检测玩家是否在附近,此事的 AI 健康状态应该是良好的,否则前两个 if 语句中的其中一个就应该成立了。因为玩家在附近,且AI 健康状况良好,于是我们转换成“攻击”状态。否则,我们进入默认的“游走”状态。直到转换函数指定的条件指出该状态应该改变为止。