Integrating a Minimal Commandable Unit

GAIS_Cover
YuewuDev

Author

YuewuDev

Created: Oct 12, 2026Updated: Oct 12, 2026

Table of contents

Integrating a Minimal Commandable Unit

This guide builds a verifiable AI-unit loop: a guard Pawn normally runs its own AI, enters a Command StateTree after receiving a move command, accepts two appended waypoints, executes an inserted emergency dodge, and finally cancels the command and resumes its previous behavior. The scenario validates the Pawn-only component constraint, execution routing, FIFO queuing, and interruption boundaries.

Related documentation: first read Command System, see Command Definition and Dispatch Parameters for fields, and read Command Extension Points when a custom backend is required.

Prerequisites

Prepare a Pawn or Character controlled by an AAIController, and confirm that it is not player-controlled. The default StateTree execution layer rejects player-controlled Pawns, Pawns without an AIController, and Controllers without StateTreeStack.

  1. GenericAISystem and the StateTree / GameplayStateTree dependencies are enabled.
  2. The Pawn's Controller has UGAIS_StateTreeStackComponent and a usable Command layer.
  3. The project Blackboard retains the default command Keys, or UGAIS_DeveloperSettings and the StateTree readers have been updated together.
  4. Command-dispatch logic runs on the server; when initiated by a client that owns the Pawn, use the corresponding Server... RPC.

UGAIS_CommandSystemComponent must be added to the Pawn itself, not to the AIController, a regular Actor, or a building Actor. If the current test object is a regular Actor, convert it to a Pawn/Character before continuing. Merely adding the component to an Actor in Blueprint is not a supported configuration.

1. Add Components to the AI Pawn

Add Command System Component (UGAIS_CommandSystemComponent) to the guard Pawn Blueprint. Do not add it to the Controller Blueprint. If the unit will later participate in squads or Tactical Assignment, also add UGAIS_UnitComponent; this tutorial does not depend on it.

Run PIE and obtain the Pawn's Get CommandSystemComponent on the server. Initially, IsIdle should be true, GetCurrentCommandData should be empty, and the queue should be empty. This immediately reveals a component attached to the wrong object or an initialization failure.

2. Create a Minimal Move Command

Create a Blueprint derived from Simple Command Definition and name it Cmd_Move. Simple is appropriate when one asset represents one stable command. Movement, interaction, healing, and ordinary attacks usually start here.

Configure the asset as follows:

Group

Suggested configuration

Purpose

Common

Set TargetType to Location and cancellation policy to Cancelable.

Allows location requests and lets a new move or dodge interrupt the current one.

Display

Provide a name, icon, and optional button slot.

Used by HUD or command UI; does not affect execution validity.

Range

Keep defaults for the first pass or set a movement-completion distance.

Supplies distance semantics to StateTree.

Execution

Add inline StateTree Stack Command Execution.

Uses the default StateTree backend.

Select a StateTree asset in the Execution StateTree field. The field is a full FStateTreeReference, so normal StateTree parameters can be configured on it.

GAIS iamcu.cmd

3. Build a Minimal Move StateTree

Create a StateTree using StateTreeAIComponentSchema and name it ST_Cmd_Move.

GAIS iamcu.cmd.tree

This StateTree does not decide whether the unit should move. It executes the current command that CommandSystem has already validated:

  1. Obtain the Pawn and Controller from StateTree context.
  2. Use the provided BB (Blackboard) Evaluators to read the target location and other data written while the command is accepted.
  3. Add Task: Timer on the Root and use its Duration as the command timeout.
  4. Feed command target data and movement parameters into the native MoveTo Task.
  5. Report success when MoveTo reaches the destination; report failure when the target is unreachable, blocked, or another condition fails.
GAIS iamcu.cmd.tree.timer
  1. A Timer timeout may also trigger a Transition that reports success.

Example flow:

plaintext
1Read current Location command
2→ Request movement to location
3→ Reached: complete Succeeded
4→ Navigation failure / invalid target: complete Failed

When the Command layer ends, StateTreeStack removes the route belonging to this command, allowing the guard's lower-priority patrol StateTree to continue. Do not leave the Command StateTree running forever, or the queue will never advance.

4. Construct and Issue the First Command

In server Blueprint or authoritative C++ logic, create a Location-form FGAIS_CommandData: Definition points to Cmd_Move, bHasLocation is true, Location is the clicked point, and a Simple command normally keeps Index = -1. Pass it to the Pawn's IssueCommand.

plaintext
1Select guard Pawn
2→ Get CommandSystemComponent
3→ Make Location Command Data (Move, target location, Index=-1)
4→ Issue Command
5→ Verify return value is Started

Expected result: CurrentCommand becomes Move, StateTreeStack enters the Command layer, and the guard begins moving. If the result is Rejected, first confirm that TargetType and payload are both Location, that Execution has a valid StateTree, and then check the Pawn Controller/Stack requirements.

5. Verify Queuing and Insertion

Create two requests using the same move command but different positions.

  1. While the guard is moving, call EnqueueCommand for positions B and C. Both should return Queued, and GetCommandQueue should list B then C.
  2. Create a location request for a medical point and call InsertCommandAfterCurrentCommand. It does not interrupt the current move but should appear before B.
  3. Create a request to dodge to a safe point and call InsertCommandBeforeCurrentCommand. Because Move is Cancelable, the current move is canceled and dodge begins immediately. Only after dodge ends with a success-class result can the interrupted move be resumed.

This matches common real-time tactical interactions: Shift appends patrol points, a temporary pickup or heal becomes the next action, and an explosion warning inserts an urgent dodge. Do not implement every high-priority request with IssueCommand; it replaces the current command and clears the existing queue, which is appropriate only when the new intent completely supersedes the old one.

6. Verify Cancellation and Results

Call CancelCommand while moving. It returns true only when the current command is Cancelable. The Command StateTree then receives cancellation, CurrentCommand clears, the latest result reports Canceled with its reason, and the lower guard AI resumes.

Observe these stable interfaces rather than inferring results from private execution state:

Observation point

Expected result

OnCommandChanged / GetCurrentCommandData

Current command enters, is replaced, or clears.

OnCommandQueueChanged / GetCommandQueue

Order after queuing, insertion, or clearing.

OnLastCommandResultChanged

Latest success, failure, or cancellation and its reason tag.

GameplayDebugger CommandSystem panel

Current, Queue, Last, LastResult, and ReasonTag.

ClearCommandQueue discards only waiting entries, which is useful when canceling Shift waypoints while keeping the current movement. ClearCommands also attempts to cancel the current command and is therefore blocked by NonCancelable.

MultiVariant: One Entry Point, Multiple AI Behaviors

When one Attack button must select melee, ranged, or healing behavior, create a Multi Variant Command Definition rather than three nearly identical Simple assets. Configure DebugName, TargetType, Requirements, Display, Range, and ExecutionDefinition for each item in Variants; FGAIS_CommandData.Index selects the array position at runtime.

For example, Index 0 can use a melee Command StateTree, Index 1 a ranged StateTree, and Index 2 a casting StateTree. They may use different ranges, target requirements, and UI. Keep Index stable: saved command references, UI, and Tactical Assignment select variants by array position, not DebugName.

When Not to Use the Default StateTree Backend

The default StateTree backend is usually sufficient. StateTree can coordinate movement, interaction, animation, project tasks, and Ability activation. Create a custom CommandExecutionDefinition only when the command's main lifecycle is clearly owned by another runtime, such as an existing Behavior Tree task system or a command that must directly await a GAS Ability completion callback.

The Pawn, CommandSystem, dispatch, and result-validation flow remains the same; only the Execution field changes to the custom adapter. The adapter must report completion to the same CommandSystem and, on cancellation, release only external state owned by that exact execution.

Common Failures

Symptom

Check first

Component cannot be used correctly or CommandSystem is missing

Was it added to Pawn/Character rather than Actor or Controller?

Command is accepted then immediately fails with a StateTree context reason

Is the Pawn non-player-controlled, controlled by AAIController, and does the Controller have StateTreeStack?

Returns Rejected

Definition, Index, TargetType and payload, CanObeyCommand, IsValidTarget, and Execution configuration.

Queue does not continue

Did the current execution actually report success? Failure normally does not advance dependent work.

Insert-before does not occur

Is the current command NonCancelable? The API rejects instead of silently queuing.

UI says issued but server state does not change

Did the call run on authority, or does the client actually own the Pawn?