《游戏中的状态机研究报告》由会员分享,可在线阅读,更多相关《游戏中的状态机研究报告(9页珍藏版)》请在金锄头文库上搜索。
1、游戏中的状态机研究报告姚慧敏一、 什么是状态机关于状态机的一个极度确切的描述是它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前” 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态, 状态机停止。二、 状态机的分类和扩展1 有限状态机(1)有限状态机的定义和特征:有限状态机,它包含了有限个“状态”和状态之间的“转移”,彼此连成一个有向图。它主要用于整个游戏场景的管理或操作单个的游戏对象和人物。有限状态机也是一种有向图的数据结构,由三部分组成
2、: 内在的所有条件; 输入条件; 状态之间起到连接作用的转换函数。一个有限状态机是一个设备,或是一个模型,具有有限数量的状态。它可以在任何给定时间根据输入进行操作,使得系统从一个状态转换到另一个状态,或者是使一个输出或者一种行为的发生,一个有限状态机在任何瞬间只能处于一种状态。(2)完成一个有限状态机 先记下AI要进行的基本行为,然后用图形表示,再变成实际的代码。下面以一个带有武器的士兵AI来举例说明怎样设计一个有限状态机。 AI规则: 敌人在户外区,没有障碍物 他有预定停留点,循环巡逻 当你进入敌人的视线锥面时,敌人激活 敌人看到你就追 他带有剑 如果离你很近,他就会停下来,用剑刺你图形布局
3、:在编码之前,一定要用简单的框图得到所有状态和变换的全貌。状态用一个圆表示,而箭头则表示状态转换正在发生,标记好每一个状态和状态转换之间的条件,在接下来的编码中将会表现的更加清晰。 如下图所示,士兵有四种状态,到达停留点(Realign Waypoint)、寻找停留点(Seek Waypoint)、追逐玩家(Chase player)和攻击玩家(Fight player)。转换的条件有:转弯(Aligned)、找到巡逻点(Waypoint Reached)、看见玩家(See player)、距离限制(Distance)、玩家死亡或离开视线(Player dead or lost sight)。
4、 编写代码: 完成规则和图之后,编写有限状态机就是一件很简单的事情了。对于状态机中所有的状态,我们要先枚举出来,最好用0来代表初始的状态,用整数表示其他的状态。因此,如果有限状态机欧N个状态的话,则最后一个状态的枚举值应为N-1。我们学习了C+语言,因此用C+中的#define语句生产状态名。 有限状态机的程序框架如下所示: #define SEEK_WAYPOINT 0 #define ROTATE_WAYPOINT 1 () 用一个整形变量表述AI所处的状态,从而驱动一个switch结构: int state switch(state) case 0: /这个状态的特定代码 break;(
5、) case N-1:/这个状态的代码break;default: break;上面是一个有限状态机的代码框架,下面我们来完成我们的士兵状态机。#define SEEK_WAYPOINT 0#define ROTATE_WAYPOINT 1#define CHASE_PLAYER 2#define FIGHT_PLAYER 3bool aligned;bool reachwaypoint;bool seeplayer;bool playerlost;bool distancelimited;int state;while(1)switch(state)case 0:animation1();i
6、f(reachwaypoint)state=1;else if(seeplayer)state=2;break;case 1:animation2();if(aligned)state=0;break;case 2:if(playerlost)state=0;else if(!distancelimited)state=3;break;case 3:if(distancelimited)state=2;if else(playerlost)state=1;break;default:break;这样,在switch结构中每个case控制一个特定的状态,将每一个case中状态的活动分为三个部分:
7、 状态名 该状态中要进行的默认动作 求输出变换时要进行的运算 case NAME OF STATE: DEFAULT ACTIONS CONDITION EVALUATIONif(TRANSTION)state=destintion state;()break; 这样布局case,可以使代码的速度更快,更容易找到潜在的缺陷。2.并行自动机 并行自动机也叫多状态自动机,意思就是,一个AI同时兼任两部状态机的工作,例如上例中的士兵,如果给他一把枪,那么他可以在完成追逐寻找和打斗的同时准备子弹,当看到敌人在自己正前方的时候就可以拿枪射击。这样,士兵就多了一个关于对枪控制的自动机。这个自动机与刚才的会
8、并列执行,控制士兵的行为。士兵对枪的控制处理图示如下: 只要玩家出现位于虚拟生物的正前方,这个NPC就会主动射击玩家。这时就会启动这个枪支自动机。 完成这个任务只要三个状态:初始状态、向敌人射击状态、装弹状态。如果我们在这里不使用并行机制,而是仍然把这两个状态合并成一个的话,则自动机将会成倍的扩张,因为在第一个自动机的每一个状态上都要加上对枪的控制。由于对枪有三个状态的控制,那么扩张之后的自动机将会是原来的三倍。这样就会使自动机变得非常复杂,而且是非常没有必要的复杂。在编写代码的时候,两个状态转换代码块也会是“并行”的:int state1;int state2;switch(state1)c
9、ase 0:break;()case N-1:/N是这个状态机的状态数break;switch(state2)case 0:break;()case M-1: /M是这个自动机的状态数break;并行自动机特别适合同时进行独立行为的系统,这里是运行和攻击。这里独立是关键,要注意的是,一个状态机的状态影响另一个状态机状态的并行自动机。但是在实际设计游戏的时候,动作往往不是完全独立的,例如,士兵要射击的话就要停止巡逻等行为,这样第二个自动机就会影响第一个的状态,但是如果不让这种影响发生的话,我们的NPC会看起来不够真实。如果能把自动机之间的行为相互联系起来,例如,士兵要开枪,那么就会发出一个信号告
10、诉第一个状态机暂停巡逻等状态,那么这样将会使游戏看起来更加真实。3.带有通信系统的状态机带有通信系统的状态机也叫同步有限状态机,相当于一个状态机队列,对象根据情形的不同,自动转化自己的所属状态机,就像是把上面的多状态机又分解成了多个单独的状态机,每一个状态机由一个特定的角色来实现。实现自动机之间的通信后,几个AI可以配合够工作并进行一定的协作,这样就形成了一个有意思的小队。在我们中学时代常玩的半条命以及目前数不胜数的FTS类游戏中,这种小队合作机制常常可以见到。例如:一个人在前进的时候,另一个人为其提供掩护,再一个人负责找个隐藏点向对手扔手雷。这三个人的状态之间形成一种联系,通过通信可以知道队
11、友的状态和自己下一步的动作。通信的实现有两种方式:对象轮询消息和事件驱动。轮询就是游戏对象主动的了解世界的情况,事件驱动就是坐等消息过来通知。在游戏设计中,常常会有数百个游戏对象,如果使用类似于公告牌的消息轮询方式的话,有可能在爆炸发生完之后,某个AI才刚刚轮询出这个消息,这样做就不适合了。如果采用消息驱动的话,则要来回的发送消息,用到大量的消息队列和循环,这样会使系统变慢。对于这两张消息传递方式,我并没有查到哪种更适合用在FTS游戏中,我参考了两本书:游戏核心算法编程内幕和游戏编程精粹1。在游戏核心算法编程内幕中,建议采用公告牌也就是轮询的方式,因为消息传递会使系统变慢。而在游戏编程精粹1中
12、说事件驱动是最好的技术。对于我来说,性能方面的讨论我真的是分辨不出好坏。公告牌系统就是对于一个单个的AI,它要在某个时间周期内检查当前世界的状态,这样会造成时间延迟。下面以事件驱动为主介绍一下状态机之间是怎样通过消息实现同步的。下面建立一个3个士兵组成小组的同步AI,以协助的方式攻击敌人。在3人小队中,只要有一个与玩家建立起了联系,那么这个人就成为了1号攻击手,这时,通过公告牌告诉另外两名队友,等待第二个AI提供火力掩护,然后攻击手就可以利用火力掩护打死玩家了。第三个AI在攻击手开始攻击的时候躲避起来扔手雷。这样,三个人形成一个同步机制,当其中一个AI牺牲后,角色传递就会发生,攻击手阵亡,掩护
13、手就成为攻击手,投手就成为掩护,然后系统生成一个AI充当投手。现在我们让三个游戏对象都能够“收听”其他对象的消息。消息中包含5种有用的信息:一个描述这个消息的名字、发送者的名字、接收者的名字、应发送的时间、以及任何相关的数据。因此,当消息传来,消息能准确到达接受者处,接受者也会知道消息来自于谁。消息关键字消息描述BeginStateMachine开始状态机定义EndStateMachine终止状态机定义State(NumberOfState)指明一个特殊状态的开始OnEnter响应一个被进入的状态:允许初始化代码OnExit响应一人被退出的状态:允许清除代码OnUpdate响应更新游戏信号On
14、Msg(NameOfMessage)响应任何定义的消息SetState(NameOfState)改变状态:退出旧状态进入新状态SendMsg()发送消息给所有游戏对象SendDelayedMsg()发送一个延迟消息给任何游戏对象当状态机开始运行的时候,Onter消息被触发,第一个状态由SetState命令设置。在上述半条命的三人小队中,对于掩护手,消息是这样作用的:BeginStateMachineOnterSetState(STATE_Patrol)OnMsg(MSG_Dead)/阵亡State(STATE_Cover)OnEnter/进入状态机开始射击掩护攻击手OnUpdateif(MSG_Dead)