中EN
精修课概念页

效用 AI:给每个念头打个分

Utility AI

更新于 2026-10-19 约 13 分钟游戏AI效用Utility AIIAUSConsideration

饿了多久,食物多香

一个 NPC 该不该去吃饭?最直接的写法是一串 if:「饥饿大于 0.7 就去吃」。可是食物在地图另一头呢?刚好在脚边、只是有点饿呢?条件越加越多,规则互相打架,NPC 还容易卡在某个行为里出不来。

效用 AI (Utility AI) 换了个问法:不问「满足哪条规则」,而问「每个选项有多想做」。给吃饭、巡逻、睡觉……每个候选行动各算一个分数,选最高的那个。饿了多久、食物多远、食物多香,都变成这个分数的一部分,一起权衡。

Dave Mark 的无限轴效用系统 (Infinite Axis Utility System, IAUS) 把这个思路做成了一套纯数据驱动的架构。按它的说法,相比硬编码的 if/then,这样的 AI 没那么脆,也更少卡在不该停留的行为里。

一次决策的四步

对每一个候选行动:

  1. 采集输入:血量、距离、弹药……通常先归一化到 [0,1][0,1];
  2. 过响应曲线:每个输入经过一条响应曲线 (Response Curve),变成这一项的分数——这一步叫一个考量 (Consideration);
  3. 合成:把几项考量的分数合成一个总分,业界常见的做法是相乘;
  4. 比较:在所有候选行动里取最高分,或者按分数加权随机,免得 NPC 显得太机械。
flowchart LR
  I["原始输入:饥饿、距离……"] --> N["归一化到 0~1"]
  N --> C["响应曲线(考量)"]
  C --> M["多项相乘,得到总分"]
  M --> P["所有候选行动比分"]
  P --> A["选中的行动"]

响应曲线:把「在乎多少」画出来

同样是「饥饿 0.5」,有的 NPC 已经坐立不安,有的还无所谓。差别就在响应曲线:横轴是归一化的输入,纵轴是这一项给出的分数。下面这些是常见的几种形状,点一点预设,再拖动 k:

响应曲线:输入 x → 分数 y

直接改上面的表达式就能重画:x 是 0 到 1 的横坐标,可以用 sin、pow、smoothstep 等函数。

pow(x, k):k 大于 1 时先慢后快(不太饿时不在乎,饿过头才猛地想吃),小于 1 时先快后慢。1 − x 适合「越近越好」这类反向的输入;smoothstep 给出两头平、中间陡的 S 形;step 是硬开关。

曲线的形状就是设计师手里的旋钮:要调的是 NPC 的「性格」,而不是一串条件分支。这些曲线只是常见形状的举例;IAUS 这类系统具体提供哪些曲线、参数怎么取,以产品文档为准,这里不展开。

动手:两个考量,一张分数地图

现在让 NPC 在「去吃饭」和「继续巡逻」之间选。吃饭的分数由两个考量相乘:饥饿过一条 pow(h, 2.0) 曲线,距离过一条 1.0 - d 曲线;巡逻是一条恒定的基线 0.3。

下面画布的左边是整个状态空间:横轴是饥饿(左边不饿,右边饿极了),纵轴是到食物的距离(下面就在脚边,上面很远)。黄色区域里吃饭赢,越黄分数越高;蓝灰色区域里巡逻赢;橙线是两者打平的分界。白圈是鼠标所在的状态,右边两根柱子是这个状态下两个行动的分数,带橙边的是赢家。

吃饭还是巡逻:两个考量合成的分数地图
改一改

也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。

改曲线、改基线、改合成方式,分界线跟着移动;代码里没有一条 if 在判断「什么时候该吃饭」。

相乘的两个性质

上面最后一条提示暴露了相乘的第一个性质:一票否决。只要有一项考量是 0,总分就是 0。「一点也不饿」本来就应该否决「去吃饭」,相加却会让另一项把分数撑起来。

第二个性质是分数塌缩:每多一项考量,就多乘一个不大于 1 的数。即使每一项都给出不错的 0.9,乘起来也会越来越小:

考量的项数123510
每项都是 0.9 时的总分0.900.810.730.590.35

于是考量多的行动天生吃亏,哪怕它每一项都很合适。所以实际系统常在相乘之外再加一个随项数调整的补偿,避免维数一多分数就塌下去。补偿的具体公式资料里没有给出,这里不展开。

选最高分,还是按分数抽签

总是选最高分的行动,NPC 在相同的局面下会做出一模一样的选择,玩家很快就能看穿。按分数加权随机——分数高的更可能被选中,但不是必然——能让行为多一点变化。

为什么设计师喜欢它

IAUS 的产品介绍里强调的是工程上的好处:

  • 设计师在工具里调曲线、打包行为,不必改代码分支;按它的说法,ArenaNet 在《激战 2:荆棘之心》的演示里,大约 7 分钟就拼出了一个独特的 NPC 行为包;
  • 行为模块可以组合到任意 NPC 上;
  • 能和影响力图 (Influence Map) 这类空间知识层配合使用。

Halo 2 的警告:浮点竞争排不出硬优先级

Damian Isla 在讲 Halo 2 的 AI 时,专门批评过「子行为用连续的浮点欲望互相竞争」的做法:选项一多,就很难排出「除非玩家在车里,否则先战斗」这种硬优先级。Halo 2 最后改用二元的相关性加优先级列表。

这对效用 AI 是一个提醒:它擅长渐变的权衡(饿了多久 × 食物多香),却不擅长绝对的抢占。设计上需要绝对抢占时,应该在效用外面再包一层优先级,或者把硬规则做成高分门槛、独立的决策层,而不是指望调几个浮点数就能表达全部逻辑。

在游戏里怎么用

取舍与常见的坑

小结与练习

  • 效用 AI 给每个候选行动打「有多想做」的分,选最高分或按分数抽签;
  • 一个考量 = 归一化的输入 + 响应曲线,曲线的形状就是 NPC 的性格;
  • 多项考量常相乘:任何一项为 0 就一票否决,但项数一多分数会塌缩;
  • 它擅长渐变的权衡,不擅长硬优先级,需要时在外面再包一层。

练习:在分数地图里加第三个行动「去睡觉」,它的分数只看一个新的考量,比如用 1.0 - s.x 表示「越不饿越想睡」。给它一种新颜色,看看状态空间被三个行动怎么瓜分。

  1. Infinite Axis Utility System / Utility AI(笔记) Dave Mark / Intrinsic Algorithm IAUS
  2. www.gameai.com/iaus.php
  3. Handling Complexity in the Halo 2 AI(笔记) Isla(对纯浮点竞争的可扩展性质疑)

相关页面

游戏开发游戏 AI 决策管线Game AI Sense-Decide-Act Pipeline游戏 AI(与生成式 DNN 不同)通常按 感知 → 决策 → 执行 分层:底层是寻路、感知与记忆;中层是角色控制(对接动画/物理);上层是目标与决策(行为树、效用、规划等)。GEA 强调「perception is reality」——玩家看不见的内部聪明不算数。游戏开发决策架构选型:FSM、行为树与效用Choosing FSM, Behavior Tree & Utility AIFSM 强在少量离散状态与硬转换;行为树强在优先级回退与模块化任务;效用强在连续权衡与数据驱动调参;GOAP 强在声明式动作组合与重规划。实作常分层混用,而不是四选一宗教战。游戏开发行为树原理Behavior Tree Fundamentals行为树 (Behavior Tree, BT) 用层级节点控制决策流:叶子做动作或条件,Sequence / Selector 等组合节点决定子节点如何推进;节点返回 Success / Failure / Running,从而自然跨多帧。相对 FSM,BT 更易用优先级回退表达「失败了还有 Plan B」。游戏开发目标导向行动规划 GOAPGoal-Oriented Action PlanningGOAP 把「如何满足目标」交给运行时规划:行动带前提与效果,用 A 在世界状态空间搜索最低成本动作序列。F.E.A.R. 用极薄 FSM(Goto/Animate)执行计划,换来目标与行动解耦、行为可叠加、失败可重规划。游戏开发黑板架构Blackboard Architecture黑板 (Blackboard) 是多模块共享的「工作记忆」:感知写入、决策读取、任务更新键值,避免各子系统用私有状态互相耦合。行为树的 Data Context、GOAP 的 Working Memory、Halo 的 Props,都是同一思想的变体。游戏开发 · UE5行为树与 StateTreeBehavior Tree & StateTreeUE 的 AI 决策有两套工具:行为树 (Behavior Tree) + 黑板 (Blackboard) 是事件驱动的,条件写成装饰器 (Decorator),用 Simple Parallel、Services 和 Observer Aborts 代替传统并行节点;StateTree 是结合了行为树选择器和状态机转换的通用分层状态机,只在转换触发时按需选择状态,激活路径上的任务并发运行,通过属性绑定传递数据,也是 Mass 的行为层。优先级抢占多的战斗 AI 适合行为树,状态显式、分层或大规模的场景适合 StateTree。

输入关键词开始搜索

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