战术分配系统
Tactical Assignment 用来解决“当前局势下该由谁去做什么、面对哪个目标”的匹配问题。它从一组独立的候选单位与目标 Source 汇集本轮输入,用 IntentTag 请求战术语义,由每个 Unit 解析出自己实际能执行的 Command,随后完成过滤、命令校验、评分、容量检查与提交。
玩家、UI 或脚本已经知道确切 Unit、Command 和目标时,直接使用 CommandSystem 更简单。Tactical Assignment 主要处理候选执行者、候选目标或目标容量会随战场变化的自动决策,例如治疗者选择友军、岗位单位占据防御点、或多名单位分摊攻击目标。
这个系统解决什么问题
命令系统回答“让这个单位做什么”;战术分配回答“这一组单位分别该做什么”。当 Unit、Command 和目标都已经明确时,直接发 Command 最简单;当只有“这支小队现在要接敌、守点、支援”这类群体战术目标时,逐个手写命令会失去角色差异、目标容量和动态重配能力。
战术分配把群体任务拆成可执行的个体 assignment:近战单位接近敌人、远程单位攻击另一目标、治疗者支援受伤友军、其余成员坚守阵地。它为每个被选 Unit 解析并提交真实 Command;移动、攻击、打断与完成由 CommandSystem 和执行层继续处理。
这个系统能做什么
能力 | 游戏内例子 |
|---|---|
按战术意图分工 | |
从动态候选中选人 | 只从当前小队、一定范围、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 按以下顺序计算:
- 汇集执行者:逐个运行
AssigneeSources,清理无效 Actor,并把各 Source 的输出合并为全局 Unit 池。 - 汇集目标:逐个运行
TargetSources,为缺少来源信息的目标补写SourceTag,清理无效目标并合并为全局 Target 池。 - 取得容量快照:World Subsystem 汇总其他 Session 已经持有的目标 claim,并解析每个物理目标的容量。
- 按顺序处理 Batch:前序 Batch 已经选中的 Unit 会被记录为 claimed,后续 Batch 从剩余 Unit 开始。
- 应用 Batch Filter:Assignee Filter 与 Target Filter 分别产生当前 Batch 的局部候选池。
- 构造 Unit/Target 配对:每个 Unit 解析
IntentTag,系统检查 CommandSystem、当前命令是否允许替换、Command 能力、取消策略和目标合法性。None/PassiveCommand 会生成无目标候选;其余目标类型需要真实 TargetSource 输出。 - 计算分数:CommandDefinition 返回
CommandScore,Batch 再应用ScoreAdjustment得到AdjustedScore。 - 选择配对:Batch 的 Allocator 从已评分候选中选择 assignment。默认 Greedy With Capacity 按调整后分数从高到低处理,每个 Unit 最多选一次,并遵守容量快照。
- 核心复核与提交:核心检查 Allocator 输出确实来自原候选、Unit 没有重复、容量仍然可用,然后向各 Unit 的 CommandSystem 提交具体 Command。
- 更新 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 或 |
Plan | 持有有序 |
Batch | 设置 |
Allocator | 从已评分、已校验的候选中选择配对;不能绕过最终容量检查。 |
前序 Batch 已分配的 Actor 不会再进入后序 Batch,所以 Batch 顺序就是战术优先级。多个 Source 是并集关系:一个 Source 本轮没有结果,不会阻止其他 Source 贡献候选。
Plan Data Asset 与 Inline Plan 的关系
Plan 和 Inline Plan 最终解析为同一种 UGAIS_TacticalAssignmentPlan,运行时都使用 Batches 与 ReevaluationInterval 驱动相同的评估、占用和提交流程。两者的差异集中在配置归属与复用方式:
形式 | 配置归属 | 适合场景 | 主要取舍 |
|---|---|---|---|
| 独立资产,由一个或多个 StateTree Task 引用。 | 多个 AI、多个状态或多个关卡共用同一战术规则;希望集中校验与调整。 | 修改资产会影响所有引用者;需要结合每个 Task 自己的 Source 才构成完整运行配置。 |
| 作为当前 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,然后:
AttackBatch 用角色 Filter 选择战斗成员,用SourceTagFilter 只保留敌人来源;HoldBatch 用角色或状态 Filter 选择守卫,用SourceTagFilter 只保留守点位置;SupportBatch 从尚未被前序 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。
它主要有三种用途:
UGAIS_TacticalAssignmentTargetFilter_SourceTag可让不同 Batch 从同一 Target 流中选择不同来源。Allowed 为空代表不限制允许来源,Blocked 始终先拒绝;匹配使用 GameplayTag 层级语义。IGAIS_CommandIntentResolver会在解析上下文中看到SourceTag,同一 Intent 因来源不同可以解析为不同 Command,例如“对感知敌人攻击”,但“对任务标记目标压制”。- 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 聚焦离散的战术匹配与协调,实际行为和移动继续交给下游系统。
接下来阅读什么
- 配置最小可运行路径:配置 Tactical Assignment Plan。
- 查询资产、Source 与容量配置:Tactical Assignment 参数。
- 实现自定义 Source、Filter、Resolver 或 Allocator:Tactical Assignment 扩展点。
- 运行时按信号定位失败:排错流程。
