效用 AI:给每个念头打个分
Utility AI
饿了多久,食物多香
一个 NPC 该不该去吃饭?最直接的写法是一串 if:「饥饿大于 0.7 就去吃」。可是食物在地图另一头呢?刚好在脚边、只是有点饿呢?条件越加越多,规则互相打架,NPC 还容易卡在某个行为里出不来。
效用 AI (Utility AI) 换了个问法:不问「满足哪条规则」,而问「每个选项有多想做」。给吃饭、巡逻、睡觉……每个候选行动各算一个分数,选最高的那个。饿了多久、食物多远、食物多香,都变成这个分数的一部分,一起权衡。
Dave Mark 的无限轴效用系统 (Infinite Axis Utility System, IAUS) 把这个思路做成了一套纯数据驱动的架构。按它的说法,相比硬编码的 if/then,这样的 AI 没那么脆,也更少卡在不该停留的行为里。
一次决策的四步
对每一个候选行动:
- 采集输入:血量、距离、弹药……通常先归一化到 ;
- 过响应曲线:每个输入经过一条响应曲线 (Response Curve),变成这一项的分数——这一步叫一个考量 (Consideration);
- 合成:把几项考量的分数合成一个总分,业界常见的做法是相乘;
- 比较:在所有候选行动里取最高分,或者按分数加权随机,免得 NPC 显得太机械。
flowchart LR I["原始输入:饥饿、距离……"] --> N["归一化到 0~1"] N --> C["响应曲线(考量)"] C --> M["多项相乘,得到总分"] M --> P["所有候选行动比分"] P --> A["选中的行动"]
响应曲线:把「在乎多少」画出来
同样是「饥饿 0.5」,有的 NPC 已经坐立不安,有的还无所谓。差别就在响应曲线:横轴是归一化的输入,纵轴是这一项给出的分数。下面这些是常见的几种形状,点一点预设,再拖动 k:
直接改上面的表达式就能重画:x 是 0 到 1 的横坐标,可以用 sin、pow、smoothstep 等函数。
曲线的形状就是设计师手里的旋钮:要调的是 NPC 的「性格」,而不是一串条件分支。这些曲线只是常见形状的举例;IAUS 这类系统具体提供哪些曲线、参数怎么取,以产品文档为准,这里不展开。
动手:两个考量,一张分数地图
现在让 NPC 在「去吃饭」和「继续巡逻」之间选。吃饭的分数由两个考量相乘:饥饿过一条 pow(h, 2.0) 曲线,距离过一条 1.0 - d 曲线;巡逻是一条恒定的基线 0.3。
下面画布的左边是整个状态空间:横轴是饥饿(左边不饿,右边饿极了),纵轴是到食物的距离(下面就在脚边,上面很远)。黄色区域里吃饭赢,越黄分数越高;蓝灰色区域里巡逻赢;橙线是两者打平的分界。白圈是鼠标所在的状态,右边两根柱子是这个状态下两个行动的分数,带橙边的是赢家。
也可以直接改右边的代码:停手约 0.3 秒会自动重新运行;改坏了点「还原代码」。
相乘的两个性质
上面最后一条提示暴露了相乘的第一个性质:一票否决。只要有一项考量是 0,总分就是 0。「一点也不饿」本来就应该否决「去吃饭」,相加却会让另一项把分数撑起来。
第二个性质是分数塌缩:每多一项考量,就多乘一个不大于 1 的数。即使每一项都给出不错的 0.9,乘起来也会越来越小:
| 考量的项数 | 1 | 2 | 3 | 5 | 10 |
|---|---|---|---|---|---|
| 每项都是 0.9 时的总分 | 0.90 | 0.81 | 0.73 | 0.59 | 0.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 表示「越不饿越想睡」。给它一种新颜色,看看状态空间被三个行动怎么瓜分。
效用 AI (Utility AI) 为每个候选行动/决策计算「有多想做」的分数,选最高分(或按分抽样)。Dave Mark 的 IAUS 将其做成纯数据驱动架构:考量 (Consideration) + 响应曲线打分,行为模块可拼装;相对硬编码 if/then,更不易卡死在单一行为。
核心循环
对每个候选决策:
- 采集世界输入(血量、距离、弹药……),常归一化到 ;
- 经 response curve 映射为该项效用;
- 多项考量合成(行业常见为相乘,并可加维数补偿);
- 在全部候选中取最高分(或加权随机防机械感)。
(IAUS 产品描述与同族实现语汇:Infinite Axis Utility System / Utility AI(笔记))
flowchart LR I["原始输入"] --> N["归一化"] N --> C["响应曲线 Consideration"] C --> M["多分项合成"] M --> P["全体决策比分"] P --> A["选中行动"]
IAUS 声称的工程收益
- 设计师用工具调曲线与打包行为,不必改代码分支;ArenaNet(GW2 HoT)演示可在约 7 分钟拼出独特 NPC 行为包。(Infinite Axis Utility System / Utility AI(笔记))
- 所有启用行为同时参与比分,新增行为是「加进集合」而非改巨型 if 链。
- 与 Influence Map 等空间知识层可组合(同页产品矩阵)。
与「浮点欲望竞争」的警示
Isla 写 Halo 2 时批评:子行为用连续浮点欲望竞争,选项一多就难排出「除非玩家在车里否则先战斗」这类硬优先级;他们改为 二元相关性 + prioritized-list。(Handling Complexity in the Halo 2 AI(笔记))
启示:效用擅长 渐变权衡(饿了多久 × 食物多香);若设计需要绝对抢占,应在效用外包一层优先级,或把「硬规则」做成高分门槛 / 独立决策层,而不是幻想调几个 float 能表达全部逻辑。
取舍
| 适合 | 慎用 |
|---|---|
| 选项多、情境连续、要「看起来在权衡」 | 必须严格优先级抢占的战斗状态 |
| 设计师要数据驱动调参 | 需要保证最优动作序列(→ GOAP/规划) |
| 避免「卡在行为出不来」 | 调试分数爆炸时缺可视化工具 |
可与 BT 混用:BT 负责阶段结构,叶子用效用选具体战术;或效用选高层意图,BT/状态机执行。见 决策架构选型:FSM、行为树与效用。
出处
- Infinite Axis Utility System / Utility AI(笔记) Dave Mark / Intrinsic Algorithm IAUS
- www.gameai.com/iaus.php
- Handling Complexity in the Halo 2 AI(笔记) Isla(对纯浮点竞争的可扩展性质疑)