【技术·真相】谈一谈游戏AI - 有限状态机

613 阅读6分钟

幸福是一种思维方式,而不是别的什么。

郑重说明:本文适合对游戏开发感兴趣的初级及中级开发和学习者,本人力图将技术用简单的语言表达清楚。鉴于水平有限,能力一般,文章如有错漏之处,还望批评指正,谢谢。

一、状态机从何而来?

在本系列文章的上一篇 【技术·真相】谈一谈游戏AI - 综述 中,我们对游戏AI进行了概要说明。其中,我们谈到了游戏 AI 模拟人类行为的最重要的概念是 Sense/Think/Act 决策模型。为了帮助大家回忆一下,我们再看一次它的样式:

在上述模型中,AI 的对象(经常被称为agent)可能主动(主动探索周围环境)或被动(收到事件通知)感知 sense,通过 think 的过程,再做出对应的 action,符合 Sense/Think/Act 决策模型。

如果我们要采取的 action 还取决于AI对象当前在做什么(状态),那我们可以将AI对象想像成会裂变成多个状态,每个状态单独做 Sense/Think/Act 决策

正是由于人们在大量的实践中发现这往往很普遍,因此就抽象出了状态机这样一种新的决策模型。类似下面这样:

二、普通状态机

机制说明

AI对象根据当前状态裂变成多个独立模型,但同一时间它只能处于其中某一个状态,因此只会触发该状态对应的 Sense-Think-Act 模型。

多个状态之间可以相互转换,当然,触发这个转换也是需要一定机制。

我们提到过,sense 有两大类,一类是主动的,如主动索敌,探测到自己血量不足等;第二类是被动的,如听到了噪音事件。

在我们演变后的状态机模型中,有了多个状态,因此,对于sense还需要额外增加一类工作:评估状态转换。

在评估状态转换时,结果要么维持当前状态,要么转换到别得状态,此时分别采取不同得 Act:

  • 如果状态不变,则继续当前 act,比如继续战斗、继续寻路等。
  • 如果状态不变,要对某一个被动收到的 event 做出 act。
  • 如果状态需要切换,则实施从一种状态过渡到另一种状态时所采取的行动。

下面是一个典型的状态机示例,根据主动 Sense 或者被动受到事件,状态转换的评估和实施一直在进行:

  1. 初始进入巡逻(Patrol)状态,当听到有噪音事件时,立即切换到到侦查(Investigate)状态;如果是遇见敌人,则进入攻击状态;
  2. 在侦查状态,如果索敌失败,会返回巡逻状态;如果索敌成功,也会进入攻击状态;
  3. 在攻击敌人时,如果判断对方已死亡,则返回巡逻状态;如果判断己方血量低,则会逃跑;

这个基本的系统能够良好运行,虽然有时状态转换条件的连续轮询可能带来较大开销。例如,如果每个AI必须在每一个思考间隔进行复杂的计算来索敌,以便决定是否从巡逻转换到进攻,如此可能会浪费大量CPU时间。

此时,我们采用状态机的一种被动响应外部事件的机制,处理外部事件,而不是老是主动感知我们可以将主动感知的思考间隔加长,用事件来实时被动感知

简单实现

前面说明了普通状态机的运行机制,那从程序角度如何实现呢?

对于每个状态的实现,有一个简单的版本:

void CStateX::OnUpdate() {
    //每一次循环(一帧或者一次思考间隔):Sense/Think/Act
}
void CStateX::OnEvent() {
    //被动sense:处理外部事件,触发对应act
}
void CStateX::OnEnter() {
    //进入当前状态需要做的一些工作,如数据初始化等
}
void CStateX::OnLeave() {
    //离开当前状态需要做的一些工作,如数据清理等
}

典型特点

普通状态机有很多优点,简单、高效、一般是程序实现。

但它的缺点也很明显:状态多了,需要考虑两两之间的转换关系,就像意大利面,几乎不可控。尤其是有了10、20个状态之后,新增一个状态工作量很大:我们要思考这个新增的状态如何转换到其他状态,这很容易出错。

另外,一个外部事件也很可能被很多个状态处理,新增一个外部事件就要在当前所有的状态处理循环中增加逻辑:看看对于每一个状态,如何处理这个新增的外部事件。

普通状态机还面临一个问题:

如果多个状态需要复用一个转换逻辑,如两种巡逻状态下,都要处理接电话的消息。当电话结束后,还要恢复到老的状态,如下:

我们当然可以在每个状态的处理循环中,都写上这样的转换代码,但如果状态越来越多,很多重复的转换复用就更有必要,于是引出了更复杂的状态机:分层状态机。

三、分层状态机(Hierarchical Finite State Machines)

我们将上述例子中的两种巡逻状态合并,组成一个大的层级状态(Watch Building),而别的状态维持不变,相当于有父子嵌套状态。上述问题改造后变成:

上图中的 H 代表上次激活的状态,这样从 Conversation 状态再切换回来时,可以恢复到此状态。

Halo2 的一个例子:

把使用手雷、掩蔽、防御归为自卫,是一个层级;交战部分使用了多层嵌套,但是原理是一样的;向尸体射击和搜查尸体归为战后处理。在撤退和闲置部分只有一个行为被嵌套,但是日后可以继续添加行为,可扩展性良好。

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

四、小结

本文开始论述AI决策模型的高级机制:状态机。

  • 首先,状态机是从何演化而来
  • 普通有限状态机的机制、实现以及特点
  • 为解决普通状态机的问题,发展出了分层状态机

希望本文对大家有所帮助!

前文:【技术·真相】谈一谈游戏AI - 综述

作者:我是码财同行,期待你的关注,不要错过我后续的文章更新