Tactical Assignment 扩展点
Tactical Assignment 的安全扩展方式是按阶段改变一个职责:Source 提供输入,Filter 提前剪枝,Resolver 选择具体 Command,命令定义验证和评分,Allocator 选择已评分候选,核心最后提交并维护 reservation。跨阶段补规则会造成重复校验、误导性的诊断或绕过世界容量。
先选择正确的扩展点
你要改变什么 | 应扩展的位置 | 容易放错的位置 |
|---|---|---|
候选 Unit/目标来自哪里 | | Allocator 或 Command Definition。 |
明显不该参与的候选 | Assignee/Target Filter | Source 的 Gather 结果状态。 |
同一意图对不同 Unit 应是什么 Command | | Target Filter 或 Allocator。 |
Unit 能否执行、目标是否合法、目标基础偏好 | | Filter 之外的重复命令逻辑。 |
已评分候选如何选中 | | 重新搜索世界、直接下发 Command。 |
物理目标允许多少人占用 | | Batch 或自定义 Allocator 的私有计数器。 |
自定义 Source 的独立产出契约
Source 的 GatherActors / GatherTargets 是 BlueprintNativeEvent,职责是向本次调用提供的临时数组写入自身候选。每个 Source 都独立执行,系统随后清理无效项、去重并合并所有 Source 的输出。
- 用
ValidateSourceConfiguration验证无法靠下一帧恢复的配置:EQS QueryTemplate、必填 RoleTag、必须显式设置的 SquadActor 等。 - Gather 本轮没有结果时输出空数组即可;不要再实现或模拟“Source Deferred/Failed”返回值。
- EQS/项目查询的运行时失败、空集或失效输出只让这个 Source 本轮不贡献候选;其他 Source 仍然有效。
- Target Source 可设置
SourceTag,供UGAIS_TacticalAssignmentTargetFilter_SourceTag、Resolver 与诊断区分输入来源。
如果一个旧 assignment 的候选在本轮 Source 并集中消失,核心会在正常评估中把不再可用的记录协调为 Stale,并释放仍匹配的命令和 reservation。不要为了“保留旧状态”把本轮空输出错误地转换为 Deferred。
什么时候扩展 Assignee Source
当候选 Unit 来自插件没有内置适配的权威容器时扩展 UGAIS_TacticalAssignmentAssigneeSource,例如遭遇战实例的参战者注册表、载具座位系统、召唤物管理器或项目自己的编队容器。覆写时:
ValidateSourceConfiguration只拒绝不会随运行时重试恢复的配置错误;GatherActors只读取自己的数据源,把本轮有效 Actor 写入输出;- 不读取其他 Source 的临时输出,不缓存上一轮结果来伪装本轮候选;
- 让核心负责无效项清理、去重和多个 Source 的并集。
从“全部小队成员”中挑治疗者时,现有小队 Source 加 Role/Tag Assignee Filter 已能覆盖,并且更容易复用。
一个项目 Source 通常只需要两个入口:
ValidateSourceConfiguration:检查类、查询资产、GameplayTag、注册表引用等稳定配置;GatherActors(ContextPawn, OutActors):从 Context Pawn 可达的组件、Subsystem 或项目容器读取当前成员。
蓝图子类可以实现这两个 BlueprintNativeEvent。C++ 子类适合高频查询、原生容器或需要更严格类型检查的 Source。每次 Gather 都从空输出数组开始,输出顺序应尽量稳定;稳定顺序有助于相同分数下获得可预测结果。
什么时候扩展 Target Source
当目标来自任务系统、掩体系统、玩家 Ping、占领点注册表或项目感知缓存时扩展 UGAIS_TacticalAssignmentTargetSource。它应产出真正的 FGAIS_CommandTargetData:
- Actor 目标用于敌人、友军、设施等;
- Location 目标用于守点、包抄、巡逻位置;
- Actor + Location 用于既要保留目标 Actor,又要固定一个接近/交互位置的场景;
TargetTags描述目标自身属性,SourceTag描述目标从哪条输入渠道进入。
为 Source 配置一个稳定的默认 SourceTag,或在每条 TargetData 上写更具体的 Tag。核心只会在目标尚未带 Tag 时采用 Source 默认值。不要用不同 Tag 复制同一 Actor 来规避容量;它们仍共享同一个 Actor reservation bucket。
自定义 Target Source 的入口与 Assignee Source 对称:
ValidateSourceConfiguration处理稳定配置;GatherTargets(ContextPawn, OutTargets)产出本轮 Actor、Location 或 Actor+Location 数据。
系统会丢弃无法建立物理目标身份的数据。Actor 目标使用 Actor 身份,纯位置目标需要有限数值坐标。多个 Source 的结果会取并集;相同物理目标携带不同 SourceTag 时可以保留为不同逻辑候选,并在容量阶段回到同一个 reservation bucket。
Filter 的剪枝职责
Filter 适合价格低、语义明确的资格判断,例如 Actor 标签、队伍态度、目标来源、是否携带 Actor、Squad Role 或与 Movement Intent 期望位置的距离。所有已配置 Filter 必须通过,之后才会进入 Command 的 CanObeyCommand 与 IsValidTarget。
Filter 负责低成本、语义明确的候选资格判断。攻击距离、资源消耗、技能冷却、复杂目标关系等继续由 CommandDefinition 统一处理,从而让手动命令和 Tactical Assignment 共用一套规则。Squad 专属规则优先放在 GAIS_SquadTacticalAssignmentAdapters 提供的 Filter/Source 适配层,核心运行时上下文保持通用。
自定义 Assignee Filter
蓝图继承 UGAIS_TacticalAssignmentAssigneeFilter 并实现 PassesAssigneeFilter,可以读取 Context Actor 与候选 Actor,适合轻量项目规则。C++ 可覆写带上下文入口,直接使用已缓存的 CommandSystem 与 Actor owned tags。
适合的例子包括“只允许当前弹药大于零”“只允许没有被玩家锁定控制的单位”“只允许属于当前遭遇实例”。如果判断依赖最终解析出的 Command 或某个 Unit/Target 配对,就已经超出 Assignee Filter 阶段,应放到 Resolver 或 CommandDefinition。
自定义 Target Filter
蓝图继承 UGAIS_TacticalAssignmentTargetFilter 并实现 PassesTargetFilter,可以读取 Context Actor 和完整 TargetData;C++ 的上下文入口还直接提供 TargetActor、SourceTag 与目标 owned tags。
适合的例子包括“只接受当前任务阶段启用的占领点”“排除玩家保留的掩体”“只让某 Batch 消费来自特定感知通道的目标”。Target Filter 在为具体 Unit 建立配对之前运行,因此不适合依赖“这个 Unit 到这个目标的最终攻击代价”;这类逐配对规则应由 CommandDefinition 的目标合法性与评分处理。
Resolver:把语义交给真正能执行的 Unit
IGAIS_CommandIntentResolver::ResolveCommandIntent 接收到 Context Actor、候选 Actor、IntentTag、预处理目标数据和 SourceTag,并返回要继续校验的 FGAIS_CommandReference。
默认 UGAIS_UnitComponent 读取 CommandIntentMappings,适合固定映射。需要根据角色、武器、Buff、目标来源或运行时状态选命令时,在 Unit、Actor 或项目组件上覆写/实现 Resolver。Resolver 只选择 Command Reference,不下发命令,也不负责最终目标选择。
Session 的 Provider 搜索顺序从候选 Actor 开始,随后检查它的 ActorComponent,并采用第一个有效解析结果。一个 Unit 同时存在多个 Resolver 时,可按明确的职责划分返回条件,例如武器组件只处理攻击 Intent,治疗组件只处理支援 Intent。固定映射、动态武器选择和项目特殊规则可以由不同 Provider 承担。
Resolver 返回的 Command 还要经过 CanObeyCommand、Cancelable 策略、目标类型、IsValidTarget 与评分。Resolver 适合决定“选哪条 Command”;CommandDefinition 继续维护这条 Command 的执行契约。
Allocator 的选择职责与容量边界
自定义 UGAIS_TacticalAssignmentAllocator 的 Allocate 收到的是已通过 Source、Filter、Intent 解析、命令校验与评分的候选,以及 TargetCapacities 容量视图。它只应将想保留的原始候选写入 SelectedAssignments。
世界 Subsystem 和核心提交阶段仍会验证选择是否对应规范候选、Actor 是否重复、以及目标容量是否仍允许。不要在 Allocator 中维护跨 Session 的持久计数、伪造新 candidate、重新 Gather Source 或直接调用 CommandSystem;这些会与 reservation ledger 冲突。
默认 UGAIS_TacticalAssignmentAllocator_GreedyWithCapacity 已按分数选择每个 Actor 的可用候选并尊重容量。只有确实需要职业配额、全局配比或特殊排序时才编写自定义 Allocator。
适合自定义 Allocator 的例子:
- 先保证每个敌人至少分到一个攻击者,再追加高分单位;
- 在近战、远程之间维持人数配比;
- 让守点单位尽量分散到不同位置;
- 以小队整体收益选择组合,而非逐个 Unit 贪心。
Allocate 的输入包含 CandidateAssignments 和 TargetCapacities,输出只填写 SelectedAssignments。实现需要保证每项都来自输入、每个 Actor 最多出现一次,并按容量视图控制新增使用量。核心仍会执行最终复核。
自定义目标占用
项目通过继承 UGAIS_ProjectPolicy 并覆写 ResolveTacticalAssignmentTargetCapacity 定义占用规则,再在 Generic AI System Settings 的 ProjectPolicyClass 选择该策略类。
容量策略可以根据 CanonicalTargetData 进行判断,例如:
- Boss Actor 返回
Limited(4); - 普通敌人返回
Limited(1); - 带“公共集结点”标签的位置返回
Unlimited; - 当前被玩法系统封锁的目标返回
Blocked。
Actor 与位置网格构成 reservation 身份。SourceTag 和 TargetTags 可以帮助策略理解目标,但它们不会创建新的 bucket。对于同一 Actor,容量策略应返回一致结果;bucket 保持占用期间会沿用已解析容量。策略对象以 CDO 运行,运行时 claim、跨 Session 计数和释放都由 UGAIS_TacticalAssignmentSubsystem 维护。
TacticalAssignmentLocationTargetGridSize 决定相邻位置何时落入同一 bucket。缩小网格可以区分更密集的站位点,增大网格可以把一片区域视为共享目标。该设置影响整个项目中的位置目标,需要结合实际点位间距调整。
assignment 从 Session 中移除或 Session 停止时,Subsystem 会释放对应 claim。命令是否仍在执行不会延长 claim 生命周期。这样新的战术计划可以立即使用已经释放的目标容量。
Deferred 与 Stale 的边界
Deferred 来自世界更新 guard 冲突或同步重入,表示本次没有改动 tracked assignment/reservation,之后由任务重评估重试。Source 的空结果或查询错误会作为该 Source 的空贡献参与正常评估。
正常完成评估时,当前 Source 并集就是权威输入。候选消失、过滤条件变化或命令不再成立会让旧 assignment 按差异处理成为 Stale/失败并清理匹配资源。自定义 Source 每轮提交当前真实结果,过期候选交给 Session 差异流程清理。
扩展后的验证顺序
- 先用固定 Source 验证 Resolver 与 CommandDefinition。
- 单独验证新的 Source 能输出独立候选,且空输出不会阻断其他 Source。
- 检查 Filter 前后数量与
SourceTag、队伍态度或 Role 预期一致。 - 确认 Allocator 的 Selected 均来自输入
CandidateAssignments,容量不足时能接受核心拒绝。 - 在 GameplayDebugger 中检查 pool、Candidates、Selected、Stale 与 Commit Outcome。
参数选择见 Tactical Assignment 参数,完整接入步骤见 配置 Tactical Assignment Plan,全局行为说明见 战术分配系统。
