Configuring a Tactical Assignment Plan
This guide builds a minimal closed loop: a StateTree Task gathers candidate Units and targets from fixed-array Sources, each Unit resolves GAIS.CommandIntent.Engage into a concrete Command, and the system selects and submits at least one assignment. When complete, the Task's AssignmentCount is greater than zero and the candidate Unit's CommandSystem shows the resolved concrete command.
The first pass uses fixed arrays and intentionally excludes EQS, squad adapters, and custom Allocators. This keeps input validity, command executability, and capacity constraints independently testable.
Prerequisites
- A StateTree driven by an
AAIController. - At least one candidate Pawn/Character with
UGAIS_CommandSystemComponenton the Pawn and the ability to execute the target Command. UGAIS_UnitComponenton the candidate Actor, or an implementation ofIGAIS_CommandIntentResolveron the Actor or one of its components.- A Command Definition that can already be issued manually and accepts the test target.
- A test target Actor and an existing
GAIS.CommandIntent.*tag in the project.
Tactical Assignment does not bypass CanObeyCommand or IsValidTarget. Confirm that the command works when issued manually first; otherwise an automatic-assignment failure cannot be isolated to either command configuration or planning.
The Task starts on Authority and derives ContextPawn from the AIController's current possession. An invalid Controller, missing Authority, changed Pawn possession, a Plan that cannot be resolved uniquely, or a Plan with no Batch causes the Task to return Failed immediately.
1. Configure the Unit's Intent Mapping
Add an entry to UGAIS_UnitComponent.CommandIntentMappings:
Field | Example | Requirement |
|---|---|---|
| | Must exactly match the Batch. |
| The project's attack or support command | Must be a valid Definition executable by this Unit. |
| Default value or a valid variant index | Required only when selecting a specific MultiVariant Command variant. |
The default UnitComponent resolves intents from this table. Implement a custom Resolver only when gameplay must choose commands dynamically from equipment, state, or target source. Do not add that complexity to a Source or Allocator during minimal validation.
2. Decide Between a Data Asset and an Inline Plan
Both entry points use the same UGAIS_TacticalAssignmentPlan type and produce the same runtime behavior. Choose based on reuse and ownership.
Situation | Choose | Reason |
|---|---|---|
Multiple squad StateTrees, states, or levels share the same Batch rules | | Centralized maintenance; each Task can still bind different Sources. |
Designers need to review, validate, and tune the Combat Plan independently | | Clear asset boundary and shared updates. |
One state needs a quick, single-Batch validation | | No additional asset; configuration stays beside the call site. |
The rule is valid only in that state and reuse would be misleading | | Keeps the local rule with its StateTree branch. |
Do not fill both Plan and Inline Plan, and do not leave both empty. The Task enters Failed when it cannot resolve exactly one Plan. Neither form stores Sources: Sources always belong to the Task's AssigneeSources / TargetSources, allowing one shared Data Asset to run against different squads.
The following example uses a Data Asset. When using Inline Plan, configure the same Batch fields under the Task's Inline Plan.
3. Create the Plan
Create a UGAIS_TacticalAssignmentPlan Data Asset and start with one Batch:
Field | Minimal setting |
|---|---|
| One Batch. |
| For example |
| Same tag as the Unit mapping. |
| Empty. |
| Default multiplier |
| Empty, using the default capacity-aware greedy allocator. |
| A positive finite value; the default is suitable for first integration. |
The Plan needs at least one Batch, every Batch needs a valid IntentTag, and the reevaluation interval must be positive and finite. These are directly validated Data Asset settings.
4. Configure Top-Level Sources on the StateTree Task
Add Task: Run Tactical Assignment Plan (FGAIS_STT_RunTacticalAssignmentPlan) to the target StateTree state. On the Task:
- Bind
AIControllerto the current AIController context. - Set exactly one of
PlanandInline Plan; this example assigns the Data Asset toPlan. - Add Actor Array to
AssigneeSourcesand provide the test Unit Actor. - Add Actor Targets to
TargetSourcesand provide the test target Actor. SetSourceTagwhen source identity must be distinguished.
Sources belong to the Task, not to Batches. Every Source fills a temporary array independently. The Task merges valid outputs into candidate streams, which the Plan's ordered Batches then consume.
1StateTree Task2 ├─ Plan or Inline Plan (exactly one)3 ├─ AssigneeSources: Actor Array4 └─ TargetSources: Actor Targets5 ↓6Plan Batch: IntentTag, Filters, ScoreAdjustment, Allocator
Using Task Inputs and Outputs in StateTree Branches
Field | Configuration | StateTree use |
|---|---|---|
| Bind current AIController context. | Determines Authority, Context Pawn, and unique Session owner. |
| Choose exactly one. | Supplies Batches and reevaluation interval. |
| Configure inline or prepare runtime Sources through task data. | Gathers all Actors that may receive Commands. |
| Configure inline or prepare runtime Sources through task data. | Gathers Actor, Location, or Actor+Location targets. |
| Read-only output. | Shows how many evaluations the Session has completed. |
| Read-only output. | Distinguishes |
| Read-only output. | Shows how many assignments the latest completed evaluation retains. |
| Edge event. | Drives a transition on the first assignment or a zero-to-nonzero recovery. |
| Edge event. | Drives fallback on the first completed zero result or a nonzero-to-zero transition. |
After entering the state, the Task remains Running. Its StateTree Scheduled Tick drives timed reevaluation. On state exit, the Task cancels scheduling and stops the Session; matching Commands and reservations are cleaned up.
5. Enter the State and Validate
The Task evaluates immediately on state entry. Observe in this order:
LastUpdateResultisCompleted. A brief world-update conflict may reportDeferredand retry later.ReplanCountincreases after a completed evaluation, andAssignmentCountis greater than zero.- The candidate Unit's CommandSystem receives the concrete Command resolved from IntentTag.
- In GameplayDebugger, Actor pool, Target pool, Candidates, Selected, and final Outcome match expectations.
OnAssignmentsAvailable fires when assignments first become available and whenever the state recovers from zero assignments to a nonzero count. It does not repeatedly broadcast while assignments remain available. When a world-update guard conflict produces Deferred, the Task remains running and retries later.
6. Use SourceTag to Mark Target Origin
When one Task gathers multiple target categories, use child tags under GAIS.TacticalAssignment.TargetSource.*. For example:
Target Source | | Output |
|---|---|---|
EQS Actor Targets | | Enemy Actors found by perception or spatial queries. |
Target Data Array / project Source | | Defensive positions supplied by the objective system. |
Project Source | | A temporary player-marked target. |
SourceTag is the Target Source's default origin tag and is written only when target data has no tag. An item in Target Data Array that already carries a tag keeps its own tag.
Then add Target Source Tag to the Batch's TargetFilters:
- Attack Batch: allow
...Perception.Enemyand...Player.PinginAllowedSourceTags. - Hold Batch: allow only
...Objective.DefensePoint. - Put permanently forbidden origins in
BlockedSourceTags. Blocked is evaluated before Allowed.
This does not create a separate capacity reservation. The same enemy Actor entering once as perception and once as a player ping still shares one world-capacity bucket based on Actor identity.
7. Use Filters So One Source Set Can Serve Multiple Batches
Sources should represent stable data origins; Batch Filters should represent current tactical rules. For a combat squad:
- Add one All Squad Units Assignee Source and explicitly bind the current Squad Actor.
- Add an enemy EQS Source and a defense-position Source to TargetSources, with different
SourceTagvalues. - Add ordered Batches:
EngagePriority: Assignee Filter selects combat roles; Target Filters require Hostile, Has Actor, and enemy origin.HoldPoints: selects guards not claimed by the prior Batch; Target Filters require No Actor and defense-position origin.SupportRemainder: selects healer/support roles from remaining members; Target Filters accept friendly Actor targets.
Every Filter in an array must pass. Assignee Filters prune unsuitable Units before intent resolution. Target Filters prune unsuitable targets before command IsValidTarget. CommandDefinition remains authoritative for final executor and target legality.
Use GameplayDebugger's Actors before -> after and Targets before -> after per Batch:
- before is already 0: inspect the Source.
- before has values but after is 0: inspect that Batch's Filters.
- after has values but result is
NoResolvedCommand: inspect Intent mappings. - Candidates has values but Selected is 0: inspect capacity and Allocator behavior.
8. Handle Empty Source Output Correctly
The current Source API writes to output arrays and has no runtime success-state return. If EQS produces no result for the current evaluation, a project query temporarily returns an empty set, or a Source output is invalid, that Source contributes zero candidates for the evaluation. Other Sources still contribute, and the evaluation completes normally.
An empty Source output therefore participates in a normal completed evaluation. If candidates disappear from the merged stream, the system reconciles previous assignments and moves no-longer-valid records into Stale cleanup. Deferred specifically indicates a world-update conflict or synchronous reentry; tracked assignments and reservations remain unchanged in that case.
Configuration errors should be fixed rather than awaited: a null Source object, EQS without QueryTemplate, or missing required RoleTag / SquadActor fails validation.
9. Add Real Gameplay Rules One Layer at a Time
After the minimal loop works:
- Replace fixed arrays with squad, EQS, or project-container Sources.
- Add Assignee Filters and verify Actor-pool before/after counts.
- Add Target Filters and verify
SourceTag, team attitude, and target shape. - Tune
ScoreAdjustmentand observe whether candidate order matches design. - Create a custom Allocator only when the default is insufficient. It receives already validated candidates and a world-capacity snapshot; it should not rediscover targets or repeat command validation.
10. Organize Idle / Combat Squad Branches
Place the Task in the StateTree state that owns the tactical semantics. Idle and Combat branches commonly hold separate Tasks:
State | Top-level Sources | Plan Batch | Result |
|---|---|---|---|
Idle | All Squad Units; targets omitted or formation/job locations supplied. | Inline Plan: | Local rules stay with Idle; members follow, occupy positions, or guard. |
Combat | All Squad Units; enemy EQS, defense positions, and friendly Target Sources. | Data Asset: | Reusable combat rules assign different members to attack, hold, or support. |
On entering Combat, the Task creates a Session and evaluates immediately. It reevaluates at ReevaluationInterval while the state remains active and stops the Session on exit. Different mappings for the same Intent create role-specific behavior: a front-line Unit resolves Engage to melee, a ranged Unit resolves it to shooting, and a healer participates only in Support. Intent Resolver is the seam where a Unit turns tactical semantics into an executable command.
Complete Six-Member Squad Example
Assume two melee units, one ranged unit, one healer, and two guards.
Assignee Sources
- All Squad Units:
SquadOverridepoints to the current Squad Actor and returns six members. This Source requires an explicit Squad Actor. - If Context Pawn itself has SquadComponent, Squad Units By Role may also be used. With no
SquadOverride, it attempts Context Pawn;RoleTagmust be valid and uses exact matching.
Target Sources
- EQS Actor Targets: queries current enemies, with
SourceTag = GAIS.TacticalAssignment.TargetSource.Perception.Enemy. - EQS Location Targets: queries defensible positions, with
SourceTag = GAIS.TacticalAssignment.TargetSource.Defense.Position. - All Squad Units: exposes squad members as friendly targets for the healing Batch.
Plan Batches
AttackPriorityIntentTag = Engage- Assignee Filter: Role Query accepts melee and ranged roles.
- Target Filters: Team Attitude = Hostile, Has Actor, SourceTag = Enemy.
- Result: three combat members receive enemy targets first.
HealAlliesIntentTag = Support- Assignee Filter: Role Query accepts only the healer.
- Target Filters: Has Actor, ally tags or Friendly attitude.
- Result: the healer receives a friendly target from members not claimed earlier.
HoldDefensePointsIntentTag = HoldPosition- Assignee Filter: Role Query accepts guards.
- Target Filters: No Actor, SourceTag = Defense.Position.
- Result: two guards compete for location targets, usually with capacity one per location.
Batch order claims Units. If the healer also passes the Attack Assignee Filter, it is claimed by the first Batch and cannot be used by HealAllies. Role Filters and Batch order jointly decide who acts first. Target capacity decides how many Units can share one target.
Idle may use a short Inline Plan with one FollowSquad Batch. When Follow uses a None or Passive TargetType, TargetSources may remain empty. Entering Combat stops the Idle Session, creates a Combat Session, and issues combat Commands.
11. Validate Real Assignments with the Panel and Logs
Run PIE, open GameplayDebugger, enable the GenericAISystem category, select a Pawn controlled by AIController, and press Shift+7 to switch to Tactical Assignment. Inspect Source counts and Actor/Target pools first, then each Batch's filter before/after counts, Candidate/Selected values, external target usage, and assignment records. This identifies whether failure occurs during gathering, intent resolution, command validation, capacity, or submission more reliably than watching whether the Unit moves.
For Session start/stop, replanning, and submission diagnostics, temporarily run Log LogGAIS_Tactical Verbose. GameplayDebugger shows the current Session/Batch snapshot. Per-Source Actor/Target details are currently emitted through Visual Logger UE_VLOG, not expanded item by item in ordinary text logs. Restore normal logging with Log LogGAIS_Tactical Log. To persist lifecycle text logging across launches, set LogGAIS_Tactical=Verbose under project [Core.Log].
Common Failures
Symptom | Check first |
|---|---|
Task immediately enters | Is AIController valid; is exactly one of Plan/Inline Plan set; are deterministic Plan/Source settings complete? |
| Does Batch IntentTag match the Unit mapping, and are Resolver and Command Definition valid? |
| Executor requirements, resources, and project state of the concrete Command. |
| The resolved Command must use |
| Does Source output match Command TargetType, and are Target Filters too strict? |
Candidates has values but Selected is zero | World-capacity snapshot, existing claims, scoring, and Allocator selection. |
One Source has no output | Inspect that Source's inputs and whether other Sources still contribute; evaluation continues with the actual merged result. |
None / Passive Commands do not require a Target Source and use local empty target data. Do not add fake targets to shared TargetSources, because other Batches in the same Task may require a real target stream.
