周末傍晚的人民公园,喇叭一响,三四十位阿姨自觉站上粉笔线画好的格子。领舞王阿姨站在最前面,抬手、转身、迈步;身后的队伍像被一根看不见的线牵着,整齐划一。仔细看,每个人都有自己的小本子——什么时候抬腿、什么时候转圈,像一本微型“舞步手册”;但也正因为每个人都认真跳自己的舞步、又不碰着别人,整支队伍才呈现出行云流水的队形。这就是多NPC协作的缩影:个体有自己的行为规则,群体则涌现出整体秩序。 游戏里的 NPC 也是这样:门卫有巡逻小本子,商贩有吆喝小本子,村民有闲聊小本子;这些“小本子”就是行为树。当玩家闯入,守卫、猎人、村民像阿姨们听到切换曲目一样,纷纷翻开应急那一页,互相配合、各司其职。这篇文章会用一套贯穿始终的生活化比喻,带你搞懂多 NPC 协作的原理、算法和工程优化,让你从“只会写一个 NPC”进阶为“能让一座村庄活起来”。

岗位关联

学会多 NPC 协作与群体行为,等于掌握了开放世界、RTS、模拟经营等项目的“第二层骨架”——让 NPC 从单兵木偶变成有呼吸的社区,这是 AI 游戏工程师的核心竞争力。

目录
  1. 可视化演示
  2. 协作基本功:先学会同频
  3. 群体算法:三句口诀让舞队活起来
  4. 通信机制:舞队暗号与公告栏
  5. 协同树设计:编舞师的分工艺术
  6. 性能优化:人再多也流畅的秘诀
  7. 动态管理:有人进场,有人退场
  8. 调优实战:排练中的意外与补救
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示Boids模型三大规则(分离/对齐/凝聚)的鸟群仿真、行为树多NPC任务分配执行及群体涌现行为模式(环阵/V型/集群)。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Boids模型三大规则 分离S: 避免邻居过近 对齐A: 匹配邻居方向 v = Sep + Ali + Coh 凝聚C: 向邻居中心靠拢

协作基本功:先学会同频

广场舞团新招了二十位新人,王阿姨第一件事不是教动作,而是教“踩点”——音乐的鼓点在哪里,脚步就应该落在哪里。哪怕你手里有一本完美的舞步手册,如果踩点不对,整个队伍照样乱作一团。多 NPC 协作的第一课,同样不是炫酷的智能,而是“同步感知”:所有 NPC 先得看见同一件事、听见同一条消息,才能谈得上配合。

在游戏里,多个 NPC 的协作可以这样理解:每个 NPC 各自运行一棵行为树做个体决策,但彼此共享同一个世界状态。比如村庄里的守卫、猎户、村民三个人,各自的决策逻辑完全不同,可当共享状态里出现“狼来了”,他们会一致地切换到应对状态。这种“个体独立、整体协同”的特征,就是群体行为的基本盘,也是 AI 驱动的游戏玩法里最值钱的部分。

要让行为树支持协作,最常用的扩展是“黑板”(Blackboard)机制。黑板可以理解为全局共享的内存区域:NPC 把自己观察到的结果(看到敌人、听到声响、需要帮助)写上去,别的 NPC 通过读黑板获取信息,再决定自己在行为树的哪个分支上继续走。行为树也可以增加“黑板查询”这种条件节点:巡逻节点先读黑板,读不到异常就继续巡逻,读到异常就切到战斗节点。

你看,舞者跳得好不好,一半靠自己的舞步手册(行为树),另一半靠那块写着“曲目、站位、领舞指令”的公告栏(黑板)。学会了同频,后面的队形变换、紧急避雨、人员流动,才有了讨论的基础。

S={s1,s2,…,sn}S = \{ s_1, s_2, \ldots, s_n \}
黑板里存了 n 条共享信息,每条都有一个键名和一个值;多 NPC 协作的第一步,就是让所有角色都能读写这块“广场公告栏”。
  1. S 表示黑板共享状态的集合像广场角落的公告栏,所有 NPC 都能看、也能往上贴。
    S={s1,s2,…,sn}S = \{ s_1, s_2, \ldots, s_n \}
  2. s_i 表示第 i 条共享信息例如“当前曲目《万疆》”“第三排右侧缺一人”,每条都有一个唯一键名。
    si=(keyi,valuei)s_i = (\text{key}_i, \text{value}_i)
  3. n 表示共享条目总数条目太多,黑板就读不过来了,因此游戏里要控制数量,只广播真正重要的状态。
    n=∣S∣n = |S|

群体算法:三句口诀让舞队活起来

扇子舞开跳前,王阿姨只给新人三句口诀:“别踩脚,跟前面,别掉队。”没有复杂的口令,队伍却总是自动排成漂亮的扇面。一九八六年,计算机图形学家 Craig Reynolds 观察鸟群后提出了同样的三句话,叫 Boids 模型。这三条规则只依赖邻居的局部信息,却能让整个群体“涌现”出队形。

在游戏 AI 里,我们把每个 NPC 当成一个“舞者”,它不需要知道全世界的状态,只需要扫一眼周围的邻居:离我太近的,我推开一点(分离);大家朝哪边移动,我调整方向(对齐);周围人都往一个目标走,我别被落下(聚合)。三者合起来,就产生了自然的群体运动,比如村民结队围观、羊群躲避野兽、士兵包抄敌人。

Boids 的数学模型其实就是一个加速度合成公式:最终转向力等于三股力量加权相加。

实现时最常踩的坑是“找邻居”的复杂度:30 个 NPC 两两检查只需 900 次,3000 个 NPC 就是 900 万次。优化方案是空间哈希——把广场画成格子,只检查自己和相邻格子里的对象,平均复杂度从 O(n²) 降到接近 O(n)。三句口诀加一个公告栏,上百个 NPC 就能像一群麻雀一样聚散自如;而真正的任务配合,还得靠下一节的“暗号与黑板”。

a⃗i(t)=α⋅a⃗sep+β⋅a⃗ali+γ⋅a⃗coh\vec{a}_i(t) = \alpha \cdot \vec{a}_{\text{sep}} + \beta \cdot \vec{a}_{\text{ali}} + \gamma \cdot \vec{a}_{\text{coh}}
每个 NPC 每帧先算出“推开力、对齐力、聚合力”三个小箭头,按权重加到一起,就成了它下一步速度的调整方向。
  1. 第 i 个 NPC 的合成转向加速度它是三股力量相加的结果,类比舞者同时收到的“推开”“跟紧”“对齐”三个念头。
    a⃗i(t)\vec{a}_i(t)
  2. 分离项当邻居进入安全距离,就产生背向邻居的推力,防止互相踩脚。
    a⃗sep=∑jp⃗i−p⃗j∥p⃗i−p⃗j∥\vec{a}_{\text{sep}} = \sum_j \frac{\vec{p}_i - \vec{p}_j}{\|\vec{p}_i - \vec{p}_j\|}
  3. 对齐项用邻居的平均速度减去自己的速度,让舞步节奏逐渐一致。
    a⃗ali=v⃗avg−v⃗i\vec{a}_{\text{ali}} = \vec{v}_{\text{avg}} - \vec{v}_i
  4. 聚合项把邻居质心的位置与自己位置做差,方向指向队伍中心。
    a⃗coh=c⃗neighbors−p⃗i\vec{a}_{\text{coh}} = \vec{c}_{\text{neighbors}} - \vec{p}_i
  5. 权重系数α、β、γ 控制三股力量的强弱,想让 NPC 更谨慎就调大分离权重。
    α, β, γ\alpha, \ \beta, \ \gamma

通信机制:舞队暗号与公告栏

换队形时,王阿姨只打一个手势:左手举起来画个圈,前排阿姨立刻蹲下,后排插空上。这个手势就是“消息传递”:一个点对点的指令,只发给附近几个人。音响旁那张粉笔板则是“共享黑板”:谁都能瞄一眼,上面写着今天的三首曲目和结束时间。

多 NPC 协作的通信,通常分两类。第一类是消息传递(Message Passing):一个 NPC 主动向另一个 NPC 或一组 NPC 发送事件,类似“你左边有敌人”“请帮我治疗”。第二类是黑板/共享状态:大家读写同一块数据,各取所需,类似公告栏。两者各有优劣:消息灵活、有指向性,但多人同时发送会拥堵;黑板简单、扩展性好,但人人都能写,容易“脏”。

工程上,通信成本必须有预算。假设有 N 个 NPC,某个 NPC 每秒发出 m 条消息,总通信量就是所有 NPC 消息量的总和,它必须小于等于引擎的通信预算 B。

舞队的暗号要精炼,游戏里的消息也要精炼。能上公告栏的,就别挨个咬耳朵;能合并成广播的,就别拆成一百句耳语。通信这件事,省着用,才够用。

Ctotal=∑i=1Nmi≤BC_{\text{total}} = \sum_{i=1}^{N} m_i \leq B
一百位阿姨同时喊“向左”,谁也听不见;不如由领舞统一喊一声,再让前排依次传话。总消息量超过预算,就会掉帧或丢消息。
  1. C_total 表示总通信量所有 NPC 每秒发出的消息条数之和。
    Ctotal=∑i=1NmiC_{\text{total}} = \sum_{i=1}^{N} m_i
  2. m_i 表示单个 NPC 的消息量每个 NPC 每秒钟发出的消息条数。
    mim_i
  3. N 表示 NPC 总数参与通信的角色数量。
    NN
  4. B 表示通信预算上限引擎每帧能处理的消息量是固定的,超过就要丢消息或卡顿。
    ∑i=1Nmi≤B\sum_{i=1}^{N} m_i \leq B

协同树设计:编舞师的分工艺术

一首《万疆》跳得好,靠的不是人人一样,而是“分工”:领舞在最前保证动作标准,左翼右翼负责队形对称,后勤组负责搬道具、调音响。王阿姨会给每组发不同的舞步册子,但所有人册子的第一页都写着同一件事:你的站位、你的上场顺序、你听谁指挥。

行为树的协同设计,就是在个体行为树之上叠加“角色分工”和“任务分配”。每个 NPC 不再只有一棵全知全能的树,而是根据身份拥有自己的子行为树:守卫的树里有巡逻、警戒、战斗,治疗师的树里有跟随、补给、治疗。要让这些树各司其职又不错位,就得有一个“分配器”来决定谁去做哪件事。

任务分配最常用的方法是评分制:每个候选任务按多个维度打分,综合分最高的任务优先执行。

多个 NPC 可能同时竞标同一个任务,所以还要有仲裁:先做完的写黑板“已认领”,其他人自动放弃,避免重复劳动。编舞师把同一支舞拆成领舞、侧翼、后勤,各跳各的又拼成整体;游戏里的 AI 策划也把同一个目标拆成多棵行为树,用评分和黑板让它们自然分工。单看每个人都只做一小块,合起来却是一场完整的演出。

Ri=∑k=1mwk⋅scorek(i)R_i = \sum_{k=1}^{m} w_k \cdot \text{score}_k(i)
R 是 NPC 给第 i 个任务的评分,分数由距离、紧急度、自身能力等多个维度加权求和。抢任务的 NPC 越多,评分机制就越重要。
  1. R_i 表示综合评分NPC 对第 i 个任务的总评分,越高越优先执行。
    RiR_i
  2. score_k(i) 表示第 k 维得分例如“距离目标多远”“任务多紧急”“我能不能打”。
    scorek(i)\text{score}_k(i)
  3. w_k 表示第 k 维权重控制距离、紧急度、能力哪个更重要,由策划调参。
    wkw_k
  4. m 表示评分维度总数一共从几个角度打分。
    mm

性能优化:人再多也流畅的秘诀

广场舞跳得最热闹时,广场上聚了上百人。有人问王阿姨:这么多人,怎么保证不卡顿、不乱套?阿姨笑了:前排主力认真学动作,后排外围跟着节奏随缘摆动;人多也不互相干扰。她其实无意中用了游戏引擎最常用的一招——LOD(Level of Detail,细节分层)。

游戏 AI 的“卡顿”往往来自行为树更新:每个 NPC 都要遍历行为树节点,如果上千个 NPC 每帧都更新,CPU 就爆了。优化的核心思路是“按需降频”:离玩家近的 NPC 需要精细决策,远处 NPC 哪怕一秒只更新两三次,玩家也几乎察觉不到。

LOD 调度的经典公式是按距离缩放更新周期:

配合空间分区(把广场划成格子,一帧只更新其中若干格),可以进一步摊平峰值。渲染侧还有一招叫“实例化”(Instancing):把相同外观的 NPC 合并成一次绘制,GPU 批量处理。后排阿姨“随缘摆动”不是偷懒,而是把算力省给前排主力;游戏里的“近精远疏”是同一个道理——玩家看着的地方绝不糊弄,看不见的地方合理降频。

Ti=Tbase⋅didrefT_i = T_{\text{base}} \cdot \frac{d_i}{d_{\text{ref}}}
NPC 离玩家越远,行为树更新周期越长、更新频率越低。玩家注意不到远处的粗糙,却能明显感受到流畅不掉帧。
  1. T_base 表示基准更新周期理想情况下每个 NPC 的基础更新周期,比如每帧一次。
    TbaseT_{\text{base}}
  2. d_i 表示 NPC 与玩家的距离距离越远,感知优先级越低。
    did_i
  3. d_ref 表示参考距离判定“远/近”的阈值,由场景规模决定。
    drefd_{\text{ref}}
  4. T_i 表示最终更新周期距离越远周期越大,更新频率越低。
    Ti=Tbase⋅didrefT_i = T_{\text{base}} \cdot \frac{d_i}{d_{\text{ref}}}

动态管理:有人进场,有人退场

八点半跳到九点半,阿姨们陆续退场,也有人中途加入。广场的容量不是无限的——太挤了,动作伸不开,还容易互相踩脚。王阿姨的做法是:新来的先去后排观察,等前排有人走了再补位。这一套“进出管理”,对应游戏里的动态群体管理。

动态群体管理要回答三个问题:什么时候生成新 NPC?什么时候移除旧 NPC?区域太挤怎么办?如果玩家走进集市,系统需要临时刷出更多摊贩和顾客;玩家离开后,这些 NPC 该“下班”就下班。密度过高时,新增 NPC 会被推迟,已有 NPC 被引导向低密度区域移动。

密度控制的指标定义为单位面积 NPC 数:

突发事件(比如下雨、怪物警报)会把整组 NPC 的状态机切换到应急状态,原有分布全部作废,重新按“避雨点/躲避点”聚集。广场舞的热闹来自恰到好处的人数:走一个、补一个,挤了就散开,下雨就收摊。游戏里的 NPC 也是如此——让世界永远热闹,但永远不拥塞。

ρ=NnpcAzone≤ρmax⁡\rho = \frac{N_{\text{npc}}}{A_{\text{zone}}} \leq \rho_{\max}
每个区域都有一个密度上限。超过上限就触发“驱散”压力场,把 NPC 推向低密度区域,同时暂停生成新 NPC。
  1. ρ 表示区域密度区域内每单位面积的 NPC 数量。
    ρ\rho
  2. N_npc 表示区域内 NPC 数量该区域当前有多少角色。
    NnpcN_{\text{npc}}
  3. A_zone 表示区域面积以平方米或游戏单位计。
    AzoneA_{\text{zone}}
  4. ρ_max 表示密度上限超出后触发驱散或停止生成。
    ρ≤ρmax⁡\rho \leq \rho_{\max}

调优实战:排练中的意外与补救

那天下雨排练,两个阿姨同时让路:一个往左闪,另一个也往左闪;一个往右让,另一个也往右让。三步之后,两个人堵在原地。王阿姨喊了一句:“你站那别动,她先走。”一句话破解僵局——这就是游戏里的死锁检测与超时回退。

多 NPC 协作最常见的 Bug 是“互相等待”:两个 NPC 同时想扶起同一把椅子,或者同时要给同一个目标让路,行为树都没有“退让”分支,结果一直僵在那里。工程上常用两个手段解决:一是给协作动作加超时,任务在规定时间内没完成就自动放弃重试;二是“优先级化”,让低优先级 NPC 让路给高优先级 NPC,避免对称僵局。

假设 NPC 执行一次协作尝试的成功概率是 p,失败后允许重试 k 次,那么“接下来至少成功一次”的概率为:

实战案例:村庄防守中,三个守卫同时响应“狼来了”,通过黑板协调分别承担“近战牵制”“远程射击”“通知村民”三个任务。近战守卫倒下时,远程守卫自动补位。这就是行为树加黑板加群体控制的分层配合。王阿姨一句“你站那别动”,其实是给系统加了超时和优先级;游戏里的死锁也是这样解开的。

Success=1−(1−p)k\text{Success} = 1 - (1 - p)^k
与其追求单次协作的完美,不如允许试错和重试。连续失败 k 次的概率是 (1-p)^k,用 1 减去它就是至少成功一次的概率。
  1. p 表示单次成功率NPC 执行一次协作动作成功的概率。
    pp
  2. k 表示最大重试次数失败后最多再试几次。
    kk
  3. (1-p)^k 表示连续失败概率每次都失败的情况全部相乘。
    (1−p)k(1-p)^k
  4. Success 表示至少成功一次的概率用整体概率空间减去全部失败的情况。
    Success=1−(1−p)k\text{Success} = 1 - (1-p)^k

AI 应用场景

开放世界村庄协防

阶段:多 Agent 决策与协作

解决的问题:传统沙盒村庄里,每个 NPC 只按各自行为树行动,怪物入侵时村民只会原地逃跑甚至卡在墙角。AI 需要让守卫、猎人、村民共享敌情并分工应对。

具体案例:当玩家引来狼群,守卫发现后立刻写黑板“狼群入侵”并播放警戒事件。村民的行为树检测到黑板状态,从“闲逛”切换为“向屋子撤退”;猎户切换为“远程攻击”,守卫切换为“近战牵制”。若守卫生命值过低,猎户通过消息请求支援,整场防御像一支分工明确的队伍。

MMORPG 主城人流控制

阶段:运行期动态管理

解决的问题:大型多人游戏的主城有大量玩家和 NPC,高峰期城门拥堵、路被堵死,玩家体验直线下降。

具体案例:系统为城区划分网格区域,实时计算 ρ = N/A。当城门口密度超过上限,系统暂停新商贩 NPC 生成,并给行商 NPC 施加“向集市偏移”的驱散力;同时用 LOD 降低远离玩家 NPC 的更新频率,保证高峰期帧率平稳。

RTS 单位编队协同

阶段:群体行为控制

解决的问题:传统编队用硬编码阵型,部队拐弯时互相卡位、队形崩散,玩家操作成本极高。

具体案例:《星际争霸》式部队用三规则 Boids 平滑编队;单位遇到敌人时,医疗兵通过黑板读取受伤单位列表,用评分函数优先治疗血量最低的单位。既有队形美感,又有行为树级别的战术智能。

Python 演示

python67 行
import mathimport random  class Boid:    def __init__(self, x, y):        self.x = x        self.y = y        self.vx = random.uniform(-1, 1)        self.vy = random.uniform(-1, 1)  class Flock:    def __init__(self, n, w, h):        self.w = w        self.h = h        self.boids = [Boid(random.uniform(0, w), random.uniform(0, h)) for _ in range(n)]        self.sep = 24.0        self.vis = 60.0        self.max_sp = 2.0     def update(self):        new_list = []        for b in self.boids:            sx = sy = cx = cy = ax = ay = 0.0            cnt = 0            for o in self.boids:                dx = o.x - b.x                dy = o.y - b.y                d2 = dx * dx + dy * dy                if d2 < self.sep * self.sep:                    sx -= dx                    sy -= dy                if d2 < self.vis * self.vis:                    cx += o.x                    cy += o.y                    ax += o.vx                    ay += o.vy                    cnt += 1            if cnt:                cx = cx / cnt - b.x                cy = cy / cnt - b.y                ax = ax / cnt                ay = ay / cnt            b.vx += sx * 0.30 + cx * 0.05 + (ax - b.vx) * 0.10            b.vy += sy * 0.30 + cy * 0.05 + (ay - b.vy) * 0.10            sp = math.hypot(b.vx, b.vy)            if sp > self.max_sp:                b.vx = b.vx / sp * self.max_sp                b.vy = b.vy / sp * self.max_sp            b.x = (b.x + b.vx) % self.w            b.y = (b.y + b.vy) % self.h            new_list.append(b)        self.boids = new_list  def main():    flock = Flock(30, 640, 480)    for frame in range(20):        flock.update()        if frame % 5 == 0:            first = flock.boids[0]            print("frame", frame, "dancer0:", round(first.x), round(first.y))  if __name__ == "__main__":    main()
目的:用约 60 行 Python 实现一个迷你 Boids 群体模拟,30 个“舞者”在 640×480 的广场上遵循分离、聚合、对齐三规则更新位置,每 5 帧打印首位舞者坐标,验证群体运动不散开也不重叠。
  1. self.boids = [Boid(random.uniform(0, w), random.uniform(0, h)) for _ in range(n)]第 0 行生成 n 个舞者的初始位置,对应广场上的随机站位。
  2. if d2 < self.sep * self.sep:距离平方小于安全距离时触发分离,防止舞者互相踩脚。
  3. if d2 < self.vis * self.vis:在视野范围内才统计邻居,这是“只看旁边的人”的局部感知。
  4. cx = cx / cnt - b.x算出邻居质心后减自己位置,得到朝队伍中心拉的聚合方向。
  5. b.vx += sx * 0.30 + cx * 0.05 + (ax - b.vx) * 0.10把三股力量加权加到速度上,对应 α、β、γ 权重。
  6. if sp > self.max_sp:限制最大速度,避免舞者越跳越快失去控制。
  7. b.x = (b.x + b.vx) % self.w超出边界就从另一侧回来,模拟一个循环的广场空间。

常见误区

误区行为树一棵树管一个 NPC,多 NPC 协作肯定要用状态机或者专门的 Agent 框架,行为树做不了大队 AI。

正解行为树不代表“孤岛”。加上黑板、消息和群体算法后,多棵树完全可以共享状态、分工协作。绝大多数商业游戏仍然以行为树为骨架,用共享黑板和分区调度支撑成群 NPC。编排队伍是同一支舞拆成多本舞步册,而不是扔掉册子。

为何常见:教科书常把行为树单独讲,容易让人误以为它只是单 Agent 决策工具。

误区我把分离、聚合、对齐三个力加上,人群就会像电影里一样聪明,能自己排队打饭、跑路避险。

正解Boids 只解决“运动层”的协调,不解决“任务层”的分工。NPC 还需要行为树决定“我要去买菜还是站岗”,Boids 只决定“我和旁边人保持多少距离、跟谁走”。两者缺一不可。

为何常见:论文动图太漂亮,很多新手误以为 Boids 是全功能群体 AI。

误区NPC 更新得越频繁越真实,掉帧是因为引擎不好,和更新逻辑没关系。

正解人眼对远处小角色的动作细节并不敏感。按 LOD 分层降频、空间分区调度,把算力留给玩家眼前的 NPC,反而会减少卡顿,提升整体真实感。后排阿姨随缘摆动,观众根本看不出区别。

为何常见:本地测试时 NPC 数量少,看不出差异;上千 NPC 同屏压测时才暴露问题。

误区开放世界就得人多,越多越热闹,性能不够就加服务器、换显卡。

正解密度过高会带来拥堵、路线反复让行、消息风暴等问题。优秀的做法是动态密度控制:热点区域维持合理上限,冷区用“氛围 NPC”补充,玩家离开后及时回收。

为何常见:被“大世界”宣传误导,忽略了并发密度对体感和性能的双重影响。

面试题

Q1. 行为树在多 NPC 协作中主要解决什么问题?

单选题 · easy

  • A.绘制 NPC 的模型外观
  • B.决定单个 NPC 下一步行为← 正确答案
  • C.管理场景的物理碰撞
  • D.计算网络的带宽开销

答案:B。行为树是“个体决策”的工具,根据条件选择动作。A 是渲染,C 是物理,D 是网络层;多 NPC 协作要在此基础上叠加黑板和群体算法。

Q2. “黑板”在多个 NPC 协作中扮演的角色最接近哪个比喻?

单选题 · easy

  • A.写给自己看的日记本
  • B.广场边的公共公告栏← 正确答案
  • C.厨房角落里的菜刀
  • D.大楼里的透明电梯

答案:B。黑板是共享状态区域,所有 NPC 都能读写,正如公告栏人人可看可贴。它让“王阿姨换曲目”这类信息一次广播、全员感知。

Q3. 群体行为算法中,“分离”规则的作用是?

单选题 · mid

  • A.让 NPC 朝目标点聚集
  • B.让过近的邻居保持距离← 正确答案
  • C.让 NPC 跟随领舞者朝向
  • D.让 NPC 随机移动增加变化

答案:B。分离在检测到距离过近时产生推开力,防止重叠踩脚。聚集是 A,对齐是 C,随机游走是 D。

Q4. 用 O(n²) 复杂度找邻居,3000 个 NPC 需要循环约多少次?

单选题 · mid

  • A.仅 3000 次
  • B.约 900 万次← 正确答案
  • C.约 9 万次
  • D.约 30 次

答案:B。n² = 3000 × 3000 = 900 万次。优化用空间哈希网格分区可降到每 NPC 只检查相邻格子,平均接近一次扫描。

Q5. 多个 NPC 竞标同一个任务时,常用哪种机制避免重复执行?

单选题 · mid

  • A.随机禁止 NPC 行动
  • B.先认领者写黑板← 正确答案
  • C.让任务永不刷新
  • D.只用一个 NPC 全干

答案:B。某 NPC 在黑板上写“已认领”,其他 NPC 读到后自动放弃同一任务,避免多人重复劳动。这是黑板共享机制的典型应用。

Q6. 以下哪些属于多 NPC 协作的常用通信手段?

多选题 · mid

  • A.点对点的消息传递← 正确答案
  • B.黑板共享状态数据← 正确答案
  • C.修改角色贴图材质
  • D.全局广播事件总线← 正确答案

答案:ABD。消息传递、黑板共享和全局事件总线是三种典型通信方式。修改贴图材质属于渲染层操作,不承载 AI 通信。

Q7. 采用 LOD 调度时,距离玩家越远的 NPC 更新周期应该?

单选题 · hard

  • A.越长,更新频率越低← 正确答案
  • B.越短,更新频率越高
  • C.与距离完全没有关系
  • D.直接停止且永不恢复

答案:A。远处 NPC 不需要高频决策,拉长周期即可省算力;但不能“永不恢复”,玩家靠近时要平滑回升。

Q8. 当某区域 NPC 密度超过 ρ_max 时,合理的处理是?

单选题 · hard

  • A.继续生成更多 NPC 填满
  • B.暂停新增并向外驱散← 正确答案
  • C.直接关闭整片区域
  • D.将所有 NPC 移出地图

答案:B。密度超限会带来拥堵和性能下降。正确做法是暂停新增、把已有 NPC 导出区域;C 破坏玩法,D 反应过度。

Q9. 下面哪些场景可能导致 NPC 协作“死锁”?

多选题 · hard

  • A.两个 NPC 互让卡在原地← 正确答案
  • B.多人占用资源不释放← 正确答案
  • C.行为树分支实在过少
  • D.任务彼此依赖无超时← 正确答案

答案:ABD。死锁的本质是“互相等待、无人让步”。分支太少只影响决策丰富度,不直接造成互锁。

Q10. 关于“行为树 + 黑板 + 群体算法”的分层协作,正确的理解是?

多选题 · extreme

  • A.个体决策归行为树← 正确答案
  • B.黑板共享状态信息← 正确答案
  • C.群体算法管运动层← 正确答案
  • D.群体算法可替代行为树

答案:ABC。三者的分工是个体决策、共享信息、运动协调。D 说反了,群体算法只解决运动层,无法替代行为树的任务决策。