接入最小命令单位

GAIS_Cover
YuewuDev

作者

YuewuDev

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

目录

接入最小命令单位

本篇做出一个可验证的 AI 单位闭环:守卫 Pawn 平时运行自己的 AI,收到移动命令后进入 Command StateTree;玩家可追加两个路点,随后插入一次紧急闪避,最后取消命令并看到原行为恢复。这个场景同时验证 Pawn 挂载限制、执行层路由、FIFO 队列和打断边界。

相关文档:先理解命令系统,字段细节见命令定义与下发参数,需要自定义后端时读命令扩展点。

前置条件

准备一个由 AAIController 控制的 Pawn 或 Character,并确认它不是玩家控制 Pawn。默认 StateTree 执行层将拒绝 Player-controlled Pawn、没有 AIController 的 Pawn,以及 Controller 上没有 StateTreeStack 的情况。

  1. 项目已启用 GenericAISystem 与 StateTree / GameplayStateTree 依赖。
  2. Pawn 的 Controller 上已有 UGAIS_StateTreeStackComponent,并配置了可用的 Command 层。
  3. 项目 Blackboard 已保留默认命令 Key,或已同步修改 UGAIS_DeveloperSettings 与 StateTree 中的读取 Key。
  4. 操作命令下发逻辑在服务端运行;若来自拥有该 Pawn 的客户端,则使用对应的 Server... RPC。

UGAIS_CommandSystemComponent 必须添加在 Pawn 本身,不是 AIController、普通 Actor 或建筑 Actor。若当前测试对象是一个普通 Actor,先改为 Pawn/Character 再继续;仅把组件通过蓝图添加到 Actor 不构成受支持的配置。

1. 给 AI Pawn 添加组件

在守卫 Pawn Blueprint 的 Components 面板添加 Command System Component(UGAIS_CommandSystemComponent)。不要把它添加到 Controller Blueprint。若该单位之后还要进入小队或 Tactical Assignment,再额外添加 UGAIS_UnitComponent;本教程不依赖它。

运行 PIE 后,在服务端获取该 Pawn 的 Get CommandSystemComponent。初始时 IsIdle 应为 true,GetCurrentCommandData 为空,队列为空。这个检查能立即发现组件挂错对象或未正确初始化的问题。

2. 创建最小移动命令

创建一个 Blueprint,父类选择 Simple Command Definition,取名 Cmd_Move。Simple 适合“一个资产就是一个稳定命令”的情况;移动、交互、治疗和普通攻击通常都从它开始。

在资产中完成下面的最小配置:

分组

建议配置

目的

Common

TargetType 设为 Location,取消策略设为 Cancelable。

位置请求可被接受,也允许新的移动或闪避打断。

Display

填名称、图标与可选按钮槽位。

供 HUD 或命令界面展示;不影响执行合法性。

Range

首轮可保持默认或按移动完成距离设置。

供 StateTree 读取距离语义。

Execution

添加内联 StateTree Stack Command Execution。

采用默认的 StateTree 后端来执行命令。

可在 Execution 的 StateTree 字段选择一个StateTree资产,该字段是完整 FStateTreeReference,可按 StateTree 的正常方式配置默认参数。

GAIS iamcu.cmd

3. 编写最小化的移动StateTree

新建一个StateTree,Schema选择StateTreeAIComponentSchema,并将资产命名为ST_Cmd_Move。

GAIS iamcu.cmd.tree

该 StateTree 非常简单,它不决定“要不要移动”。它只负责 CommandSystem 已验证的当前命令的实际执行:

1.通过StateTree context拿到自身 Pawn和Controller、

2.通过提供的BB(Blackboard) Evaluators获取接受命令期间写入到黑板组件的目标位置等信息。

3.Root节点具有Task:Timer任务,配合Duration参数,实现命令的执行超时。 4.将命令目标数据和移动参数,喂到原生的MoveToTask。

5.MoveToTask在抵达时回报成功;目标不可达、被阻挡或外部条件失败时回报失败。

GAIS iamcu.cmd.tree.timer

6.Timer超时,也会触发Transition回报成功。

例如守卫单位可在 Command StateTree 中执行:

plaintext
1读取当前 Location 命令
2→ 请求移动到位置
3→ 到达:完成 Succeeded
4→ 导航失败/目标无效:完成 Failed

Command 层结束后,StateTreeStack 会撤销这条命令对应的路由,守卫的低优先级巡逻 StateTree 可继续运行。不要让 Command StateTree 永远停留在运行状态,否则队列永远不会推进。

4. 构造并下发第一条命令

在服务端蓝图或权威 C++ 逻辑中,构造 Location 形式的 FGAIS_CommandData:Definition 指向 Cmd_Move,bHasLocation 为 true,Location 为点击点;Simple 命令的 Index 通常保持 -1。将它传给 Pawn 的 IssueCommand。

plaintext
1选择守卫 Pawn
2→ Get CommandSystemComponent
3→ Make Location Command Data(Move、目标位置、Index=-1)
4→ Issue Command
5→ 检查返回值为 Started

验证点:CurrentCommand 变为 Move,StateTreeStack 进入 Command 层,守卫开始移动。若返回 Rejected,先检查 TargetType 与载荷是否同为 Location、Execution 是否有有效 StateTree,再检查 Pawn 的 Controller/Stack 条件。

5. 验证排队与插队

继续使用同一个移动命令构造两个不同位置的请求。

  1. 当守卫正在移动时,对位置 B、位置 C 依次调用 EnqueueCommand。两次都应返回 Queued,GetCommandQueue 顺序为 B、C。
  2. 再构造“到医疗点”的位置请求,调用 InsertCommandAfterCurrentCommand。它不打断当前移动,但应出现在 B 之前。
  3. 构造“闪避到安全点”的位置请求,调用 InsertCommandBeforeCurrentCommand。因为 Move 是 Cancelable,当前移动会被取消,闪避立即开始;闪避成功后,被中断的移动才有机会继续。

这正是实时战术游戏中常见的交互:Shift 追加巡逻点,临时拾取/治疗放在下一步,爆炸警告则使用前插闪避。不要把所有高优先级命令都做成 IssueCommand;它会替换当前命令并清空已有队列,适合“新意图完全覆盖旧意图”的场景。

6. 验证取消与结果

移动途中调用 CancelCommand。只有当前命令为 Cancelable 时才会返回 true;随后 Command StateTree 收到取消,CurrentCommand 清空,最近结果显示 Canceled 和对应原因,低层守卫 AI 恢复。

观察这些接口,而不是从执行层私有状态猜测结果:

观察点

应看到什么

OnCommandChanged / GetCurrentCommandData

当前命令进入、替换或清空。

OnCommandQueueChanged / GetCommandQueue

排队、前插或清理后的顺序。

OnLastCommandResultChanged

最近一次成功、失败或取消及原因标签。

GameplayDebugger CommandSystem 面板

Current、Queue、Last、LastResult 与 ReasonTag。

ClearCommandQueue 只放弃等待项,适合玩家取消 Shift 路点但保留正在走的路;ClearCommands 还会尝试取消当前命令,因此会受 NonCancelable 保护。

MultiVariant:一个入口,多种 AI 行为

当“攻击”按钮需要根据选择或单位类型执行近战、远程和治疗性攻击时,创建 Multi Variant Command Definition,而不是复制三个几乎相同的 Simple 资产。为 Variants 的每项设置 DebugName、TargetType、Requirements、Display、Range 和 ExecutionDefinition;运行时 FGAIS_CommandData.Index 选择数组位置。

例如 Index 0 配置近战 Command StateTree,Index 1 配置远程 Command StateTree,Index 2 配置施法 StateTree。它们可以有不同射程、目标要求和 UI 显示。务必保持 Index 稳定:保存的命令引用、UI 和 Tactical Assignment 都用数组位置选择变体,不使用 DebugName 作为运行时身份。

什么时候不要用默认 StateTree 后端

默认 StateTree 后端通常足够:StateTree 可以组织移动、交互、动画、项目任务,也能在其中启动 Ability。只有当命令的主执行生命周期明确由其他运行时拥有时,才创建自定义 CommandExecutionDefinition,例如项目已有 Behavior Tree 任务系统,或一条命令应直接等待 GAS Ability 的完成回调。

此时仍然保留本篇的 Pawn、CommandSystem、下发和结果验证流程;变化仅在 Execution 字段选择你的适配器。自定义适配器必须在完成时回报给同一个 CommandSystem,并在取消时只清理这一次执行拥有的外部状态。

常见失败

症状

优先检查

组件无法正确使用或没有 CommandSystem

是否把组件添加在 Pawn/Character,而不是 Actor 或 Controller。

命令接受后立刻失败,Reason 是 StateTree context

Pawn 是否非玩家控制,是否由 AAIController 控制,Controller 是否有 StateTreeStack。

返回 Rejected

Definition、Index、TargetType 与载荷、CanObeyCommand、IsValidTarget、Execution 配置。

队列不继续

当前执行是否确实报告成功;失败通常不会推进依赖队列。

前插没有发生

当前命令是否为 NonCancelable;该入口会拒绝,而不是偷偷排队。

UI 显示已下达但服务端无变化

调用是否在权威端,或客户端是否真正拥有该 Pawn。