中EN
精修课概念页

决策架构选型:同一个守卫,四种写法

Choosing FSM, Behavior Tree & Utility AI

更新于 2026-10-19 约 12 分钟游戏AI选型FSM行为树UtilityGOAP

同一个守卫

前面几课分别讲了行为树、效用 AI 和 GOAP。这一课把它们放在一起比:拿同一个守卫,用四种架构各写一遍。

守卫的需求很普通:

  • 平时巡逻;
  • 看见玩家就开枪,子弹打光要装弹;
  • 玩家跑了,就去他最后出现的地方搜一搜,搜不到再回去巡逻;
  • 血量低了,不管在干什么,都先找掩体。

下面的四张草图是自己编的示意,只为看清每种架构的「形状」,不是哪个游戏的真实实现。

写法一:有限状态机

状态机 (FSM) 的写法最直接:列出状态,再画出状态之间的转换。

flowchart LR
  P["巡逻"] -->|"看见玩家"| F["开枪"]
  F -->|"子弹打光"| R["装弹"]
  R -->|"装好了"| F
  F -->|"丢了目标"| S["搜索"]
  S -->|"又看见了"| F
  S -->|"搜不到"| P
  P -->|"血量低"| C["找掩体"]
  F -->|"血量低"| C
  R -->|"血量低"| C
  S -->|"血量低"| C

状态少的时候它非常清楚。麻烦在于「血量低就找掩体」这种对所有状态都成立的规则,得在每个状态上各画一条边。再加一个维度更糟:守卫可能拿着枪,也可能空着手。把「在做什么」和「拿着什么」塞进同一台状态机,n 个动作状态乘 m 个装备状态,就是 n × m 个状态。

《游戏编程模式》(GPP) 给了三种扩展办法:

扩展解决什么守卫的例子
并发状态机把正交的维度拆成两台机器,n × m 变成 n + m「动作」一台、「装备」一台,偶尔用 if 互相查一下
分层状态机共用的转换放进父状态,子状态不处理的输入往上冒开枪、装弹、搜索都放进父状态「警戒中」,「血量低 → 找掩体」只写一次
下推自动机用栈记住历史,做完一件事回到之前的状态装弹时把「装弹」压栈,装完出栈,自动回到装弹前在干的事

GPP 的结论是:状态机最大的优点——强约束——也是它的上限。AI 再复杂下去,通常会转向行为树和规划。

写法二:行为树

行为树把守卫的需求写成一张优先级列表,排在前面的先试:

flowchart TD
  R["Selector:守卫"] --> A["Sequence:保命"]
  R --> B["Sequence:战斗"]
  R --> D["Sequence:搜索"]
  R --> E["巡逻"]
  A --> A1["血量低?"]
  A --> A2["去掩体"]
  B --> B1["看得见玩家?"]
  B --> B2["Selector:开火"]
  B2 --> B3["开枪(没子弹就失败)"]
  B2 --> B4["装弹"]
  D --> D1["有上次看见的位置?"]
  D --> D2["去那里搜"]

「血量低就找掩体」不再需要到处画边:它是排在最前面的分支,每次都先检查。没子弹时开枪失败,Selector 自然退到装弹。这正是行为树的强项:优先级回退和模块化的子树。弱点是它基本是反应式的,不会往前推演一长串行动。

写法三:效用 AI

效用 AI 不排顺序,而是给每个行动打分,选最高的:

行动分数怎么算(几项考量相乘)
开枪看得见玩家 × 距离合不合适 × 还有没有子弹
装弹子弹越少越想装
找掩体血越少越想躲 × 附近有没有掩体
搜索上次看见玩家的时间越近,越想去搜
巡逻一条恒定的低分基线

守卫会显得在「权衡」:血不多但玩家就在眼前、掩体又很远时,它可能选择继续开枪。代价是硬优先级很难表达——如果设计要求「血量低时无论如何先躲」,靠调几个分数去凑,迟早会在某个边角情况下被别的高分行动压过去。Damian Isla 在 Halo 2 上就批评过这种纯浮点竞争的做法。

写法四:GOAP

GOAP 只声明目标和行动,计划交给规划器去搜:

行动前提效果
搜索无看得见玩家
装弹有备用弹匣已上膛
开枪已上膛、看得见玩家威胁消除
去掩体附近有掩体在掩体里

目标「威胁消除」、当前状态「没上膛、看不见玩家」时,规划器会排出「搜索 → 装弹 → 开枪」这样的序列;中途被堵住了,就从当前状态重新规划。执行层仍是一个很薄的状态机。它擅长行动组合多、需要应变的情况,代价是世界状态的设计和规划的 CPU。

对照表

状态机行为树效用 AIGOAP
表达状态 + 转换树 + 成功 / 失败 / 运行中分数竞争前提 / 效果 + 搜索
怎么扩展加状态、加转换加分支、加子树加行动和曲线加行动、加目标
失败了怎么办手写转换Selector 自然回退改选次优分重新规划
典型痛点组合爆炸长期规划弱硬优先级难调世界状态设计和 CPU

怎么选

先看问题像什么:

flowchart TB
  Q{"问题像什么?"} --> A["离散的阶段不多:状态机 / StateTree"]
  Q --> B["有优先级、失败要回退:行为树"]
  Q --> C["连续权衡、要调参:效用 AI"]
  Q --> D["行动组合多、要重新规划:GOAP"]
  1. 角色动作、UI、动画层:状态少、转换清楚,用状态机或分层状态机(虚幻引擎里是 StateTree)。
  2. 战术任务、需要 Plan B:用行为树。虚幻引擎里,优先级抢占明显的战斗场景也倾向事件驱动的行为树。
  3. 选项多、要「权衡感」、设计师要调参:用效用 AI,但记住 Isla 对纯浮点优先级的警告。
  4. 行动组合爆炸、要声明式扩展和失败重试:用 GOAP 或别的规划器,执行层仍可以是一个很薄的状态机。

混着用

实际项目很少四选一,更常见的是分层混用:

  • 行为树选高层,效用选叶子战术:树负责「巡逻 / 战斗 / 撤退」这样的阶段,进到战斗分支后,用效用分数挑具体打法。
  • 效用或 GOAP 出意图,状态机落地:Orkin 的 F.E.A.R. 就是计划 → Goto / Animate。
  • Halo 2 的做法:行为有向无环图加优先级列表,相当于行为树的 Selector;再用订单和风格让设计师导控。

在游戏里怎么用

取舍与常见的坑

小结与练习

  • 状态机擅长少量离散状态和硬转换;行为树擅长优先级回退和模块化;效用 AI 擅长连续权衡和数据驱动;GOAP 擅长声明式组合和重新规划;
  • 状态机的组合爆炸可以用并发状态机、分层状态机、下推自动机缓解,但它的上限来自强约束本身;
  • 选型先看问题的形状,实际项目常常分层混用。

练习:给守卫加一条新需求——「听到枪声就去查看」。分别想一想:在状态机里要加几条边?行为树里这个分支放在哪个位置?效用 AI 要加什么考量?GOAP 要加什么行动或目标?哪种改动最小?

  1. Game Programming Patterns — state Robert Nystrom《Game Programming Patterns》State 章
  2. Behavior trees for AI: How they work(笔记) Chris Simpson BT
  3. Infinite Axis Utility System / Utility AI(笔记) Dave Mark IAUS
  4. Three States and a Plan: The A.I. of F.E.A.R.(笔记) Orkin GOAP
  5. Handling Complexity in the Halo 2 AI(笔记) Isla Halo 2
  6. GEA4 Vol II §18.2.3 Artificial Intelligence(摘录笔记) GEA4 §18.2.3
  7. Behavior Tree Overview / StateTree Overview UE 选型语境

相关页面

游戏开发状态模式与有限状态机State Pattern & Finite State Machines状态模式 (State Pattern) 把对象“依赖状态的行为”分散到各个状态类中,对象只持有指向当前状态的指针并把调用委托给它,状态切换就是换指针。它是实现有限状态机 (Finite State Machine, FSM) 的一种方式。FSM 能用很强的结构约束理清一团乱麻的标志位代码;状态组合爆炸、需要复用和需要“回到之前状态”时,分别用并发状态机、分层状态机和下推自动机来扩展。游戏开发行为树原理Behavior Tree Fundamentals行为树 (Behavior Tree, BT) 用层级节点控制决策流:叶子做动作或条件,Sequence / Selector 等组合节点决定子节点如何推进;节点返回 Success / Failure / Running,从而自然跨多帧。相对 FSM,BT 更易用优先级回退表达「失败了还有 Plan B」。游戏开发效用 AIUtility AI效用 AI (Utility AI) 为每个候选行动/决策计算「有多想做」的分数,选最高分(或按分抽样)。Dave Mark 的 IAUS 将其做成纯数据驱动架构:考量 (Consideration) + 响应曲线打分,行为模块可拼装;相对硬编码 if/then,更不易卡死在单一行为。游戏开发目标导向行动规划 GOAPGoal-Oriented Action PlanningGOAP 把「如何满足目标」交给运行时规划:行动带前提与效果,用 A 在世界状态空间搜索最低成本动作序列。F.E.A.R. 用极薄 FSM(Goto/Animate)执行计划,换来目标与行动解耦、行为可叠加、失败可重规划。游戏开发游戏 AI 决策管线Game AI Sense-Decide-Act Pipeline游戏 AI(与生成式 DNN 不同)通常按 感知 → 决策 → 执行 分层:底层是寻路、感知与记忆;中层是角色控制(对接动画/物理);上层是目标与决策(行为树、效用、规划等)。GEA 强调「perception is reality」——玩家看不见的内部聪明不算数。游戏开发 · UE5行为树与 StateTreeBehavior Tree & StateTreeUE 的 AI 决策有两套工具:行为树 (Behavior Tree) + 黑板 (Blackboard) 是事件驱动的,条件写成装饰器 (Decorator),用 Simple Parallel、Services 和 Observer Aborts 代替传统并行节点;StateTree 是结合了行为树选择器和状态机转换的通用分层状态机,只在转换触发时按需选择状态,激活路径上的任务并发运行,通过属性绑定传递数据,也是 Mass 的行为层。优先级抢占多的战斗 AI 适合行为树,状态显式、分层或大规模的场景适合 StateTree。综合游戏编程模式选型:解耦与性能Choosing Game Programming Patterns本页把《Game Programming Patterns》里的 19 个模式放到同一张取舍图上:大多数模式要么为了解耦(降低改代码时需要装进脑子里的知识量),要么为了性能(利用对数据和硬件的具体假设),而这两个目标经常互相拉扯。下面先讲全书的定位和 Nystrom 的架构观,再给出按问题选模式的路线和与 UE 的对照。

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原