Authoring Domains (HTN Canvas)
A Domain is the AI’s task-decomposition knowledge graph. This chapter covers editing it visually with HTN Canvas, the source and compiled asset formats, and the compile-time V1–V18 validation.
Asset shapes: source → product
- Source asset
.htndomain: the text ObjectStream that HTN Canvas edits and saves (a node graph + document metadata). Text was chosen so version-control diffs stay readable. - Compiled product
.htndomain_compiled: the binary asset the AssetBuilder emits after flattening and indexing the graph. Read-only at runtime, shared zero-copy by all agents. HtnAgentComponentreferences the product, not the source—the most common newcomer mix-up.
Building graphs with HTN Canvas
HTN Canvas is a standalone executable tool (not an in-Editor dock). Launch it via:
- Editor menu Tools → HTN Canvas (spawns the co-located
HtnCanvasas a separate process); - The Open in HTN Canvas button on
EditorHtnAgentComponent(resolves the compiled Domain back to its.htndomainsource and opens it).
Graph-building steps
Follow the guard_domain.htndomain sample:
- Drag out a Domain Root from the node palette (exactly one per graph) and connect its
Rootslot to the root compound task (the root reference takes no parameters). - Drag out a Compound Task, name it in the inspector (e.g.
Root), and connect it to the Domain Root. - Drag Method nodes into the compound’s ordered
Methodsslots—child slot index = method priority (right-click a slot to move it up/down). Edit conditions row by row in the inspector (key | operator | operand). - Drag Primitive Task nodes into a method’s ordered
Subtasksslots—child slot index = execution order. Binding options below. - Declare every key referenced by conditions/effects with a WorldState Key node (name + type + default). Check Externally Written on keys written only by game code to silence the V16 warning (the checkbox does not create a sensor for you).
- Cross-domain reuse: use a Domain Import node to reference a zero-parameter compound task in another
.htndomain(the sample importsPatrolfrompatrol_common.htndomain). Import paths are project-relative.
Other node types: Predicate (C++ predicate conditions), Task Ref (reference a task in the same graph), Constant / Param Ref (operands), Macro (.htnnode macros).
The two primitive binding fields
Primitive nodes carry two mutually exclusive fields; the sample demonstrates all binding styles:
| Binding | OperatorName | NodeConfigId |
|---|---|---|
| C++ built-in/registered operator | non-empty (e.g. LogMessage, Wait) | empty |
.htnnode preset/macro | empty | non-empty (e.g. Sample.AlertPause, Common.ShortWait) |
How to provide these operators is covered in Extending operators.
Live validation
The bottom Validation panel shows V1–V18 errors/warnings live (Canvas runs the same checks as the builder without writing the runtime product). Double-click an entry with a node id to locate and center that node.
Saving and compilation
Save as <name>.htndomain under the project asset tree; AssetProcessor compiles <name>.htndomain_compiled automatically. The builder loads the source document, builds the node registry (C++ descriptors + a scan of *.htnnode), runs V1–V18 validation plus flattening, and registers dependencies for Domain Imports (guaranteeing imported domains compile first). Recompiling while the game runs triggers agent hot reload (see
HTN Agent component).
Validation rules V1–V18
| Rule | Severity | Meaning |
|---|---|---|
| V1 | Error | Exactly one Domain Root; root connected; the root task reference takes no parameters |
| V2 | Error | Slot connection types are legal; compound tasks have at least one method |
| V3 | Error | Static cycle in the task reference graph |
| V4 | Error | Empty task name; reference to an unknown subtask |
| V5 | Error | Condition operator/operand types mismatch the WorldState key type |
| V6 | Error | Effect references an unknown key; illegal operator/value type |
| V7 | Error | Task parameter count over the limit (8); reference to an unknown parameter name |
| V8 | Error | Subtask argument count/types mismatch the target signature |
| V9 | Error | Operators/predicates must be registered; argument count and types must match |
| V10 | Error | .htnnode baseOperator chain missing/cyclic; macro subtasks must be registered operators; macros cannot nest macros |
| V11 | Error | Domain Import path/task-name/signature/conflict/resolver problems |
| V12 | Error | Same-name WorldState keys with conflicting types |
| V13 | Warning | Unreachable task definitions exist |
| V14 | Warning | A non-last method with no condition set masks later methods |
| V15 | Warning | Method-tree complexity (methodCount × depth) over the threshold |
| V16 | Warning | WorldState key has no writer and is not marked Externally Written |
| V17 | Error | Condition set atom count over the limit (64) |
| V18 | Error | Compiled pool index width over the limit |
The V15 threshold is configurable via Settings Registry
/O3DE/Gems/HtnPlanner/Compiler/ComplexityWarnThreshold(default4096).
.htnnode extension assets
*.htnnode files (JSON) in the project are scanned into the node registry by both the builder and Canvas, becoming node templates referenceable in graphs (NodeConfigId). They are the zero-compile extension channel for designers, in two kinds:
- Operator preset: wraps a registered operator with pre-bound settings / conditions / effects;
- CompoundMacro: declares
methodsand is inlined into a regular compound task at compile time. Macros cannot reference macros; WorldState keys referenced by macro conditions are declared by the host Domain (mind the namespace convention—the sample macros use unprefixed key names such asEnemyVisible, and the host domain must declare them accordingly).
Formats and fields are detailed in
Extending operators. After editing a .htnnode, AssetProcessor rebuilds all dependent Domains automatically; an already-open Canvas palette, however, rescans only after reopening the document or restarting Canvas.