命令系统
GenericAISystem 的 Command System 是“把已经决定的行为交给单位执行”的统一桥梁。玩家点击、UI 快捷键、关卡脚本、小队指令和上层 AI 都可以提出请求;命令系统负责在 Pawn 上验证请求、维护当前命令与等待队列、处理替换和取消,并把被接受的命令交给可适配的执行层。它不负责替你决定战略或战术:Tactical Assignment、StateTree 决策或项目逻辑先决定“应该做什么”,Command System 再保证“这一次具体命令如何可靠地开始、结束或被中断”。
这使玩家交互与 AI 调度走向同一条落地路径:上层只产生命令请求,单位侧只观察同一份当前状态和结果,执行后端可以随项目演进而替换。
这个系统解决什么问题
游戏里同一个单位常同时面对玩家输入、UI 快捷操作、关卡脚本、小队指令和 AI 决策。如果每个来源都直接调用移动、Ability、动画或行为树,就会各自维护目标、打断、排队、网络权限和完成回调:玩家取消移动可能留下一条 Ability,AI 抢占行为可能不通知 UI,旧异步回调甚至可能结束新动作。
CommandSystem 把这些“已明确的行为请求”收束为统一合同:谁执行、执行什么、目标是什么、是否允许、能否被打断、完成后结果如何,都由 Pawn 上同一条命令生命周期处理。它让上层系统无需关心具体后端,也让 UI、调试器与网络观察同一份状态。
这个系统能做什么
能力 | 典型用途 |
|---|---|
统一玩家与 AI 调度 | 玩家点击移动、技能按钮、小队命令、Tactical Assignment 的结果都通过同一入口落到单位。 |
校验明确请求 | 按命令定义检查执行者状态、目标形态、标签需求、变体 Index 与执行层是否有效。 |
管理当前命令与队列 | 支持最新意图替换、Shift 路点、连续交互与任务顺序展示。 |
处理高优先级打断 | 用前插命令实现闪避、紧急治疗、临时反应;按取消策略保护不可打断行为。 |
连接可替换执行后端 | 默认进入 StateTree Command 层,也可适配 GAS Ability、Behavior Tree、动画或项目运行时。 |
提供可复制观察结果 | UI、GameplayDebugger 和项目逻辑可读取当前命令、队列、最近终结结果与原因。 |
例如一支玩家可指挥小队平时由 Squad AI 驱动巡逻:玩家点击目标位置时下发 Move,当前命令替换可取消巡逻动作;使用追加操作时加入后续路点;遇到危险时前插 Dodge;命令完成后 StateTree Stack 回到 Squad 行为。整个过程不需要 UI、AI 和移动后端各自维护另一套“当前命令”。
什么时候不该使用它
CommandSystem 不负责从大量候选 Unit 与目标中决定战术分配,那应使用 Tactical Assignment;不负责阵型槽位和持续移动建议,那属于 Squad、Formation 与 Movement Intent;也不替代项目的实际移动、伤害、冷却或动画实现。它的边界是:上层已经知道“让这一个 Pawn 做这件明确的事”,需要可靠地提交、观察和终结它。
先记住:组件只属于 Pawn
UGAIS_CommandSystemComponent 继承 UPawnComponent,只能添加到 Pawn 或 Character,不能挂在普通 Actor、建筑 Actor、AIController 或 PlayerController 上。建筑、炮塔或其他非 Pawn 单位若需要接受命令,应由项目提供对应的 Pawn 表达,或在项目侧另建适合它们的命令入口;不要把 CommandSystem 强行当作通用 Actor 组件。
组件可以存在于玩家控制 Pawn 或 AI Pawn 上,但内置的 StateTree 执行层有更严格的运行条件:它要求被命令 Pawn 是非玩家控制、由 AAIController 控制,并且该 Controller 上存在 UGAIS_StateTreeStackComponent。这不是 CommandSystem 的通用限制;它只说明默认 StateTree 后端适合 AI 单位。玩家 Pawn 可以使用其他执行层,或由项目把命令转交给自己的角色/Ability 逻辑。
它在整个系统中的位置
CommandSystem 是生命周期权威:它分配本次执行身份、发布可观察状态,并只接受仍属于当前执行的完成回报。执行层负责做实际工作和在正确时间报告成功、失败或取消。StateTree Stack 管的是行为层优先级;Queue 管的是已决定任务的运行序列;它们不替代 CommandSystem 的命令账本。
一条命令由哪些部分组成
层次 | 作用 | 常见使用者 |
|---|---|---|
| 命令的公开契约:目标规则、可执行性、评分、UI 数据和执行配置。 | 蓝图资产、C++ 命令类、Tactical Assignment。 |
| 这一次请求:Definition、变体 | 输入、UI、脚本、网络 RPC。 |
| Pawn 上的权威入口与可复制状态投影。 | 单位、UI、观察者、调试器。 |
| 无状态执行适配器;把精确命令执行送到具体框架。 | StateTree、GAS、Behavior Tree、项目组件。 |
组件复制的是当前命令、等待队列和最近一次终结结果,适合 UI 与观察者读取;它不是完整历史记录,也不是执行后端的运行时存储。
先选对命令定义
绝大多数项目应从内置的两种可配置父类创建 Blueprint 或 C++ 子类,而不是一开始继承裸 UGAIS_CommandDefinition。
选择 | 适合什么 | 主要价值 |
|---|---|---|
| 一个资产主要代表一个稳定行为,例如移动、交互、治疗或普通攻击。 | 在一份默认配置中提供 Common、Requirements、Display、Execution、距离和组目标设置;通常忽略 |
| 一个玩家入口或战术语义下有多种稳定变体,例如 Attack 的近战、远程和技能攻击。 | |
直接继承 | 命令配置必须依赖执行 Pawn、装备、职业、运行时状态或项目规则动态解析。 | 可按 Actor 与 Index 覆写片段、目标校验、评分和执行层选择。 |
MultiVariant 不只是“同一命令换图标”。同一命令的不同变体可以各自进入不同的 StateTree,甚至完全驱动不同执行后端;例如近战攻击进入贴身 StateTree,施法攻击启动 GAS Ability,逃跑变体交给项目的 Behavior Tree 适配器。
只有在静态资产字段无法表达规则时,才从 UGAIS_CommandDefinition 自行构建:例如同一“攻击”命令必须根据武器、职业或 Buff 动态返回不同 TargetType、Requirement、Range、Display 或 ExecutionDefinition。不要为了保存少量不同数值而复制整套动态逻辑;优先使用 Simple 或 MultiVariant 的编辑器配置。
从请求到执行
下发前,CommandSystem 会核对 Definition、变体 Index、目标形态、执行 Pawn 是否能执行、目标是否有效,以及能否解析到有效执行层。请求被接受后,组件提交新的当前状态,再由执行定义启动这一次精确执行;执行层最终以成功、失败或取消回报给 CommandSystem。
目标数据必须与命令的 TargetType 对齐:无目标/Passive 不应塞入目标,Actor 命令需要 Actor,Location 和 Direction 命令需要显式位置。错误的目标形态不是由执行层“猜测修正”的问题,而是请求在进入执行前就应被拒绝的契约错误。
立即下达、排队与插队
以玩家可直接指挥的单位为例:点击新位置移动通常应立即替换当前可取消移动;追加路点应排队;发现脚下即将爆炸时的闪避可以插到当前动作前;补药或一次拾取则可插到当前攻击之后。
入口 | 行为 | 适合案例 |
|---|---|---|
| 空闲时立即开始;当前命令可取消时替换它并清空等待项;当前命令不可取消时,成为唯一等待替换项。 | 玩家最新的移动/攻击意图,或脚本要求立刻切换工作。 |
| 空闲时立即开始;否则追加到 FIFO 队尾。 | Shift 路点、巡逻、连续采集。 |
| 保持当前命令,将新命令放到队首,成为下一条。 | 攻击收尾后立刻拾取,再继续原队列。 |
| 仅当前命令可取消时中断它,先执行新命令;新命令以成功类结果结束后才尝试恢复被中断命令。 | 紧急闪避、战术打断、必须先响应的交互。 |
“下发”不等于永远立即开始:返回 Queued 表示请求被接受但在等待;Rejected 表示校验或当前生命周期条件未通过;NoChange 表示请求与当前状态相同。UI 应根据返回值和可复制状态更新提示,而不要只凭按键已触发就显示“已执行”。
打断、取消与队列推进
每个命令通过 CancellationPolicy 声明自己能否被替换。Cancelable 的当前命令可以被替换、前插或 CancelCommand 停止;NonCancelable 会阻止这些打断,普通 IssueCommand 在这种情况下只保留最新的等待替换请求。ClearCommandQueue 只删后续项;ClearCommands 会尝试取消当前命令并清空后续项,因此也受当前命令的取消策略约束。
队列只在当前命令以成功类结果结束时继续推进。失败通常会终止当前链路并清掉等待项;取消是否可视为可继续,由命令的成功判定决定。这个边界可避免“攻击失败后仍盲目执行后续依赖动作”,也让设计者能明确选择“取消后继续巡逻”还是“取消后停止”。
取消并不是只修改 UI 状态。CommandSystem 会让当前执行层收到取消通知;执行层必须只释放仍由这次精确执行拥有的 Ability、StateTree 路由、移动任务或视觉状态,不能误伤已经替换进来的新命令。
默认执行层:StateTree Stack Command Execution
UGAIS_CommandExecutionDefinition_StateTree 是默认的 UGAIS_CommandExecutionDefinition。它持有完整的 FStateTreeReference,因此不仅能指定 StateTree 资产,也能保存该引用支持的参数和属性覆写。命令开始时,它把执行请求送入 StateTreeStack 的保留 Command 层;命令结束或取消时,Stack 会撤销对应的精确路由,让较低层的 Unit 或 Squad AI 恢复控制权。
这通常已经足够覆盖大多数 AI 命令:在 StateTree 中读取命令 Definition、变体、目标、范围与自身 Pawn,完成移动、交互、攻击判断,必要时启动 Ability,再把终结结果回报给 CommandSystem。不要因为 StateTree 能调用 Ability 就误以为必须额外创建 GAS 执行层;当 Ability 只是该 StateTree 流程的一步时,保留默认后端更容易维护。
Simple 命令可直接配置一个 StateTree 引用;MultiVariant 的每个变体可配置自己的引用。于是“近战攻击 / 远程攻击 / 治疗技能”既可共享一个命令入口,也可执行完全不同的 StateTree 工作流。
何时需要自定义执行层
当命令的主生命周期不应由 StateTreeStack 持有时,继承 UGAIS_CommandExecutionDefinition:例如项目已有 Behavior Tree 任务运行器、一次命令应直接启动并等待 GAS Ability、或需要对接独立的动画/交互系统。自定义执行层应保持为无状态适配器:StartExecution 建立这一次执行的异步路由或同步完成,CancelExecution 只停止这次执行拥有的外部状态,最终通过 CommandSystem 的完成入口报告结果。
它不是放置长期决策、共享缓存或每次执行临时变量的地方。复杂 AI 的“何时选择命令”仍属于 StateTree、Behavior Tree、Tactical Assignment 或项目决策系统;执行定义只处理“已接受命令怎样交给该后端执行”。具体扩展边界见命令扩展点。
网络与观察
命令状态由服务端维护。服务端可直接调用下达、排队、插队、取消和清理入口;拥有该 Pawn 的客户端可以调用对应 Server... RPC。没有该 Pawn 网络所有权的客户端不能把这些 RPC 当作通用远程调用,应通过 PlayerController 或项目网络入口把请求交给权威逻辑。
客户端 UI、GameplayDebugger 和项目观察者应读取 CurrentCommand、队列、最近结果,以及 OnCommandChanged、OnCommandQueueChanged、OnLastCommandResultChanged。这些是稳定的观察接口;不要依赖执行层内部任务来判断命令是否已完成。
