战术分配系统

GAIS_Cover
YuewuDev

作者

YuewuDev

创建时间: 2026年10月12日更新时间: 2026年10月12日

目录

战术分配系统

Tactical Assignment 用来解决“当前局势下该由谁去做什么、面对哪个目标”的匹配问题。它从一组独立的候选单位与目标 Source 汇集本轮输入,用 IntentTag 请求战术语义,由每个 Unit 解析出自己实际能执行的 Command,随后完成过滤、命令校验、评分、容量检查与提交。

玩家、UI 或脚本已经知道确切 Unit、Command 和目标时,直接使用 CommandSystem 更简单。Tactical Assignment 主要处理候选执行者、候选目标或目标容量会随战场变化的自动决策,例如治疗者选择友军、岗位单位占据防御点、或多名单位分摊攻击目标。

这个系统解决什么问题

命令系统回答“让这个单位做什么”;战术分配回答“这一组单位分别该做什么”。当 Unit、Command 和目标都已经明确时,直接发 Command 最简单;当只有“这支小队现在要接敌、守点、支援”这类群体战术目标时,逐个手写命令会失去角色差异、目标容量和动态重配能力。

战术分配把群体任务拆成可执行的个体 assignment:近战单位接近敌人、远程单位攻击另一目标、治疗者支援受伤友军、其余成员坚守阵地。它为每个被选 Unit 解析并提交真实 Command;移动、攻击、打断与完成由 CommandSystem 和执行层继续处理。

这个系统能做什么

能力

游戏内例子

按战术意图分工

Engage 时为前排、远程、治疗单位分别解析合适 Command。

从动态候选中选人

只从当前小队、一定范围、EQS 或项目注册表提供的 Unit 中选择。

从动态目标中配对

将敌人、友军、据点或位置目标按 SourceTag、标签和命令规则筛选。

避免过度集中

对同一 Actor/位置维护世界级 reservation 与容量,限制同时攻击或驻守人数。

持续重评估

单位死亡、目标离开、容量变化或 StateTree 分支变化后重新协调 assignment。

让不同 Unit 使用不同实际命令

同一个 Intent 可映射为近战、远程、治疗或防守 Command。

从意图到命令

FGAIS_TacticalAssignmentBatch.IntentTag 用“接敌、支援、驻守”等语义描述本批次的任务。候选 Actor 或其组件上的 IGAIS_CommandIntentResolver 再把语义变成具体的 FGAIS_CommandReference;UGAIS_UnitComponent 已通过 CommandIntentMappings 提供默认的数据驱动实现。

因此 Tactical Assignment 负责“候选怎样配对”,CommandDefinition 仍是“这条命令能否由该 Unit 对该目标执行、得分如何”的权威。自定义 Resolver 可以根据角色、装备、状态或 SourceTag 选择不同 Command;不要在 Source 或 Allocator 中写死这项命令选择。

同一个战术意图如何落到不同 Command

假设 Combat Plan 的第一批使用 GAIS.CommandIntent.Engage:

  • 剑士的 UGAIS_UnitComponent.CommandIntentMappings 将 Engage 映射到近战追击 Command;
  • 弓手将同一 Intent 映射到远程射击 Command;
  • 炮塔可以映射到不可移动的定点攻击 Command;
  • 更复杂的 Unit 可以实现 IGAIS_CommandIntentResolver,根据目标的 SourceTag、当前武器或弹药选择 Command 与变体索引。

每个候选 Unit 独立解析一次 Intent。解析出的 FGAIS_CommandReference 随后经过 CommandSystem 能力检查、Command 的 CanObeyCommand、取消策略、目标类型、IsValidTarget 与 GetTargetScore。持续重分配要求最终 Command 采用 Cancelable 策略,这样 Session 才能在目标失效、Batch 改选或 StateTree 退出时安全替换和清理。

Plan 因此可以保持通用。它表达“优先安排一批人接敌”,每种 Unit 决定自己执行哪一条可运行命令。

Session 会先检查候选 Actor 自身,再检查其 ActorComponent,寻找实现 IGAIS_CommandIntentResolver 的对象;第一个返回有效 FGAIS_CommandReference 的 Provider 胜出。常规 Unit 使用 UGAIS_UnitComponent 即可。项目同时挂载多个 Resolver 时,应让它们的适用条件清晰,避免多个 Provider 对同一 Intent 同时返回结果。

提交阶段调用候选 Unit 的 UGAIS_CommandSystemComponent.ReplaceCommandImmediately。成功结果会立即成为当前 Command,队列中不会追加一条延迟执行项。当前 Command 必须允许替换;战术分配由此能够在重评估时快速切换攻击目标、守点位置或支援对象。

一轮战术分配如何形成

每次进入 Task 或到达 ReevaluationInterval 时,Session 按以下顺序计算:

  1. 汇集执行者:逐个运行 AssigneeSources,清理无效 Actor,并把各 Source 的输出合并为全局 Unit 池。
  2. 汇集目标:逐个运行 TargetSources,为缺少来源信息的目标补写 SourceTag,清理无效目标并合并为全局 Target 池。
  3. 取得容量快照:World Subsystem 汇总其他 Session 已经持有的目标 claim,并解析每个物理目标的容量。
  4. 按顺序处理 Batch:前序 Batch 已经选中的 Unit 会被记录为 claimed,后续 Batch 从剩余 Unit 开始。
  5. 应用 Batch Filter:Assignee Filter 与 Target Filter 分别产生当前 Batch 的局部候选池。
  6. 构造 Unit/Target 配对:每个 Unit 解析 IntentTag,系统检查 CommandSystem、当前命令是否允许替换、Command 能力、取消策略和目标合法性。None / Passive Command 会生成无目标候选;其余目标类型需要真实 TargetSource 输出。
  7. 计算分数:CommandDefinition 返回 CommandScore,Batch 再应用 ScoreAdjustment 得到 AdjustedScore。
  8. 选择配对:Batch 的 Allocator 从已评分候选中选择 assignment。默认 Greedy With Capacity 按调整后分数从高到低处理,每个 Unit 最多选一次,并遵守容量快照。
  9. 核心复核与提交:核心检查 Allocator 输出确实来自原候选、Unit 没有重复、容量仍然可用,然后向各 Unit 的 CommandSystem 提交具体 Command。
  10. 更新 Session 真值:系统比较新旧 assignment,保留未变化项,重发需要更新的项,清理 Stale 项,并发布新的世界级 target claim。

这条链路解释了常见现象:Pool 有值而 Candidates 为零,问题位于 Resolver 或 Command 校验;Candidates 有值而 Selected 为零,问题位于评分、Allocator 或容量;Selected 有值而最终失败,问题位于提交时的状态变化或核心复核。

小队 AI 的典型用法

在 Squad StateTree 中让不同分支持有不同的 Task: Run Tactical Assignment Plan:Idle 状态用小队成员 Source 和 Follow/Hold Intent;Combat 状态用小队成员 Source、敌人/阵地 TargetSource,并按顺序配置 Engage、HoldPosition、Support Batch。前序 Batch 已选 Unit 不会进入后序 Batch,因此顺序就是“先攻击、再守点、最后支援”的优先级。

进入状态时 Task 创建 Session 并立即评估,状态保持期间按 ReevaluationInterval 重评估,退出状态时停止 Session 并释放仍属于该 Session 的命令与 target claim。为不同 Unit 配置同一 Intent 的不同映射即可产生分工;不要在 Batch 中写死 Command 类。

Source、Plan 与 Batch 的职责

Source 位于运行 Task/Session 的顶层。FGAIS_STT_RunTacticalAssignmentPlan 提供 AssigneeSources 和 TargetSources;每轮评估中,每个 Source 各自收到临时输出数组,系统把有效输出去重后合并为全局候选流。Plan 的 Batch 再从这份流中按顺序做不同优先级的请求。

层次

职责

StateTree Task / Session

选择 Plan、提供顶层 Source、持有本次运行与重评估生命周期。

Source

独立贡献候选 Actor 或 FGAIS_CommandTargetData;Target 可携带 SourceTag。

Plan

持有有序 Batches 和 ReevaluationInterval。

Batch

设置 IntentTag、Assignee/Target Filter、ScoreAdjustment 与可选 Allocator。

Allocator

从已评分、已校验的候选中选择配对;不能绕过最终容量检查。

前序 Batch 已分配的 Actor 不会再进入后序 Batch,所以 Batch 顺序就是战术优先级。多个 Source 是并集关系:一个 Source 本轮没有结果,不会阻止其他 Source 贡献候选。

Plan Data Asset 与 Inline Plan 的关系

Plan 和 Inline Plan 最终解析为同一种 UGAIS_TacticalAssignmentPlan,运行时都使用 Batches 与 ReevaluationInterval 驱动相同的评估、占用和提交流程。两者的差异集中在配置归属与复用方式:

形式

配置归属

适合场景

主要取舍

Plan Data Asset

独立资产,由一个或多个 StateTree Task 引用。

多个 AI、多个状态或多个关卡共用同一战术规则;希望集中校验与调整。

修改资产会影响所有引用者;需要结合每个 Task 自己的 Source 才构成完整运行配置。

Inline Plan

作为当前 StateTree Task 的内联实例保存。

单个状态专用规则、原型验证、只在一处使用的短 Plan。

配置就地可读;其他 Task 需要重新配置或复制,后续修改各自独立。

Task 要求二者必须且只能设置一个。AssigneeSources 和 TargetSources 始终由 Task 持有。Plan 描述“怎样分配”,Task 描述“本次运行从哪里取得人和目标”。这使同一个 Data Asset 能被不同小队、不同感知范围或不同关卡输入复用。

Source 与 Filter 为什么必须分开

Source 回答“本轮可能有哪些 Unit/目标”,Filter 回答“这个 Batch 允许哪些候选参与”。Source 在每轮评估开始时只运行一遍并形成全局并集;每个 Batch 再用自己的 AssigneeFilters、TargetFilters 从并集中切出局部视图。

例如 Combat Task 可以只配置一次“全部小队成员”与“敌人、掩体、守点位置”三类 Source,然后:

  • Attack Batch 用角色 Filter 选择战斗成员,用 SourceTag Filter 只保留敌人来源;
  • Hold Batch 用角色或状态 Filter 选择守卫,用 SourceTag Filter 只保留守点位置;
  • Support Batch 从尚未被前序 Batch 占用的成员中选择治疗者,并只保留友军 Actor 目标。

这比为每个 Batch 重复搜索世界更稳定,也让 GameplayDebugger 能分别显示“Source 根本没找到”还是“这个 Batch 的 Filter 排除了”。

Filter 负责候选剪枝,Command 继续负责执行与目标规则。系统先去掉前序 Batch 已占用的 Unit,再要求当前 Batch 的全部 Assignee Filter 通过;Target Filter 同样全部通过后,才为 Unit/目标建立配对,解析 IntentTag,并继续执行 Command 的 CanObeyCommand、可取消性、IsValidTarget 与评分。

SourceTag 的职责与边界

每个 Target Source 都可以配置 SourceTag。系统只在该 Source 产出的 FGAIS_CommandTargetData 尚未携带 SourceTag 时补写它;因此 Target Data Array 中逐条目标自带的 Tag 优先于 Source 的默认 Tag。

它主要有三种用途:

  1. UGAIS_TacticalAssignmentTargetFilter_SourceTag 可让不同 Batch 从同一 Target 流中选择不同来源。Allowed 为空代表不限制允许来源,Blocked 始终先拒绝;匹配使用 GameplayTag 层级语义。
  2. IGAIS_CommandIntentResolver 会在解析上下文中看到 SourceTag,同一 Intent 因来源不同可以解析为不同 Command,例如“对感知敌人攻击”,但“对任务标记目标压制”。
  3. GameplayDebugger 的 assignment 记录会显示来源 Tag,帮助区分目标来自 EQS、任务系统、玩家标记还是其他 Provider。

同一 Actor 以不同 SourceTag 进入时可以保留为不同的逻辑候选。目标容量仍按物理目标统一计算:Actor reservation 使用 Actor 身份,纯位置目标使用规范化网格。目标自身属性继续由 TargetTags 表达;SourceTag 只承载输入来源语义。

Source 运行时的正确心智模型

Source 的公开 Gather 入口写入输出数组。当前接口没有“成功、延期、失败”返回状态。空输出、EQS 本轮无结果、查询仍在处理、查询被终止或某个运行时输出无效,都会使该 Source 本轮贡献零候选;其他 Source 继续汇集并参与规划。

确定性配置错误在 Session 启动与评估前处理。系统会检查 Source 对象和 ValidateSourceConfiguration():空 Source 对象、EQS 缺少 QueryTemplate、必填 RoleTag 或明确要求的 SquadActor 未配置,都需要修复资产或上下文。运行中发现 Source 失效时会记录契约诊断,并将该 Source 视为零候选贡献;本轮仍使用其他 Source 的当前输出。

Deferred 表示世界级更新 guard 已被另一 Session 占用,或出现同步重入。此时已跟踪 assignment 与 reservation 保持原状。Source 本轮为空会进入正常评估;当候选消失时,完成的评估会协调旧分配,使失效记录进入 Stale,并释放仍匹配的命令与目标 reservation。

目标容量与持续协调

UGAIS_TacticalAssignmentSubsystem 是世界级 Session 注册表和物理目标容量权威。它为当前候选目标构造容量快照,纳入其他 Session 的既有 claim;Allocator 可以排序和选择,但核心会再次验证选择是否来自合法候选且仍满足容量。

默认容量策略使用 UGAIS_ProjectPolicy.MaxAssigneesPerTacticalTarget。项目也可以覆写 ResolveTacticalAssignmentTargetCapacity,针对目标返回:

  • Limited:最多允许 MaxAssignees 个 assignment;
  • Unlimited:允许任意数量共享;
  • Blocked:当前物理目标不接受 assignment。

Actor 目标按 Actor 身份进入 reservation bucket。纯位置目标按 UGAIS_DeveloperSettings.TacticalAssignmentLocationTargetGridSize 量化到网格。目标的 SourceTag、TargetTags 和 Actor 目标附带的位置不会拆分 bucket;同一物理目标始终共享一份容量。

容量由 assignment 和 Session 生命周期持有。assignment 在重评估中移除、提交失败或 Session 停止时,对应 claim 会释放。无目标 Command 不创建物理目标 reservation。多个 Context 同时规划时,Subsystem 通过世界级账本协调它们;较早成功提交的 claim 会计入后续 Session 的容量快照。

自定义容量策略适合表达“Boss 可被四人围攻”“掩体点只容纳一人”“集结区域允许无限共享”“暂时封锁的交互点拒绝占用”。策略对象以 CDO 方式调用,应保持无状态;跨 Session 的实际使用量已经由 Subsystem 维护。

运行方式与可观察结果

通常由 StateTree 的 Task: Run Tactical Assignment Plan 运行。进入状态时它立即创建 Session 并评估;之后依据 ReevaluationInterval 请求定时重评估;退出状态时撤销调度并关闭 Session。Task 公开 ReplanCount、LastUpdateResult 与 AssignmentCount,以及“首次可用/从不可用变可用”和反向边沿事件。

一个 ContextActor 同时只能拥有一个活跃 Session,防止两个计划同时替同一上下文争抢候选。若看到没有分配,按 Source pool、Filter 前后数量、Intent Resolver、命令校验、Candidates、Selected 和 Commit 的顺序排查;不要在 Allocator 中修复上游输入或命令问题。

调试与日志

在 GameplayDebugger 选择 Context Pawn/Controller 后打开 Tactical Assignment 面板。它显示最近一次完整评估的 Source 数、Actor/Target pool、每个 Batch 的过滤前后数量、Candidate/Selected、外部目标占用,以及 New/Kept/Reissued/Stale/Failed 记录和原因。按 Source → Filter → Resolver → Command → Capacity → Commit 的顺序定位失败。

需要诊断 Session 生命周期、重规划和提交结果时启用 LogGAIS_Tactical 的 Verbose 文本日志。GameplayDebugger 显示当前 Session/Batch 快照;每个 Source 的 Actor/Target 明细当前通过 Visual Logger 的 UE_VLOG 输出。面板快照用于诊断,运行时正确性不依赖 Shipping/Test 是否填充诊断数据。

适用场景与相邻系统

当任务需要根据动态候选和容量持续重配时使用 Tactical Assignment;让成员回到阵型槽位由 Squad、Formation 和 Movement Intent 负责;命令的 Unit 与目标已经明确时直接使用 CommandSystem。Tactical Assignment 聚焦离散的战术匹配与协调,实际行为和移动继续交给下游系统。

接下来阅读什么