游戏人工智能开发之6种决策方法

转自:http://www.gameres.com/467913.htmlhtml

人工智能遵循着:感知->思考->行动

  决策方法:有限状态机(Finite-State Machines),分层状态机(Hierarchical Finite-State Machines),行为树(Behavior Trees),效用系统(Utility Systems),目标导向型行动计划(Goal-Oriented Action  Planners),分层任务网络(Hierarchical Task Networks)

  有限状态机

  有限状态机是目前游戏AI中最多见的行为模型。状态机的代码简单又快速,使用上强大、灵活、计算开销小。

  状态机的一个好处是能够可视化,以下图所示:算法

图中有四个状态:巡逻(patrol),查看(investigate),攻击(attack),逃走(flee),咱们把实心圆当作初始状态。

  简要过程:假设NPC士兵正在保卫他的阵地,当前状态为巡逻,当他听到什么动静时就会转到查看状态,跑到声音源去查看,若是看到敌人就转到攻击状态,若是没看到过一段时间又会回到巡逻状态。在攻击状态中若是血值低下就会进入逃跑状态。若是击败了敌人,又会回到巡逻状态。

  状态机状态类的一个主要结构以下,onEnter函数就至关于unity中的Start()函数,在类开始时调用,做为对旧状态的过分和新状态产生的开始,好比当从巡逻转向攻击状态时,能够在攻击状态的开始让NPC大喊“发现敌人!进攻!”等等。onUpdate()就至关于unity中的Update(),你可让它每帧都执行,或者几秒钟执行一次,是循环执行的,每次执行时间间隔由你来决定。onExit()就是在退出一个状态以前要执行的,好比,杀死敌人以后由攻击状态转向巡逻状态以前,让NPC作一个欢呼手势并大叫胜利了。FSMTransition列表为将要转到的全部可能的状态。安全

class FSMState
{
  virtual void onEnter();
  virtual void onUpdate();
  virtual void onExit();
  list<FSMTransition> transitions;
};

每一个状态还存储着FSMTransition的类,表明能从当前状态能够转到的状态网络

class FSMTransition
{
  virtual bool isValid();
  virtual FSMState* getNextState();
  virtual void onTransition();
}

  当转换条件知足时isValid()返回true,好比当发现敌人NPC就从巡逻状态转到攻击,getNextState()返回将要转到的状态,onTransition()是状态之间转换的过渡,和上面说的onEnter()差很少。

  最后是有限状态机类FiniteStateMachineide

class FiniteStateMachine
{
  void update();
  list<FSMState> states;
  FSMState* initialState;
  FSMState* activeState;
}

有限状态机类包含一个包含全部状态的列表states,initialState为初始状态,activeState为当前状态。

  伪代码以下:函数

在  activeState.transtitions中循环调用isValid(),检测是否符合达到下一状态的条件
若是符合转换条件
  调用activeState.on Exit(),退出当前状态
  设置activeState 为 validTransition.getNextState(),把当前状态赋值为下一状态
  调用activeState.onEnter(),下一状态的开始
若是不符合转换条件,调用activeState.onUpdate(),让NPC执行当前状态须要作的事

在编写有限状态机的代码只前最好画一个上面的草图,这样既能够明确转换关系,又能够不漏掉该有的状态。

  分层有限状态机

  有限状态机虽然好,可是它有很大的缺点,当状态少的时候能够运用自如,当状态多的时候10个以上就已经结构很是复杂,并且容易出错。

  若是咱们让NPC巡逻两个地方,好比安全的室内,和门口。人工智能

若是咱们想在一个状态上附加一些情况,例如当NPC在巡逻时,让他接一个电话而后再恢复巡逻,此时若是使用有限状态机的话咱们必需要新建一个打电话的状态来作过渡,可是此时的巡逻有两个,因此能接到电话的状态也有两个,而后加了两个相同的状态,不少这样的重复的小状态使得状态机愈来愈复杂。以下图:spa

这时,咱们能够用分层有限状态机来解决这个问题,把多个状态机归为一层,以下图,把巡逻安全处和门口归为看守建筑,这样咱们只须要有一个打电话状态就能够了。设计

分层有限状态机增长了一个滞后,在有限状态机中并无,在一个普通的有限状态机中,是从初始状态开始的,在分层有限状态机中是一个嵌套的状态。注意上图有H的圈,表明历史状态(history state),当咱们第一次进入嵌套状态->看守建筑时,历史状态H表示为初始状态,以后历史状态H表示为最近处在的一个状态。

  在咱们的例子中:初始状态就是看守建筑,,而后进入看到手机按住这个嵌套,巡逻安全处是初始状态。当从巡逻安全处转换到巡逻门口这个状态时,H历史状态就转变为巡逻门口状态,此时来电话了,转换到接电话状态,接电话结束,咱们回到嵌套状态中的历史状态,此时为巡逻门口,可见H历史状态就是一个临时的,便于嵌套外的状态返回到以前的嵌套内的小状态,以不至于出错,或者换回了别的状态,若是接完电话回到巡逻安全处,那就出大错了。

  分层有限状态机,就这样避免了重复状态,能够实现更大的更复杂的状态。

  实例:

  Halo2使用了这一技术,以下图:code

可见:把使用手雷、掩蔽、防护归为自卫,交战部分使用了多层嵌套,可是原理是同样的,向尸体设计和搜查尸体归为战后处理。在撤退和闲置部分只有一个行为被嵌套,可是往后能够继续添加行为,可扩展性良好。

  至于如何在嵌套的层里对行为进行选择,能够就按这个顺序执行,也能够加上权重优先级,或者你想让他执行哪一个经过代码来控制。

  行为树

  行为树是树型结构的,每一个节点都表明了一个行为,每一个行为均可以有子行为。

  全部行为都有一个先决条件,就是产生的这些行为的条件。

  整个算法先从树的根部开始,而后开始检查每个先决条件。树的每一层只能够执行一个行为,因此当一个行为正在执行,它的兄弟节点都不会被检查,可是它们的子节点仍是要检查的。相反若是一个行为的先决条件当前并不知足,则跳过判断它的子节点,继续判断它的兄弟节点。一个树所有检查完毕以后,决定执行优先级最大的,而后再依次执行每一个动做。

  伪代码:

使根节点为当前节点
当存在当前节点
  判断当前节点的先决条件
  若是先决条件返回true
    把节点加到执行清单
    使子节点为当前节点
  不然
    使兄弟节点为当前节点
执行执行清单上的全部行为

不一样于状态机,行为树是无状态的,不须要记下以前执行的行为,只是判断行为该不应执行。

  行为树的节点之间是不相关的,删除或增长节点,对其余节点都无影响。因此,可扩展性也是行为树的一个优点。另外还能够为决策树添加灵活性与随机性,父节点能够随机决定是否检查子节点。

  缺点:决策树作的选择并不必定是最优的,结果也不必定是咱们想要的。并且决策每次都要从根部往下判断选择行为节点,比状态机要耗费时间。每次决策都要通过大量的条件判断语句,会变得很是慢。

  另外还有一个问题,例如:一个农民要收割做物,敌人出现了,农民逃跑,逃出了距离敌人的必定范围以后,又回去收割做物,走到敌人的范围又逃出,这样来回往复,是一个弊端,,能够根据状况来写代码避免,不然会被玩家***的。

  效用系统

  人工智能的逻辑->电脑的逻辑,是基于简单的bool问题,好比:“我能看到敌人吗?”,“我有弹药吗”,是简单的是或者不是的问题,因此作出的行为一般是极端化的,一个单一的行动。好比:

if (CanSeeEnemy())
{
  AttackEnemy();
}
if (OutOfAmmo())
{
  Reload();
}
即时有多条件的行为,bool判断带来的也是一个单一的行动。
if (OutOfAmmo() && CanSeeEnemy())
{
  Hide();
}

因此有些状况,只是作这些布尔判断是不合适的,会遗漏不少状况,判断也不稳当。好比:咱们可能须要同时考虑与敌人的距离、有多少弹药、饥饿程度、HP值,等等。这些判断条件能映射出许多动做,比咱们单一的判断作不作这个动做要好不少。utility-based system,基于效用的系统,会根据权重、比率、队列和许多须要考虑的事项来作出最优选择,使AI比普通的行为树更有头脑。根据上面的例子,使用效用系统咱们的AI能够作出咱们想要的动做,并根据当前状况作出不一样强度的动做,使AI真实、更具可能性,也再也不是只有一个正确的选择了。决策树就是对AI说,“只是你将要作的一个行为”,效用系统就是对AI说:“这些是你可能要作的行为”

  Sims模拟人生的人工智能就是使用的效用系统(sims的人工智能让我膜拜至今),在sims中,小人结合当前环境和自身的状态,来作出行动的选择。例如:小人“很是饿”结合环境“没有食物”会比只有“有一点饿”更加吸引人的眼球。若是“有一点饿”小人会以接近“美食”为第一执行行为。注意,这里的“美食(的美味程度)”、“食物不多(食物储备程度)”、“一点饿(饿的程度)”,都是一个有范围的数值(经常使用的是0-1的浮点值)。

  当须要选择新的行为时,咱们经过分数(上面说的各类程度)来选择相对最优的选择,或者加上一个随机值再选择,使得接近优选的几个选择都有必定概率(概率可根据所加随机值决定)被选中。

  目标导向型行动计划

  GOAP来源于STRIPS方法,这两种都是让AI创造他们本身的方法去解决问题,咱们提供给它一系列可能的动做做为对这个世界的描述,和每一个动做使用的先决条件,和行动带来的影响。AI拥有一个初始状态和他须要达到的目标。有一组目标,AI能够经过优先级或当前状态选择一个。计划系统决定一个动做序列来知足当前目标,计划出一个像路径同样的能最简单达到目标状态的动做序列。

  GOAP是一个反向连接搜索,从要实现的目标开始,找到什么动做能实现目标,在寻找刚才动做的先决条件,一直往前推,知道达到你的当前(初始)状态。这种反向连接搜索替代了启发式的前向连接搜索。

  伪代码:

把目标加到未解决事件列表
对于每一个为解决事件(for)
  移除这个为解决事件
  找到达成事件的动做
  若是动做的先决条件已经知足
    增长动做到计划中
    往回推须要达到先决条件的动做到计划中
  不然
    添加该先决条件到未解决时间中

例如:咱们创建一个NPC士兵,把它的目标设为杀死其余敌人,咱们设置它的目标为Target.Dead。为了让目标去死,NPC必需要有一个武器用来射击,这是一个先决条件,可是如今NPC并无正在装备的武器,NPC就须要执行找到武器这个动做,若是NPC有武器库,他就会从武器库中拿一个,若是病没有武器库,就须要寻路去找一个武器装备了。获得武器装备以后就要找到敌人,实现方式多种多样,徒步寻找、或者NPC周围有车也能够开着车去寻找。我么发现,咱们给NPC大量的动做选择,让NPC本身决定该作什么,于是产生动态不可预知又有趣的行为,并且表现得很天然,比开发者建立行为好多了。

  分层任务网络

  HTN也是寻找一个计划来让AI执行,不一样之处在于怎样找出这个计划。开始拥有一个初始状态和一个跟任务表明咱们须要解决的问题。原理是最高级的任务分解成更小的任务再继续分解直到咱们解决问题。每一个高级任务都有不少方式被完成,当前世界状态决定高级任务要分解成哪组小任务。HTN与GOAP相反,HTN是前向连接搜索,是从当前状态一直推到目标状态,向前推直到问题解决。世界状态分散成几种属性,它的HP、精力,敌人的HP、相距距离,计划根据这些来制定。

  咱们有两种任务:原始任务和复合任务。原始任务是能够只解决问题的任务,也就是能够直接达到目标的任务。在游戏中,它能够为开火、装填子弹、移动到掩蔽物。这些人物能够影响世界状态,开火这个任务须要先有子弹,并执行装填子弹这个任务。复合任务是高级别的任务,能够看做方法。一个方法是一组任务能够完成复合任务,这一组任务是由先决条件决定的。复合任务让HTN推断出世界而且决定该作什么动做。

  使用复合任务,咱们就能构建一个HTN域,这个域是一大层任务,表明咱们解决问题的方法。

  伪代码:

增长根复合任务到分解列表中
对于每一个在咱们分解列表中的任务(for)
  移除任务
  若是任务是复合任务
    找到知足当前条件状态而且能处理该复合任务的方法
    若是该方法找到了,增长方法的任务到分解列表中
    若是没找到,恢复到以前分解任务的状态中
  若是任务是原始任务
    在当前状态下执行任务
    增长任务到最终计划列表

HTN就是从最高级的根任务分解更小的任务再分解成更更小,分解是须要判断当前状态和条件的。当咱们终于分解为原始任务,咱们把原始任务加到最终计划中,每个原始任务都是一个可操做步骤,咱们能够直接执行它。

相关文章
相关标签/搜索