配置 Tactical Assignment Plan

GAIS_Cover
YuewuDev

作者

YuewuDev

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

目录

配置 Tactical Assignment Plan

本篇建立一个最小闭环:StateTree Task 从固定数组 Source 汇集候选 Unit 和目标,Unit 把 GAIS.CommandIntent.Engage 解析为具体 Command,系统选择并提交至少一条 assignment。完成后,Task 的 AssignmentCount 大于零,候选 Unit 的 CommandSystem 中能看到对应的具体命令。

第一轮使用固定数组,暂时不加入 EQS、小队适配器和自定义 Allocator。这样可以把“输入是否正确”“命令是否可执行”“容量是否允许”分开验证。

前置条件

  • 一个由 AAIController 驱动的 StateTree。
  • 至少一个候选 Pawn/Character,在 Pawn 上带有 UGAIS_CommandSystemComponent,并能接收目标 Command。
  • 候选 Actor 上的 UGAIS_UnitComponent,或其上实现的 IGAIS_CommandIntentResolver。
  • 一个已经能被手动下发、且能接受测试目标的 Command Definition。
  • 一个测试目标 Actor,以及项目中已存在的 GAIS.CommandIntent.* 标签。

Tactical Assignment 不会绕过 CanObeyCommand 或 IsValidTarget。先确认手动命令能运行;否则自动分配失败时无法判断问题在命令还是规划。

Task 在 Authority 上启动,并从 AIController 当前 Possession 派生 ContextPawn。Controller 无效、缺少 Authority、Pawn 已换人、Plan 无法唯一解析或 Plan 没有 Batch,都会让 Task 直接返回 Failed。

1. 配置 Unit 的意图映射

在 UGAIS_UnitComponent.CommandIntentMappings 添加一项:

字段

示例

要点

IntentTag

GAIS.CommandIntent.Engage

必须与 Batch 完全相同。

Command.Definition

项目的攻击或支援命令

必须是此 Unit 可执行的有效 Definition。

Command.Index

默认值或有效变体索引

MultiVariant Command 才需要选择特定变体。

默认 UnitComponent 会从该表解析意图。若玩法需要根据装备、状态或目标来源动态选命令,再实现自定义 Resolver;最小验证阶段不应把这层复杂度混入 Source 或 Allocator。

2. 先决定使用 Data Asset 还是 Inline Plan

两种入口使用同一个 UGAIS_TacticalAssignmentPlan 类型,运行结果一致。配置是否需要复用,决定采用哪一种形式。

如果你的情况是

选择

原因

多个小队 StateTree、多个状态或多个关卡共用同一批次规则

Plan Data Asset

规则集中维护;每个 Task 仍可绑定不同 Source。

Combat Plan 要由设计人员独立审阅、校验和调参

Plan Data Asset

资产边界清晰,引用者共享修改。

只为一个状态快速验证单 Batch

Inline Plan

无需额外资产,配置紧贴调用点。

规则仅在该状态成立,复制到别处反而容易误用

Inline Plan

让局部规则与 StateTree 分支一起维护。

不要同时填写 Plan 和 Inline Plan,也不要两者都留空;Task 会因无法解析唯一 Plan 而进入 Failed。还要注意:两种 Plan 都不保存 Source。Source 始终配置或绑定在 Task 的 AssigneeSources / TargetSources 上,所以一个共享 Data Asset 可以针对不同小队运行。

以下示例使用 Data Asset;若选择 Inline Plan,就在 Task 的 Inline Plan 展开相同的 Batch 字段。

3. 创建 Plan

创建 UGAIS_TacticalAssignmentPlan Data Asset,并先只填一个 Batch:

字段

最小设置

Batches

一个 Batch。

DebugName

例如 EngageTargets,便于调试定位。

IntentTag

与 Unit 映射相同。

AssigneeFilters、TargetFilters

留空。

ScoreAdjustment

保持默认乘数 1、偏置 0。

Allocator

留空,使用默认容量感知贪心分配器。

ReevaluationInterval

保持正数;首次接入可使用默认值。

Plan 至少需要一个 Batch;每个 Batch 需要有效 IntentTag;重评估间隔必须是正的有限数值。它们是 Data Asset 可直接校验的配置项。

4. 在 StateTree Task 上配置顶层 Source

在目标 StateTree 状态中添加 Task: Run Tactical Assignment Plan(FGAIS_STT_RunTacticalAssignmentPlan)。在该 Task 上:

  1. 把 AIController 绑定到当前 AIController 上下文。
  2. 在 Plan 和 Inline Plan 中只设置一个;本例将 Data Asset 填入 Plan。
  3. 在 AssigneeSources 添加 Actor Array,填入测试 Unit Actor。
  4. 在 TargetSources 添加 Actor Targets,填入测试目标 Actor;需要区分来源时设置 SourceTag。

Source 在 Task 上,不在 Batch 中。每个 Source 独立填充临时数组,Task 将有效输出合并为候选流,再交由 Plan 的各 Batch 顺序消费。

plaintext
1StateTree Task
2 ├─ Plan 或 Inline Plan(二选一)
3 ├─ AssigneeSources:Actor Array
4 └─ TargetSources:Actor Targets
5 ↓
6Plan Batch:IntentTag、Filter、ScoreAdjustment、Allocator

Task 输入与输出怎样用于 StateTree 分支

字段

配置方式

StateTree 中的用途

AIController

绑定当前 AIController 上下文。

决定 Authority、Context Pawn 和 Session 唯一所有者。

Plan / Inline Plan

二选一。

提供 Batch 与重评估间隔。

AssigneeSources

内联配置或通过任务数据准备运行时 Source。

汇集所有可能接收 Command 的 Actor。

TargetSources

内联配置或通过任务数据准备运行时 Source。

汇集 Actor、Location 或 Actor+Location 目标。

ReplanCount

只读输出。

判断 Session 已完成多少次评估。

LastUpdateResult

只读输出。

区分 Completed、Deferred 与 Failed。

AssignmentCount

只读输出。

判断当前完整评估保留了多少 assignment。

OnAssignmentsAvailable

边沿事件。

首次得到 assignment,或从零恢复到非零时驱动转换。

OnAssignmentsUnavailable

边沿事件。

首次完整评估为零,或从非零降到零时驱动兜底状态。

Task 进入状态后保持 Running,定时重评估由 Task 自己的 StateTree Scheduled Tick 驱动。退出状态时 Task 撤销调度并停止 Session,匹配的 Command 与 reservation 随之清理。

5. 进入状态并验证

Task 进入状态时会立即评估。请按顺序观察:

  1. LastUpdateResult 显示 Completed;短暂的世界更新冲突可能显示 Deferred 并等待重试。
  2. ReplanCount 在完整评估后增加,AssignmentCount 大于零。
  3. 候选 Unit 的 CommandSystem 收到由 IntentTag 解析得到的具体 Command。
  4. GameplayDebugger 中 Actor pool、Target pool、Candidates、Selected 和最终 Outcome 都符合预期。

OnAssignmentsAvailable 在首次得到分配,以及之后从无分配恢复为有分配时触发。连续有分配期间不会重复广播。若本轮因世界更新 guard 冲突得到 Deferred,Task 会保持运行并在之后重试。

6. 用 SourceTag 标记目标来自哪里

当一个 Task 同时收集多类目标时,为 Target Source 使用 GAIS.TacticalAssignment.TargetSource.* 子标签。例如:

Target Source

SourceTag

产出

EQS Actor Targets

...Perception.Enemy

感知或空间查询发现的敌人 Actor。

Target Data Array / 项目 Source

...Objective.DefensePoint

任务系统给出的守点位置。

项目 Source

...Player.Ping

玩家临时标记的目标。

SourceTag 是 Target Source 的默认来源标签:只有目标数据本身没有 Tag 时才补写。若 Target Data Array 中某一项已经带 Tag,该项自己的 Tag 会保留。

接着在 Batch 的 TargetFilters 添加 Target Source Tag:

  • 攻击 Batch:AllowedSourceTags 允许 ...Perception.Enemy 与 ...Player.Ping;
  • 守点 Batch:只允许 ...Objective.DefensePoint;
  • 某类危险来源永远不可用时放入 BlockedSourceTags。Blocked 先于 Allowed 判定。

这不会创建独立的目标占用。同一个敌人 Actor 即使以“感知”和“玩家标记”两个 Tag 进入,世界容量仍按同一个 Actor 计算。

7. 用 Filter 让同一份 Source 服务多个 Batch

Source 应尽量表达稳定的数据来源,Batch Filter 表达当前战术规则。以战斗小队为例:

  1. AssigneeSources 只放一个 All Squad Units,显式绑定当前 Squad Actor。
  2. TargetSources 放敌人 EQS Source 与守点位置 Source,并为两者设置不同 SourceTag。
  3. Plan 中按优先级添加 Batch:
    • EngagePriority:Assignee Filter 选择战斗角色;Target Filter 要求 Hostile、Has Actor、敌人来源。
    • HoldPoints:从前一批未占用的 Unit 中选择守卫;Target Filter 要求 No Actor、守点来源。
    • SupportRemainder:选择治疗/支援角色;Target Filter 限定友军 Actor。

所有配置在同一数组内的 Filter 都必须通过。Assignee Filter 在命令意图解析前把不合适的 Unit 剪掉,Target Filter 在命令 IsValidTarget 前剪掉不合适的目标;CommandDefinition 仍负责最终的执行者和目标合法性。

验证时看 GameplayDebugger 中每个 Batch 的 Actors before -> after 与 Targets before -> after:

  • before 就是 0:回到 Source;
  • before 有值、after 为 0:检查当前 Batch 的 Filter;
  • after 有值但 NoResolvedCommand:检查 Intent 映射;
  • Candidates 有值、Selected 为 0:检查容量和 Allocator。

8. 正确处理没有 Source 输出的情况

当前 Source API 使用输出数组,没有 runtime 成功状态返回值。EQS 本轮没有结果、某个项目查询暂时产出空集或一个 Source 输出被判无效时,该 Source 本轮贡献零候选;其他 Source 继续参与,整轮评估仍会正常完成。

Source 空输出仍会完成本轮正常评估。若候选因此从合并流中消失,系统会协调先前 assignment,并让不再可用的记录进入 Stale 清理。Deferred 专门表示世界更新冲突或同步重入,此时保留旧的 tracked assignment/reservation。

配置错误则应修复,而非等待下轮:空 Source 对象、EQS 缺 QueryTemplate、必填 RoleTag 或 SquadActor 缺失都会使验证失败。

9. 按层增加真实玩法规则

最小闭环通过后,逐项加入:

  1. 用小队、EQS 或项目容器 Source 替换固定数组。
  2. 加入 Assignee Filter,再验证 Actor pool 的过滤前后数量。
  3. 加入 Target Filter,检查 SourceTag、队伍态度和目标形态。
  4. 调整 ScoreAdjustment,观察候选排序是否符合设计。
  5. 默认 Allocator 不足时再自定义 Allocator;它接收已校验候选与世界容量快照,不应重新发现目标或重复命令校验。

10. 组织一个 Idle / Combat 小队分支

在小队的 StateTree 中,把 Task 放在真正拥有该战术语义的状态。通常由 Idle、Combat 等分支分别持有对应 Task:

状态

顶层 Source

Plan Batch

结果

Idle

All Squad Units;目标可省略或提供编队/岗位位置。

Inline Plan:Follow 或 Hold Intent。

规则只属于 Idle 分支时就地维护,成员恢复跟随、站位或警戒。

Combat

All Squad Units;敌人 EQS、守点位置和友军 Target Source。

Data Asset:先 Engage,后 HoldPosition,再 Support。

可复用战斗规则;同一小队不同成员分别攻击、驻守或支援。

进入 Combat 时 Task 创建 Session 并立即评估;状态保持期间按 ReevaluationInterval 重评估;退出 Combat 时 Task 停止 Session。为每种 Unit 配置同一 Intent 的不同映射即可产生分工:前排的 Engage 解析为近战 Command,远程解析为射击 Command,治疗者只参与 Support Batch。Intent Resolver 才是“该 Unit 把战术语义落成什么命令”的接缝。

一个六人小队的完整配置示例

假设小队包含两名近战、一名远程、一名治疗者和两名守卫。Combat Task 使用:

Assignee Sources

  • All Squad Units:SquadOverride 指向当前 Squad Actor,得到六名成员。这个 Source 要求显式 Squad Actor。
  • 如果 Context Pawn 本身带有 SquadComponent,也可以按角色使用 Squad Units By Role;其 SquadOverride 为空时会尝试 Context Pawn,RoleTag 必须有效并采用精确匹配。

Target Sources

  • EQS Actor Targets:查询当前敌人,SourceTag = GAIS.TacticalAssignment.TargetSource.Perception.Enemy。
  • EQS Location Targets:查询可守卫位置,SourceTag = GAIS.TacticalAssignment.TargetSource.Defense.Position。
  • All Squad Units:把指定 Squad 的成员作为友军目标,供治疗 Batch 使用。

Plan Batches

  1. AttackPriority
    • IntentTag = Engage
    • Assignee Filter:Role Query 接受近战与远程
    • Target Filter:Team Attitude = Hostile、Has Actor、SourceTag = Enemy
    • 结果:三名战斗成员优先取得敌人目标
  2. HealAllies
    • IntentTag = Support
    • Assignee Filter:Role Query 只接受治疗者
    • Target Filter:Has Actor、友军标签或 Friendly 队伍态度
    • 结果:治疗者从前序 Batch 未使用的成员中取得友军目标
  3. HoldDefensePoints
    • IntentTag = HoldPosition
    • Assignee Filter:Role Query 接受守卫
    • Target Filter:No Actor、SourceTag = Defense.Position
    • 结果:两名守卫竞争位置目标,每个位置的容量通常设置为一

Batch 顺序会先占用 Unit。若治疗者也满足 Attack 的 Assignee Filter,它会在第一批被 claim,后续 HealAllies 无法再使用。因此角色 Filter 和 Batch 顺序共同决定“谁先干什么”。目标容量则决定“同一个目标能分给多少人”。

Idle 分支可使用一个短 Inline Plan,只有 FollowSquad Batch。Follow Command 若采用 None 或 Passive 目标类型,Task 可以保留空 TargetSources。进入 Combat 分支后,Idle Session 停止,Combat Task 创建新的 Session 并下发战斗 Command。

11. 用面板与日志验证真实分配

运行 PIE 后打开 GameplayDebugger,启用 GenericAISystem category,选中由 AIController 控制的 Pawn,再按 Shift+7 切换 Tactical Assignment section。先看 Source 数与 Actor/Target pool,再看每个 Batch 的 Filter 前后数量、Candidate/Selected、外部目标占用和 Assignment 记录;这比只看 Unit 是否移动更容易判断问题在搜集、意图解析、命令校验、容量还是提交。

需要诊断 Session 开始/停止、重规划和提交结果时,可在控制台临时使用 Log LogGAIS_Tactical Verbose。GameplayDebugger 用于查看当前 Session/Batch 快照;每个 Source 产出的 Actor/Target 明细当前通过 Visual Logger 的 UE_VLOG 输出,不在普通文本日志里逐项展开。诊断结束后执行 Log LogGAIS_Tactical Log。需要跨启动保留生命周期文本日志时在项目 [Core.Log] 配置 LogGAIS_Tactical=Verbose。

常见失败

现象

优先检查

进入 Task 立即 Failed

AIController 是否有效;Plan/Inline Plan 是否恰好一个;Plan/Source 的确定性配置是否完整。

NoResolvedCommand

Batch IntentTag 与 Unit 映射是否一致;Resolver 和 Command Definition 是否有效。

CannotObeyCommand

具体 Command 的执行者需求、资源和项目状态。

NonCancelableCommand

Resolver 返回的 Command 需要采用 Cancelable 取消策略,才能参加持续重分配。

InvalidTarget

Source 输出是否匹配 Command 的 TargetType;Target Filter 是否过严。

Candidates 有值但 Selected 为零

世界容量快照、已有 claim、评分与 Allocator 选择。

一个 Source 无输出

检查该 Source 自身输入和其他 Source 是否仍有候选;本轮会按实际合并结果继续评估。

None / Passive Command 不需要 Target Source,会使用局部空目标数据。不要向共享 TargetSources 填充虚假目标;同一 Task 的其他 Batch 可能仍需要真实目标流。

后续阅读