省流总结
赶时间的朋友依旧看这里 (๑¯◡¯๑):
为什么精英怪"什么都会"? 不是数值高,而是底层架构让它能同时干很多事——而且随时能中断当前行为,切到更紧急的事。
FSM 改一个行为要连 10 条线,行为树只需要插一个树枝。 状态越多,这个差距越恐怖。
但别盲目上行为树。 AI 只有 5 个状态?用行为树是杀鸡用牛刀。
想知道《最后生还者》的 Hunter 是怎么"不做蠢事"的?《光环 2》怎么用树形结构第一次在 3A 游戏里证明有效性?《全境封锁》怎么靠行为树管理 55 种 NPC 变体?
往下翻,答案都在后面。
引子
上一篇我们聊了有限状态机(FSM)——从吃豆人的四个幽灵,到合金装备的守卫 AI,再到魂系 Boss 的套路循环。FSM 在简单场景下确实好用,但当状态数超过某个阈值后,"状态爆炸"会让系统变得没法维护。
如果你还没看过第一篇,建议先补课——本篇会直接沿用 FSM 的基本概念(状态、转移、动作),不再重复解释。
今天这篇,来看看 FSM 遇到瓶颈之后,业界是怎么破局的。
你遇到过那种让你血压飙升的精英怪吗?
不是因为它数值高,而是因为它什么都会Σ( ° △ °|||)︴
你刚躲进掩体喘口气,它切枪包抄你侧翼。你拉开距离想狙击,它三枪点射然后缩回去。你把它打残了,它一边丢烟雾弹一边往掩体后跑,追都追不上。
这一刻你心里冒出的念头不是"这家伙好聪明",而是:"它怎么同时干这么多事的?它到底在想什么?"
要理解这种"同时干很多事"的能力从何而来,我们可以从设计者的角度换个思路——
想象一下你在给一个按部就班的管家写"待办清单"。但问题是,一个"看起来聪明"的管家,背后需要的判断条件,可能比你能想到的所有情况加起来还多。
我们来感受一下这个问题的规模。
你需要教会笨蛋管家处理以下情况:日常打扫、发现漏水、修理水管、准备晚餐、有客人到访、宠物生病、火警响起、停电后恢复、打扫完成后汇报……
用 FSM 来实现的话,你需要画一张状态图:每个行为是一个状态,状态之间用转移线连接。
9 个状态,最多 72 条转移线。15 个状态,最多 210 条。30 个状态?快 1000 条了。
![]()
这不是压缩毛巾,这是状态爆炸
这还没完。你想加一个"被闪光弹致盲"的新状态?你需要从每一个已有状态画一条转移到新状态的线,还要考虑被致盲后恢复到哪个状态。
每加一个新状态,维护成本都在涨。
这就是所谓的"状态爆炸"(State Explosion)——状态数量线性增长,转移线数量呈平方级增长。改一个状态,牵动整个网络。
第一章:《最后生还者》
状态机的最佳实践与限制
上篇聊过《光环 2》里 Bungie 是怎么用 Objective Tree(目标树)应对状态爆炸的。而在 Bungie 之后约十年,Naughty Dog 选择了另一条路:把成熟的架构通过工程实践打磨到工业级水准。
如果说 FSM/HFSM 有一个"工业级最佳实践",那 Naughty Dog 在《最后生还者》(The Last of Us, 2013)中实现的 Hunter AI 绝对是这个方向的代表作。
2014 年,Naughty Dog 的 AI 程序员 Travis McIntosh 在 GDC 上做了一个演讲,题为 《The Last of Us: Human Enemy AI》。 这个演讲之所以能成为经典,不是因为它展示了什么新技术——恰恰相反,它展示的是如何把一套成熟的系统通过大量工程调优做到 3A 级别的水准。
Skills Priority Stack + Behaviours 两层架构
Naughty Dog 的 Hunter AI 并非传统意义上的 HFSM,而是采用了一种技能优先级堆栈(Skills Priority Stack)+ Behaviours的分层架构。
核心思想很简单:把决策分成两层,上层决定"做什么",下层决定"怎么做"。
Skills(技能 / 高层决策): 这是 AI 的"战术层"。它决定 Hunter 当前应该采取什么策略——追击玩家、在掩体后等待、搜索扰动来源、包抄玩家侧翼,还是撤退。这些 Skills 通过一个优先级堆栈(Priority Stack)管理,每个 Skill 有自己的启用/禁用条件,堆栈决定了哪些 Skill 可以中断哪些 Skill。
Behaviours(行为 / 底层执行): 这是 AI 的"执行层"。它负责把高层意图落地——怎么移动、怎么瞄准、怎么射击、怎么播放动画。Behaviours 通过一个Behaviour Stack管理,负责改变动画状态和寻路系统。
Post Selectors(位置选择器): 用于选择掩体、开放位置等,这些选择器的条件定义使用LISP 脚本编写。
注意: TLOU 的系统更接近一个"带优先级的任务调度器",而不是传统 HFSM 的"状态机 + 子状态机"模型。传统 HFSM 中,子状态之间的切换仍然需要显式编写转移线;而 TLOU 的架构中,系统通过优先级堆栈自动选择最高分的 Skill,不需要手动编写 Skill 之间的转移关系。
![]()
这种架构的好处是解耦。
一个 Hunter 可以有"追击"这个 Skill,但具体怎么追击——是跑步追击还是蹲伏追击——由 Behaviour 层决定。
不同敌人可以共用同一套 Behaviours,只需要配置不同的 Skills。同一套"走路"和"射击"的底层代码,普通士兵和精英士兵都能用,区别只在于高层决策不同。
感知系统:视觉视锥 + 音频传感器
Hunter AI 之所以"看起来聪明",很大程度上归功于它的感知系统。
这个系统由两部分组成:
视觉视锥(View Cones): 每个 Hunter 有一个扇形的视野区域。在这个区域内的玩家会被"看到",区域外的则不会。视锥的角度和距离会根据难度和情境动态调整。
音频传感器(Audio Sensors): 玩家在环境中发出的声音(脚步声、枪声、碰倒物品)会被 Hunter "听到"。声音传播的距离和方向决定了 Hunter 的反应——听到远处枪声可能只是"警觉",听到身边脚步声则直接进入"搜索"状态。
感知系统和决策系统的分离,是《最后生还者》AI 设计的另一个精妙之处。 感知系统输出"玩家可能在附近"这样的模糊信息,决策系统再根据这些信息选择对应的 Skill。
核心设计理念:"让角色不做蠢事"
Travis McIntosh 在演讲中提到了一句非常经典的话,也是整个 Hunter AI 设计的核心理念:
"让角色不做蠢事比让角色变聪明更重要。"
("Making sure characters don't do stupid things is more important than making them do smart things.")
这句话的含义是:玩家对一个 AI 的"聪明度"感知,很大程度上取决于它什么时候不犯错。 一个 AI 如果在玩家已经躲进掩体后还在原地站着不动,玩家会觉得它"蠢"。但如果它能在玩家消失后主动搜索、包抄或者等待,玩家就会觉得它"聪明"——哪怕它的行为逻辑本质上还是 if-else 判断。
所以《最后生还者》的 AI 团队花了很多精力在防止 AI 做蠢事上:
避免 Hunter 在开阔地带暴露
避免多个 Hunter 挤在同一个掩体后
避免 Hunter 在玩家已经绕到身后时还面朝前方 这些"不蠢"的行为,比"聪明"的行为更能提升玩家的游戏体验。
FSM/HFSM 的局限性
在给出局限性之前,先说句公允话:HFSM 确实缓解了状态爆炸问题。 通过把状态组织成层级,子状态内部的转移不需要与外部状态一一连接。一个"战斗"子状态机内部可以有 10 个状态,但对外只表现为一个"战斗"状态——这大幅减少了顶层状态之间的转移线数量。
但 HFSM 也有它的结构性局限:
子状态间的转移仍然需要显式定义。 同一层级内的子状态切换,仍然需要编写转移线和条件。新增一个子状态,仍然需要考虑它和同层级其他状态之间的所有转移关系。
不能天然实现优先级抢占。 如果一个 Hunter 正在"追击"玩家,突然听到手雷飞来——在 HFSM 中,需要从"追击"状态显式写一条转移到"躲避"状态的线,并在躲避完成后写恢复逻辑。而在行为树中,这只需靠 Selector 的排列顺序隐式表达。
跨层级通信成本高。 底层 Behaviour 需要向顶层 Skill 报告执行结果,顶层需要向底层传递意图参数。随着层级加深,通信开销增长。
Naughty Dog 用工程上的匠心克服了这些局限——通过精心设计的优先级堆栈和 LISP 规则脚本,让 Hunter AI 实现了接近行为树的优先级抢占效果。 但这种方式依赖大量手动调优和规则编写,而不是架构本身的优势。 有没有一种架构,能从根本上减少这些维护成本?
有。而且它已经在一款 2004 年的游戏中被证明有效。
第二章:行为树的核心概念
从"改不动"到"搭积木"
在聊 Bungie 的选择之前,我们需要先回答一个根本问题:
行为树到底解决了 FSM 的什么痛点?
FSM 的三个根本矛盾
上一篇中我们详细拆解了 FSM 的三大矛盾,这里简要回顾一下
改不动(状态爆炸): 所有状态摊在一个平面上。加一个新行为,需要考虑它和每一个已有状态的转移关系。N 个状态加 1 个新状态,可能需要新增 N 条转移线。状态越多,修改成本越高。
走不远(行为组合困难): FSM 一次只能在一个状态里。想让 AI"一边后退一边射击一边扔手雷"?你得新建一个"后退 + 射击 + 扔手雷"的组合状态。组合的数量随行为数量指数级增长。
看不懂(调试困难): 看到 AI 在做什么容易(它当前在哪个状态),但它为什么选了这个状态不容易。状态图越大,理解"为什么 AI 此刻在做这件事"就越需要追踪一大片转移线。
行为树用一个根本不同的思路来解决这些问题。(~ ̄▽ ̄)~
行为树的核心概念——一家公司的决策体系
行为树把决策组织成一棵树。 从根节点开始,沿着优先级从高到低的路径一路走,找到第一个"当前应该执行的行为"。 每帧从头重新评估(Tick),确保 AI 永远能响应最新的游戏状态。
在讲解行为树的核心机制前,让我们先请各位总裁来安排一家公司的决策体系:
打卡机(Root 节点): 每天准时启动这家公司,但从不参与任何决策——"签到成功"
部门总监(Selector 节点): 每天开无数次例会,从左到右检查手里的项目——"先看看项目 A 能不能推进?不能?那项目 B 呢?"
项目经理(Sequence 节点): 按步骤推进——"需求评审做完没?好了?那进入开发阶段……"
质检员(Condition 节点): 只检查不干活——"预算批了吗?批了,放行。没批,打回去。"
基层员工(Action 节点): 真正动手干活的——搓代码、做设计、跑测试。
并行项目组(Parallel 节点): 同时推进两个独立任务——"营销和开发同时开发,互不干扰。"
这套层级体系的精妙之处在于:每一层只关心自己该管的事,不越级。 打卡机不会去管员工代码写得怎么样——他只负责开启美好的一天。 项目经理只管按步骤推进,不问"为什么要做这个项目"。 每个节点只关注自己的职责范围,决策路径清晰可追溯。
Running 状态:行为树"可中断性"的关键锚点
当一个节点返回 Running,父节点不会跳过它——但也不会被它"锁死"。 如果父节点是一个 Selector,而更高优先级的兄弟节点的条件在下一帧变成了真,那么当前 Running 节点会被抢占(中止),高优分支启动。
我们可以用打电话占线来理解: 家长(父节点)在打电话(Running)。
如果有急事(高优先级事件)找家长,电话会被强行挂断(Abort),家长去处理急事。
如果没有急事,家长就继续打他的电话。
抢占的运作逻辑:
![]()
这就意味着:不需要显式写"中断当前行为"的转移线,优先级由 Selector 的排列顺序隐式表达。 后续的"现场改需求"章节会演示这个机制如何让修改成本从"重构整个状态图"降为"插一个新分支"ε(*′・∀・`)з゙。
中止策略(Abort):行为树抢占机制的工程实现
上节提到"高优先级条件成立时,低优行为会被自动中止"——但"自动中止"这个描述在工程实现上需要额外的机制来支撑。 这就是 Abort(中止策略) 的概念。
为什么需要 Abort?
考虑一个场景:AI 正在执行"走到掩体后"的动作(返回 Running),这时玩家突然从侧面冲出来。 如果没有 Abort,即使"射击玩家"这个高优条件已经成立,AI 还是会坚持把"走到掩体后"这个动作执行完——这就产生了一个"迟钝"的时间窗口。
Abort 让行为树能在高优先级条件满足时,同一帧内立即中断当前正在运行的低优先级分支。 它不是等到"下一帧 Selector 重新评估"时才切换,而是即时响应。
三种 Abort 类型
在现代行为树引擎(如 Unreal Engine)中,Abort 有三种策略:
Self(自中止): 当前节点检查自己的条件。如果条件不再满足,自行中止。
示例:AI 正在"追击玩家",但玩家超出了追击距离——Self Abort 会立即终止追击。
Lower Priority(低优先级中止): 当前节点运行时,如果有更高优先级的兄弟节点的条件变为真,则中止当前节点。
示例:AI 正在"巡逻",突然检测到"手雷飞来"——高优先级的"躲避手雷"分支触发 Abort,立即中断巡逻。
Both(两者兼有): 同时启用 Self 和 Lower Priority。这是 Unreal Engine 中 Selector 节点的默认 Abort 策略。
Observer Abort:更高效的实现
朴素的 Abort 实现需要每帧检查所有节点的条件,这在高深度行为树中开销不小。 Observer Abort(观察者中止) 是一种优化策略:
当一个节点开始执行时,它会"注册观察"自己关心的条件变量(如"玩家距离"、"是否有手雷")。
只有当这些条件变量发生变化时,才会触发重新评估和可能的 Abort。
这避免了每帧遍历所有条件的冗余计算。
小结: Abort 策略是行为树"可中断性"的工程基石。后续的"改需求"章节中,"插入一个新手雷躲避分支"之所以不影响已有行为,正是得益于 Abort 机制的自动优先级裁决 (๑´`๑)。
Selector 与 Sequence:两种组合逻辑(Memory 版本)
Selector(选择器)和 Sequence(顺序器)是行为树中最核心的两种组合节点。
说明: 以下展示的是 Memory Selector / Memory Sequence 的行为——这是现代游戏引擎(如 Unreal Engine)的默认实现方式。标准 Selector/Sequence 在每一帧会从头尝试,而 Memory 版本会"记住"上次运行的子节点位置,下一帧从该位置继续,避免重复执行已成功的子节点。两者的核心逻辑一致,差异仅在于是否需要保留执行状态。
Selector 的逻辑是"选一个能干的": 从左到右尝试子节点,停在第一个返回 Success 或 Running 的那一个。如果子节点返回 Failure,则继续尝试右边下一个。
![]()
Selector
Sequence 的逻辑是"按步骤走完": 从左到右依次执行子节点,遇到 Failure 立即停止,全部 Success 才算整体成功。
![]()
Selector 管优先级 / 选择性(选哪个方案),Sequence 管步骤 / 顺序性(方案内的步骤怎么走)。两者组合使用,就构成了一棵完整的决策树。
![]()
耄耄没有地方放了
不过行为树不是在所有方面都比 FSM 好。 如果你的 AI 只有 5 个状态,FSM 写起来更快、跑起来更快、调试起来更简单。 但当状态数超过 15 个、行为需要灵活组合、开发过程中需求频繁变更时,行为树的结构性优势就会显著体现出来(≧∀≦)ゞ。
第三章:《光环 2》
第一次在 3A 游戏里证明"树形结构"有效
2004 年,《光环 2》发售。 2005 年,Bungie Studios 的 AI 程序员 Damian Isla 在 GDC 上发表了一个演讲:《Handling Complexity in the Halo 2 AI》。
这个演讲能成为游戏 AI 领域的经典文献,不是因为它介绍了什么全新的学术理论——而是因为它第一次在 3A 游戏里大规模展示了树形优先级决策结构的实际效果。
严格来说,《光环 2》用的不是现代意义上标准化的行为树(缺少 Tick / 返回状态等标准机制),而是一种叫做 Objective Tree(目标树) 的架构。 这是 Damian Isla 在 GDC 2005 演讲中使用的正式术语。 它运行在 HFSM(分层有限状态机)框架内,是一种声明式的树形结构,用来管理 AI 目标的优先级和复用。 它是行为树概念的前身和早期变体,第一次在商业游戏里证明了"树形优先级结构"的有效性,直接启发了后来行为树在游戏开发中的广泛采用。
Covenant 敌人的战术行为
在《光环 2》中,Covenant 阵营的敌人(精英战士 Elite、咕噜人 Grunt、豺狼人 Jackal 等)展现出了让人印象深刻的战术行为。
先看进攻和撤退——敌人会根据战况判断是该冲上来打,还是退回去找掩体。 不是固定脚本,而是根据当下情况自己做决定。
再看团队配合。多个敌人会协同行动:有的正面吸引火力,有的绕到玩家侧翼包抄。 这不是预先编排的动画,而是每个敌人独立决策的结果。
还有一些细节也很到位:
投掷手雷不是盲目乱扔,而是会考虑落点和玩家的掩体位置。
不同兵种有不同风格:Elite 更主动攻击,Grunt 更容易恐慌逃跑,Jackal 更擅长用能量盾防守。
这些行为在玩家看来非常"智能",但底层逻辑其实并不复杂: 树形优先级结构让每个敌人都能根据当前环境条件,自主决定最合适的行为。
Tick 机制在《光环 2》中的实际体现
虽然《光环 2》的架构不完全等同于现代行为树,但它已经包含了 Tick 机制的核心思想: 每帧重新评估战术优先级。
这意味着敌人的决策不是一次性做出的。
它不是在"追击"状态里一路追到底。 而是每一帧都在问自己:"我现在应该继续追击吗?还是该找掩体?还是该扔手雷?"
如果战况变化(比如玩家绕到了侧翼),AI 在下一帧就能调整行为。
这种"每帧重新评估"的机制,是让 AI 看起来"灵活"和"聪明"的关键。 玩家感觉敌人"会根据战况调整策略",本质上是因为敌人的决策树在每一帧都在重新运行。
Selector / Sequence 的实际应用
在《光环 2》的行为架构中,优先级裁决和步骤执行的逻辑已经初具雏形。
优先级裁决(类似 Selector): 敌人先检查"有没有手雷飞来?"(高优)。有的话执行"躲避"。没有则检查"能不能攻击玩家?"(中优)。能的话执行"攻击"。不能的话检查"该不该找掩体?"(低优)。
这种优先级结构天然支持抢占——高优条件成立时,低优行为会被自动中止。
步骤执行(类似 Sequence): 当决定"投掷手雷"时,敌人需要按步骤执行:拾起手雷 → 瞄准 → 投掷 → 返回战斗。
任何一个步骤失败(比如手雷被玩家打掉),整个序列就终止,AI 回到优先级裁决重新选择行为。
![]()
Objective Tree 的行为复用机制
《光环 2》架构中另一个值得注意的设计是 Objective Tree 的共享节点引用机制。
在传统 FSM 中,每个状态是独立的,行为不能复用。"找掩体"这个行为如果被"躲避手雷"和"寻找战术位置"两个场景需要,就得写两份代码。
在 Objective Tree 中,行为可以作为子节点被多个父节点引用。 "找掩体"行为只写一次,可以被"躲避"、"进攻"、"撤退"等多个分支调用。 这就减少了代码重复,也让行为修改(比如优化"找掩体"的算法)能同时惠及所有调用它的分支。
Objective Tree 的共享节点引用是实现行为复用的早期方案。 现代行为树引擎(如 Unreal Engine 的 Behavior Tree)通过不同的机制(如 Compound 节点、子树引用)实现了类似的可复用性。
第四章:《全境封锁》
"改需求"时的行为树优势
如果说《光环 2》证明了行为树"能做复杂 AI",那么 Massive Entertainment 在《全境封锁》(Tom Clancy's The Division, 2016)中的实现则证明了它"能在大规模 NPC 开发中管理复杂度"。 发售时《全境封锁》的 AI 表现中规中矩,但其行为树工具链和架构设计在 GDC 演讲中得到了业界的认可,尤其在多模板、多派阀的行为管理方面提供了有价值的实践经验。
2016 年,Massive Entertainment 的 Philip Dunstan 和 Drew Rechner 在 GDC 上发表了演讲:《Blending Autonomy and Control: Creating NPCs for Tom Clancy's The Division》。
这个演讲的核心主题不是行为树的技术细节——而是如何用行为树来管理大规模、多派阀、多模板的 NPC 行为开发。
11 种 AI 模板 + 4 个派阀
《全境封锁》的 NPC 系统规模相当庞大:
11 种 AI 模板: Assault(突击)、Rusher(冲锋)、Sniper(狙击)、Thrower(投掷)、Controller(控制)、Heavy Weapons(重武器)、Tank(坦克)、Leader(领袖)、Support(支援)、Special(特殊)等。
4 个派阀: Rioters(暴徒)、Cleaners(净化者)、Rikers(瑞克帮)、Last Man Battalion(最后营)。其中 LMB 有标准和高强度两种配置,所以 4 个派阀合计 5 种 AI 变体。
每个派阀有自己的特色行为:
Rioters 更像暴民——冲动、喜欢近战、容易恐慌。
Cleaners 则是军事化组织——配合默契、善于利用掩体、会使用火焰武器。
Rikers 喜欢用毒气和近战。
LMB 则是训练有素的士兵——包抄、压制、战术撤退样样精通。
如果要用 FSM 来实现这套系统,每个派阀 × 每个模板都需要独立的状态图。 11 模板 × 5 派阀 = 55 个变体。 每个变体如果有 15 个状态,就是 800 多个状态定义。 状态之间的转移线?上千条。 维护成本可想而知( >﹏<。)。
行为树如何解决规模问题
行为树的解决方案是模块化组合:
基础行为(找掩体、移动、瞄准、射击)写一次,所有模板和派阀共用。
派阀特色通过不同的行为树配置实现:Rioter 的行为树里"近战冲锋"的优先级更高,LMB 的行为树里"包抄"的优先级更高。
模板特色通过参数调整实现:Sniper 的"射击距离"参数更大,Tank 的"血量阈值"参数更高。
这种架构的核心优势是: 新增一个模板不需要重写已有代码,只需要种一棵新的行为树(或调整已有行为树的参数)。
可视化编辑器 + 运行时调试工具
Massive Entertainment 搞了一套专有的行为树可视化编辑器,让设计师(不仅仅是程序员)可以创建和修改 NPC 行为。
编辑器支持:
拖拽节点构建行为树
实时预览行为树执行流程
运行时调试:在游戏运行过程中观察 AI 当前执行到树的哪个节点
这套工具链是行为树在实际项目里落地的重要保障。 没有可视化工具,行为树的"可读性"优势就大打折扣——设计师需要看代码才能理解 AI 的逻辑。 有了工具,设计师可以直接在编辑器里看到"AI 正在执行哪个行为"、"为什么 AI 选了这条分支而不是那条"。
FSM vs 行为树:现场改需求的工作量对比
这是最能体现行为树价值的场景。
假设开发进行到中期,策划提出了一个新需求: "当玩家在掩体后超过 5 秒时,敌人应该投掷手雷把玩家逼出来。"
用 FSM 实现这个需求:
新增一个"投掷手雷"状态。
从每一个可能进入"战斗"的状态(巡逻→发现玩家→追击→战斗→躲掩体→……)画一条转移到"投掷手雷"的线。假设涉及 5 个已有状态,就是 5 条新转移线。
在每条转移线上写条件:"玩家在掩体后 > 5 秒"。
考虑"投掷手雷"完成后的转移:回到战斗?回到追击?回到躲掩体?
考虑并发问题:如果"投掷手雷"和"被手雷炸"同时触发,谁优先?
测试:确保每条新转移线在所有场景下都能正确触发,不会和已有转移线冲突。
![]()
过于复杂实在画不出来了
用行为树实现这个需求:
在 Selector 的"战斗"分支下,插入一个新的 Sequence 子节点。
这个 Sequence 包含两个子节点:Condition("玩家在掩体后 > 5 秒?")→ Action("投掷手雷")。
完成。0 个已有节点被修改。
![]()
这个多清爽
这就是为什么 Massive Entertainment 选择了行为树——不是因为 FSM 做不出《全境封锁》的 AI 效果。 而是因为在大规模、多模板、多派阀的开发过程中,行为树的修改成本远低于 FSM(u‿ฺu )。
第五章:《F.E.A.R.》
两种思路,一个选择
上一篇中我们简要提到了 GOAP,说它用"三个状态 + 一个规划器"的架构把 FSM 压缩到了极致。 这里展开详述——因为 GOAP 是行为树之外,另一条解决 FSM 局限性的有效路径。
在讨论游戏 AI 时,如果不提《F.E.A.R.》(First Encounter Assault Recon, 2005),那就像讲操作系统不提 Linux 一样不完整。
Monolith Productions 的 AI 程序员 Jeff Orkin 在 GDC 2006 上发表了演讲:《Three States and a Plan: The A.I. of F.E.A.R.》。 这个演讲介绍了一种完全不同于 FSM 和行为树的 AI 架构: GOAP(Goal-Oriented Action Planning,面向目标的动作规划)。
而这套系统之所以让人印象深刻,不是因为它用了什么高深的算法——恰恰相反,它的状态机部分只有三个状态: Jeff Orkin 在演讲中使用的命名是 Goto / Animate / Use Smart Object:
Goto(移动): 移动到目标位置。
Animate(动画): 执行具体动作(射击、换弹、翻越掩体、投掷手雷等)。
Use Smart Object(使用场景物体): 与环境中的智能对象交互。
![]()
Jeff Orkin 在演讲中提到,Use Smart Object本质上是 Animate 的一个特化版本——所以在某些实现中,底层状态机实际上只有两个状态。 是的,三个状态。
但就是这个看起来"过于简单"的状态机,驱动了游戏中那些让无数玩家惊呼"这 AI 也太聪明了吧"的敌人行为。
GOAP 的核心思想:规划器 + 原子动作
GOAP 的魔法不在于状态机,而在于它背后的规划器(Planner)。
规划器的工作原理是这样的:
定义目标(Goal): 比如"消灭玩家"、"找到掩体"、"换弹夹"。
定义原子动作(Action): 每个动作有前置条件(Precondition)和后置效果(Effect)。比如"射击"的前置条件是"枪里有子弹 and 能看到玩家",后置效果是"玩家受伤"。
规划(Plan): 规划器从当前状态出发,搜索一条动作序列,使得执行完这条序列后能达到目标。这本质上是一个搜索问题。
执行(Execute): 将规划出的动作序列交给状态机逐个执行。如果执行过程中环境变化导致规划失效,重新规划。
举个具体例子。假设敌人的目标是"消灭玩家",但枪里没有子弹了:
![]()
当前状态:枪里没子弹,玩家在掩体后
目标:消灭玩家(҂‾ ▵‾)︻デ═一
规划结果:
找掩体(前置条件:附近有掩体 → 效果:在掩体后)
换弹夹(前置条件:在掩体后 and 有备用弹夹 → 效果:枪里有子弹)
从掩体后射击(前置条件:枪里有子弹 and 能看到玩家 → 效果:玩家受伤)
如果换弹夹的过程中玩家绕到了侧面,规划器会重新规划: 也许变成"扔手雷"而不是"射击",因为前置条件满足了(手雷在包里 and 玩家在投掷范围内)。
为什么 F.E.A.R. 的敌人给人感觉"极其聪明"
F.E.A.R. 的敌人之所以给玩家留下深刻印象,有几个关键原因:
动态规划带来不可预测性。 敌人不是按照预设的脚本行动,而是根据当前环境实时规划。同样的初始状态,如果玩家的走位不同,敌人的应对策略也可能不同。这种"不可预测但合理"的行为,最接近玩家对"智能"的定义。
环境利用能力。 GOAP 的原子动作可以引用环境信息——"附近有掩体"、"有手雷可用"、"有高台可以占据"。规划器会自动选择利用这些环境要素的动作序列。玩家觉得敌人"会用环境",其实是因为规划器搜索出了利用环境的最优路径。
团队配合的幻觉。 多个敌人使用相同的 GOAP 系统,它们会根据各自的环境条件做出不同的规划。一个敌人正面射击吸引注意力,另一个敌人绕到侧翼——这在玩家看来是"团队配合",但本质上只是两个独立的规划器各自做出了当前最优的决策。
补充: 网上有一些文章把《中土世界:暗影魔多》中的"涌现式叙事"归因于 GOAP,这是不准确的。Nemesis System(宿敌系统)的核心是一套角色关系追踪 + 动态叙事生成系统,与 GOAP 的规划机制不同。GOAP 擅长的是单个 AI 的行为规划,涌现式叙事则需要额外的系统来追踪角色间的关系和事件历史。
GOAP 与 行为树对比
GOAP 是给 AI 装了个大脑,让它自己想(灵活但难控)。 行为树则是给了 AI 一本操作手册,让它照着做(死板但稳定)。
![]()
GOAP 为什么没有成为主流?
GOAP 看起来很香,但它有几个实质性的局限:
性能开销大。 规划是一个搜索问题。当动作数量多、环境条件复杂时,规划可能需要显著的计算时间。不过,业界已有多种优化方案——包括分层规划、增量规划、以及规划结果缓存。在合理的优化下,GOAP 的性能开销是可以接受的。
调试困难。 当 AI 做出"蠢行为"时,你需要理解规划器为什么选择了这条动作序列——是前置条件定义有误?还是搜索算法找到了次优路径?这比行为树的"这个分支为什么没走到"难定位得多。这是 GOAP 最实际的障碍。
行为不可控。 规划器可能组合出设计师没有预料到的行为序列——其中有些可能是 bug。行为树中,AI 只能做树里定义好的事。对于需要精确控制难度曲线的游戏(如魂系),这一点尤其关键。
环境建模复杂。 每个动作都需要精确定义前置条件和后置效果。维护一套完整的"动作-条件-效果"知识库本身就是一项大工程。
正是因为这些局限,GOAP 在实际商业游戏中的采用率远不如行为树。 但这不意味着 GOAP 没有价值——在需要高度自适应行为的场景(如沙盒游戏、模拟类游戏)中,GOAP 的规划能力是行为树难以替代的。
游戏 AI 的首要任务不是"变聪明",而是"为玩家提供有趣的挑战"——这通常需要精确控制 AI 的行为范围和强度,而这正是行为树的优势领域┐(´д`)┌。
第六章:《耻辱 2》
为什么沉浸式模拟游戏不用行为树?
沉浸式模拟游戏(Immersive Sim)对 AI 的要求,几乎是所有游戏类型中最极端的。
为什么? 因为沉浸式模拟游戏的核心设计理念是"给玩家提供多种解决问题的方式"。
在《耻辱 2》(Dishonored 2, 2016)中:
玩家可以正面突突突
可以潜行绕过所有敌人
可以用超能力瞬移到敌人身后暗杀
可以把敌人引到一起让他们互相误伤
可以黑入敌人的通讯系统让他们自相残杀……
玩家的玩法是不可预测的。 AI 系统必须能应对设计师自己都可能没有想到的玩家行为组合。
2017 年,Arkane Studios 的 Laurent Couvidou 和 Xavier Sadoulet 在 GDC 上发了演讲:《Taking Back What's Ours: The AI of Dishonored 2》。 这个演讲展示了一个面对极端玩家行为时,AI 系统是怎么设计和演进的。
规则引擎 vs 行为树:Arkane 的选择
GDC 2017 演讲原文显示,Arkane 的《耻辱 2》AI 决策核心是规则引擎(Rule System)而非行为树。
为什么沉浸式模拟游戏选择了规则引擎? 因为沉浸式模拟需要"去中心化"的响应机制。
规则引擎的核心特征是:
规则可以独立定义、独立评估、独立触发。
不需要像行为树那样受限于固定的树形结构。
当玩家做出完全出乎意料的行为时,规则系统可以通过新增规则来应对,而不需要重构整棵树。
这正是《耻辱 2》面对"不可预测的玩家"时的最佳选择。
应对"不可预测的玩家"
《耻辱 2》的 AI 面临的最大挑战是玩家超能力的不可预测性。 玩家可能同时使用"时间减速 + 瞬移 + 附身"的组合,也可能把整个场景的物品都变成炸弹。 AI 需要在完全无法预料玩家行为的前提下,仍然做出合理的反应。
Arkane 的解决方案是基于规则引擎的决策系统:
总共约 6000 条规则: GDC 2017 演讲原文列出了 5 组数据(4609、373、596、204、246 条),加起来差不多 6000 条,涵盖 NPC 行为、战斗策略、对话动画等多个方面。
数据驱动配置: 这些规则不是硬编码的——它们是数据驱动的。规则作为配置数据存在,规则引擎根据当前情境评估并选择最合适的行为。这意味着设计师可以在不修改代码的情况下调整 AI 的行为。
小队成员间的情报共享
《耻辱 2》的敌人小队有一个精妙的设计:情报共享系统。
当一个小队成员发现玩家时,它不会独自追击——它会把玩家的位置信息分享给小队其他成员。 其他成员收到信息后,会根据各自的位置和状态做出响应:有的从正面压制,有的从侧翼包抄,有的呼叫增援ᕕ( ᐛ )ᕗ。
这套系统的核心是数据共享机制——规则引擎的每条规则都可以读取和写入共享的情报数据。 当一个成员写入"玩家位置:X, Y, Z"时,其他成员的规则在下一轮评估时就能读取到这个信息并触发响应。 这与行为树中的"黑板"(Blackboard)概念类似,但实现方式更灵活——规则引擎不需要固定的树形结构来组织数据读写。
![]()
复杂系统的"暗坑"
但 Arkane 在实现过程中也发现了一些"暗坑"——那些在理论上很美好、在实践中却很棘手的问题:
规则数量爆炸。 6000+ 条规则听起来很壮观,但每一条规则都需要测试、验证、调试。当规则之间存在冲突或优先级不明确时,AI 可能做出不合理的行为。规则越多,冲突的可能性越大。
决策深度控制。 规则系统可以无限复杂地嵌套——规则引用规则,条件叠加条件……深度超过 5 层后,系统的可读性急剧下降。调试一个 10 层深的规则链,和理解一个 50 个状态的状态图一样痛苦。
性能优化。 《耻辱 2》的规则评估系统开销不小——尤其是当场景中有大量 NPC 时。Arkane 需要实现优先级裁剪(低优先级规则在特定条件下跳过评估)、评估频率调整(不同 NPC 以不同频率评估规则)、以及观察者模式(只有情境数据变化时才重新评估相关规则)等优化手段。
数据驱动的双刃剑。 规则作为配置数据虽然灵活,但也意味着 bug 可能不在代码里而在数据里。"AI 为什么不躲避"可能不是因为规则逻辑有 bug,而是因为某条规则的优先级配错了。这类 bug 往往更难定位。
这些"暗坑"不是某个特定架构的根本缺陷,而是任何复杂系统都会面临的工程挑战。 无论是行为树、规则引擎还是 GOAP,框架之上仍然需要精心的工程实践来保证系统的稳定性和可维护性(;´д`)。
第七章:行为树的陷阱
那些暗坑
行为树不是银弹。
No Silver Bullet. ——Fred Brooks

有银弹也不代表能杀掉吸血鬼
你在文档里看到的都是"行为树多么优雅、多么好扩展"——但实际项目中,行为树有自己的调试地狱。
这里单辟一小节,讨论行为树在工业实践中最常遇到的三个"暗坑"。
暗坑一:条件抖动(Condition Jitter)
当一个高频变化的 Condition 节点导致 AI 在两个行为之间高频来回切换时,AI 的表现可能比 FSM 更"智障"。
举个例子。 AI 的行为树顶层有两个分支:
分支 1(高优):玩家距离 < 10m → 近战攻击
分支 2(低优):玩家距离 >= 10m → 远程射击
如果玩家刚好站在 10m 的边界上晃动,AI 会在"近战攻击"和"远程射击"之间每帧切换。 玩家看到的效果就是:AI 像个癫痫患者一样抽搐——往前冲一步又退回来,举枪又放下。
怎么解决?
加迟滞(Hysteresis): 近战触发距离 8m,退出距离 12m。中间留一个"缓冲区"。
降低 Condition 评估频率: 不是每帧都检查,而是每 0.5 秒检查一次。
使用 Service 节点的 Tick 间隔控制: 不要让 Service 每帧都跑。
暗坑二:Running 节点泄漏
一个永远返回 Running 的节点会让 AI"卡死"在一个行为上,永远无法被抢占。
举个例子。 你写了一个"移动到目标位置"的 Action 节点。如果目标位置永远不可达(比如被墙挡住了),这个节点会一直返回 Running。
结果: AI 卡在一个永远到不了的位置,所有更高优先级的分支都被它占着位置,无法触发。 玩家看到的效果就是:AI 对着一面墙发呆,玩家走过去把它打死它都不动。
怎么解决?
给所有 Running 节点加超时机制。 如果超过 N 秒还在 Running,强制返回 Failure。
用 Blackboard 记录"尝试过但失败的位置",避免重复尝试。
代码审查时重点检查所有可能永远 Running 的节点。
暗坑三:深层树的重入问题
当行为树深度超过 5-6 层时,同一帧内的 Tick 遍历可能出现"重入"——一个节点在同一帧内被多次执行。
这在 Parallel 节点和嵌套 Selector/Sequence 的组合中尤其常见。
举个例子。 你有一个 Parallel 节点,两个子分支都引用了同一个"播放动画"的子树。如果这个子树内部有状态修改(比如设置 Blackboard 变量),同一帧内两次执行可能导致状态覆盖。
结果: AI 播放了错误的动画,或者 Blackboard 数据被意外覆盖。
怎么解决?
避免在行为树中共享可变状态。 如果必须共享,确保它是幂等的。
限制行为树深度。 超过 5-6 层时考虑拆分成多棵子树。
在调试工具中标注"重入风险节点"。
第八章:总结
写到这里,我们已经走过了从《最后生还者》的 HFSM,到《光环 2》的早期行为树实践,再到《全境封锁》的大规模行为树应用,以及 GOAP 的替代方案和《耻辱 2》的极端场景挑战。
现在是时候回答一个核心问题了: 行为树到底是神器,还是又一个被过度吹捧的技术?
技术演进的启示
回顾整个技术演进历程,有几个清晰的趋势:
从"能做什么"到"能改什么"。 FSM 的瓶颈不是功能不够——理论上 FSM 可以模拟任何行为树的逻辑。它的瓶颈是修改成本。当状态数超过某个阈值,加一个行为的成本变得不可接受。行为树解决的正是这个问题——它让新增行为的成本从 O(N) 降为 O(1)。
从"单体决策"到"分层架构"。 《最后生还者》的 Skills Priority Stack + Behaviours 两层架构,本质上是把决策和执行解耦。行为树的 Selector / Sequence 组合机制,则是把优先级和步骤解耦。分层解耦是管理复杂度的通用方法。
从"硬编码"到"数据驱动"。 《全境封锁》的行为树配置、《耻辱 2》的规则系统,都体现了同一个趋势——把 AI 行为从代码中抽离出来,变成可配置的数据。这让设计师(不仅仅是程序员)可以参与 AI 调优,也让迭代速度大幅提升。
2020 年代的新趋势:混合架构
进入 2020 年代后,游戏 AI 架构呈现出明显的混合化趋势——不再追求"用一种架构解决所有问题",而是根据场景组合使用多种技术:
Utility AI + 行为树混合。 《模拟人生》(The Sims)系列长期使用的 Utility AI 在近年受到更多关注——它的核心是"给每个可选行为打分,选最高分",就像 Sim 们会根据饥饿、社交、娱乐等需求的紧急程度自动决定"先去吃饭还是先去社交"。现代实践中,常见的模式是用 Utility AI 做高层决策(选哪个行为),用行为树做底层执行(行为的具体步骤)。这种组合兼顾了灵活性和可控性。
更好的行为树案例。 如果你想了解行为树在现代游戏中的表现,以下几个案例值得研究:
《Alien: Isolation》(2014): 异形 AI 使用双层 AI 系统——"导演 AI"(知道玩家位置但不可直接攻击)控制"异形 AI"(不知道玩家位置但会搜索),行为树在其中扮演了关键角色(Mike Lipshaw, GDC Europe 2015)。
《Halo 3》(2007): Bungie 在《光环 2》的基础上进一步进化了行为树架构,增加了更完善的并行行为支持和更精细的优先级控制。
《地平线:零之曙光》(2017): Guerrilla Games 的机器兽 AI 使用了复杂的行为树系统,不同机器兽有不同的行为树配置,支持动态行为切换。
机器学习辅助 AI。 虽然深度学习尚未取代传统的基于规则的 AI 架构(因为可控性要求),但在特定子任务上已有应用——如《Forza Motorsport》中的驾驶 AI 使用了强化学习来优化赛车线。
核心趋势总结: 游戏 AI 正在从"单一架构之争"走向"实用主义混合"。选择哪种架构不再是信仰问题,而是工程问题——根据项目需求、团队技能和开发周期做出最务实的选择。
行为树的核心价值和代价
核心价值:
可中断: 优先级抢占不需要显式写中断逻辑。
可复用: 行为节点可以被多个分支引用(DAG 结构)。
可扩展: 新增行为只需要插入新分支,不影响已有节点。
可读性: 树形结构直观反映决策逻辑。
可调试: 可以实时观察 AI 的执行路径。
代价:
学习成本: 理解 Tick 机制、Selector / Sequence 逻辑、Running 状态的抢占行为,需要一定的学习投入。
运行时开销: 每帧遍历树比状态查询开销大。需要优化(Observer Abort、Tick 频率调整等)。
调试复杂度: 深度嵌套的行为树同样难以理解。Parallel 节点会增加调试难度。
过度设计的风险: 不是所有 AI 都需要行为树。简单 AI 用行为树是杀鸡用牛刀。
技术选型决策框架
如果你在开发一款游戏,在 FSM 和行为树之间犹豫,可以参考这个决策框架。
注意: 这个框架没有考虑最关键的现实变量——团队能力。 一个对 FSM 有丰富经验、有完善自研调试工具的团队,和一个熟悉 UE4 行为树但缺乏深入理解的新手团队,他们的技术选型会截然不同。 架构选择从来不只看技术优劣,更要看团队能不能驾驭它。
![]()
没有最好的架构,只有最适合当前项目需求的架构。 行为树不是通解——它只是管理 AI 复杂度的一个工具。 用好它的前提是理解它的优势和代价,然后在合适的场景下使用它。
下期预告
如果你已经读到这里,说明你对游戏 AI 这个话题是真的感兴趣。
下一期,我们会聊一个更加有趣的话题:Utility AI(效用 AI)和 AI 动态难度调整实践。
简单来说,Utility AI 让 AI 的决策不再基于"满足 / 不满足"的二元条件,而是基于"这个行为有多好"的量化评分。
AI 不再问"我该不该逃跑",而是问"逃跑、进攻、躲掩体、扔手雷——哪个效用值最高"。
更重要的是,我们会讨论Utility AI 在动态难度调整(DDA)中的应用——这正是现代游戏实现"无感难度调节"的核心技术:通过量化评估玩家当前状态,实时调整 AI 的行为倾向、反应速度和资源分配,让游戏始终保持在"既不太简单、也不太困难"的心流区间。
这种技术在《求生之路》的 AI Director、《生化危机4》的动态难度系统、以及多款现代独立游戏中都有成功应用。
这套思路比传统难度分级更自然(没有"简单/普通/困难"的硬性切换),比固定脚本更灵活(AI 行为随玩家表现实时变化)——而且它的核心思想(给每个选项打分然后选最高的)其实非常直觉。
如果你对游戏 AI 感兴趣,或者正在为自己的游戏选择 AI 架构,下一期内容一定不要错过。
感谢阅读!(๑•ㅂ•)✧
附录:关于 AI"读指令"的常见误解
Tick 机制本身不区分"世界状态变化"和"按键输入"。 如果 AI 没有感知延迟,它可以在手雷生成的同一帧做出反应——这在物理上当然不可能(信息传递需要时间),但在代码层面它确实可以。这就是为什么有些游戏的 AI 会让玩家觉得"它读了我的指令"。
"读指令"在玩家社区里是个更广义的概念,不只是指 AI 直接读取你的按键输入,更指 AI 以远超人类反应速度(<100ms)对任何可观测的行为做出完美反应——哪怕是弹道出现或角色起跳动作的第一帧。 不公平感的来源不一定是 AI 作弊了,更可能是它反应得太完美了,完美到不像人类 (摊手)。
真正"公平"的 AI 设计,不会让 Tick 机制裸奔。 负责任的实现会在 AI 和环境之间加入"感知延迟"——比如 AI 的视野检查每隔 0.2 秒才触发一次,听觉范围加一个模糊半径,让 AI 不能"看到"墙壁后面的东西,甚至在看到玩家后先"愣"零点几秒再反应。 这才是"公平的挑战"和"作弊的读指令"之间的界线。
本文基于公开 GDC 演讲和技术资料整理,不同工作室的实现细节可能有所差异。欢迎评论区交流指正,我会持续更新完善。
参考文献
系列文章索引:
第一篇(FSM):【游戏科普】你以为Boss在思考?关于游戏AI背后的有限状态机
第二篇(行为树):本文
第三篇(预告):Utility AI + 混合架构实践
Travis McIntosh. "The Last of Us: Human Enemy AI." GDC 2014. Naughty Dog. 介绍了 Hunter AI 的 Skills + Behaviours 两层架构、视觉视锥和音频传感器感知系统,以及"让角色不做蠢事"的核心设计理念。
Damian Isla. "Handling Complexity in the Halo 2 AI." GDC 2005. Bungie Studios. 介绍了《光环 2》中 Covenant 敌人的 Objective Tree(目标树)架构,包括优先级裁决、步骤执行和行为复用。这是树形优先级决策结构在 3A 游戏中的开创性应用。
Philip Dunstan & Drew Rechner. "Blending Autonomy and Control: Creating NPCs for Tom Clancy's The Division." GDC 2016. Massive Entertainment. 介绍了《全境封锁》中 11 种 AI 模板和 5 个派阀的行为树实现,以及可视化编辑器和运行时调试工具的开发。
Jeff Orkin. "Three States and a Plan: The A.I. of F.E.A.R." GDC 2006. Monolith Productions. 介绍了 GOAP(Goal-Oriented Action Planning)系统在《F.E.A.R.》中的应用,包括规划器原理、大量目标(dozens of goals)的动态选择、以及只有三个状态(Goto、Animate、Use Smart Object)的状态机架构。
Laurent Couvidou & Xavier Sadoulet. "Taking Back What's Ours: The AI of Dishonored 2." GDC 2017. Arkane Studios. 介绍了《耻辱 2》中应对不可预测玩家行为的 AI 系统,包括约 6000 条规则(GDC 原文列出 5 组数据:4609、373、596、204、246),以及小队情报共享机制。
Ian Millington. Artificial Intelligence for Games. CRC Press, 2009. 游戏 AI 领域的经典教材,全面介绍了 FSM、行为树、GOAP 等技术的理论和实践。
Unreal Engine Documentation. "Behavior Trees." Epic Games. Unreal Engine 官方行为树文档,介绍了 Blackboard、Service、Task 等核心概念以及可视化工具的使用。
更多游戏资讯请关注:电玩帮游戏资讯专区
电玩帮图文攻略 www.vgover.com
