HTN Planner getting started
This chapter gets an NPC running HTN planning with the shortest possible path. The Gem ships a sample Domain, guard_domain (a guard that engages when an enemy is visible and patrols otherwise), which is the best starting point.
Prerequisites
- The
HtnPlannerGem is enabled in your project (check it in Project Manager, or addHtnPlannerto the Gem list in the project’sproject.json). After enablement the system entity automatically carriesHtnSystemComponent; no manual step needed. - AssetProcessor is running: it compiles sample and project
.htndomainsource assets into runtime.htndomain_compiledproducts.
Minimal loop (five steps)
1. Add the component to an entity
Select the AI entity in the Editor and add the HTN Agent component (runtime class HtnAgentComponent, editor class EditorHtnAgentComponent). Only one per entity—the component provides the exclusive HtnAgentService.
2. Assign the Domain asset
Set the Domain field in the component’s property panel to a .htndomain_compiled asset. Note that you pick the compiled product, not the .htndomain source—sources are edited in HTN Canvas and products come from AssetProcessor. Pick the guard_domain sample to start.
The remaining defaults are enough for the agent to run on its own:
| Field | Default | Purpose |
|---|---|---|
| Auto Start | true | Request the first plan automatically once the asset is ready |
| Continuous Mode | true | Replan after a plan completes so the AI keeps running |
| Root Task | empty | Use the Domain’s own root task |
| Opportunistic Replan | true | Recompute in the background when WorldState changes; preempt when strictly better |
See HTN Agent component for the full field reference.
3. Drive decisions with WorldState
The agent’s decisions are driven entirely by WorldState (a per-agent fact blackboard). The guard sample reads keys such as Guard.EnemyVisible and Guard.Ammo to choose between engaging and patrolling. Game code / sensor components write facts over HtnAgentRequestBus:
#include <HtnPlanner/HtnPlannerBus.h>
#include <HtnPlanner/HtnValue.h>
// When a sensor spots an enemy:
HtnPlanner::HtnAgentRequestBus::Event(
agentEntityId,
&HtnPlanner::HtnAgentRequests::SetWorldStateValue,
AZ::Crc32("Guard.EnemyVisible"),
HtnPlanner::HtnValue(true));
Lua / ScriptCanvas equivalent (BehaviorContext category HTN):
HtnSetWorldState(self.entityId, "Guard.EnemyVisible", true)
Key point: with Opportunistic Replan enabled, every WorldState write makes the agent re-evaluate whether the current plan is still optimal—this is how the AI reacts to a changing environment.
4. Enter game mode
Press Play. Because Auto Start = true, the agent immediately:
- Solves an ordered list of primitive tasks (the Plan) against the current WorldState;
- Executes operators one by one (the guard sample uses built-in
Wait/LogMessage); - Replans automatically once the plan completes (
Continuous Mode = true).
What happens inside the guard sample
The task decomposition tree of guard_domain (overview):
GuardDomain
└─ Root (compound task)
├─ Method "Engage" [Guard.EnemyVisible == true]
│ ├─ AlertPause (primitive, .htnnode preset Sample.AlertPause: a short pause)
│ └─ Attack (compound task)
│ ├─ Method "Shoot" [Guard.Ammo > 0]
│ │ └─ Fire (primitive, LogMessage; effect: Guard.Ammo decremented by 1)
│ └─ Method "Melee" (unconditional fallback)
│ └─ Melee (primitive, LogMessage)
└─ Method "Patrol" (unconditional fallback)
└─ Patrol (Domain Import ← patrol_common.htndomain)
The imported patrol_common provides the zero-parameter compound task Patrol: first GoToPost (built-in Wait, with an effect setting Patrol.AtPost), then Scan (LogMessage).
The planner tries methods in declaration order: when Guard.EnemyVisible is true it picks “Engage”, then checks Guard.Ammo to decide Fire vs. Melee. With no enemy it falls through to “Patrol”. All three primitive binding styles (C++ built-in operator via OperatorName, .htnnode preset via NodeConfigId, and Domain Import) are visible in this one sample.
Try it: in game mode, flip Guard.EnemyVisible to true in the ImGui panel (below); the agent immediately preempts the patrol plan and switches to engaging.
Verify it works
- Run
htn_dumpStatsin the console: you’ll see active agent count, decomposition steps, and solve/replan counters. - The guard sample’s
LogMessageoperator prints to the console—the most direct sign of life. - With the ImGui Gem enabled,
htn_imguiPanel trueopens the live panel showing the state machine, current plan, and all WorldState key/values (Bool/Int/Float/Vector3 are editable for what-if analysis).
Debug tooling is covered in Debugging.
Next steps
- Understand what just happened → Core concepts
- Fine-tune agent behavior → HTN Agent component
- Author your own Domain → Authoring Domains
- Add custom operators (open doors, fire weapons, move…) → Extending operators