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

flowchart LR src["my_domain.htndomain\nsource asset (text ObjectStream)"] ap["AssetProcessor\nHtnDomainBuilder"] out["my_domain.htndomain_compiled\nruntime product (binary HtnDomainAsset)"] src --> ap --> out out -->|referenced by HtnAgentComponent| run["runtime planning"]
  • 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.
  • HtnAgentComponent references 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 HtnCanvas as a separate process);
  • The Open in HTN Canvas button on EditorHtnAgentComponent (resolves the compiled Domain back to its .htndomain source and opens it).

Graph-building steps

Follow the guard_domain.htndomain sample:

  1. Drag out a Domain Root from the node palette (exactly one per graph) and connect its Root slot to the root compound task (the root reference takes no parameters).
  2. Drag out a Compound Task, name it in the inspector (e.g. Root), and connect it to the Domain Root.
  3. Drag Method nodes into the compound’s ordered Methods slots—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).
  4. Drag Primitive Task nodes into a method’s ordered Subtasks slots—child slot index = execution order. Binding options below.
  5. 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).
  6. Cross-domain reuse: use a Domain Import node to reference a zero-parameter compound task in another .htndomain (the sample imports Patrol from patrol_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:

BindingOperatorNameNodeConfigId
C++ built-in/registered operatornon-empty (e.g. LogMessage, Wait)empty
.htnnode preset/macroemptynon-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

RuleSeverityMeaning
V1ErrorExactly one Domain Root; root connected; the root task reference takes no parameters
V2ErrorSlot connection types are legal; compound tasks have at least one method
V3ErrorStatic cycle in the task reference graph
V4ErrorEmpty task name; reference to an unknown subtask
V5ErrorCondition operator/operand types mismatch the WorldState key type
V6ErrorEffect references an unknown key; illegal operator/value type
V7ErrorTask parameter count over the limit (8); reference to an unknown parameter name
V8ErrorSubtask argument count/types mismatch the target signature
V9ErrorOperators/predicates must be registered; argument count and types must match
V10Error.htnnode baseOperator chain missing/cyclic; macro subtasks must be registered operators; macros cannot nest macros
V11ErrorDomain Import path/task-name/signature/conflict/resolver problems
V12ErrorSame-name WorldState keys with conflicting types
V13WarningUnreachable task definitions exist
V14WarningA non-last method with no condition set masks later methods
V15WarningMethod-tree complexity (methodCount × depth) over the threshold
V16WarningWorldState key has no writer and is not marked Externally Written
V17ErrorCondition set atom count over the limit (64)
V18ErrorCompiled pool index width over the limit

The V15 threshold is configurable via Settings Registry /O3DE/Gems/HtnPlanner/Compiler/ComplexityWarnThreshold (default 4096).

.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 methods and 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 as EnemyVisible, 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.


Copyright © 2026 DawnEngine. All rights reserved.

DawnEngine is a commercial 3D engine distributed under the DawnEngine end-user license agreement. Engine binaries and source are proprietary and are not covered by the licenses below.

Documentation only: the prose and templates on this site are a derivative work of Open 3D Engine (O3DE) documentation by the O3DE Contributors, used under CC BY 4.0 (documentation content), Apache 2.0 (site code), and the MIT license (inline code samples).

The open-source 3D engine that DawnEngine is built on top of is Open 3D Engine . DawnEngine is not affiliated with, endorsed by, or sponsored by The Linux Foundation or the O3DE project. “O3DE” and “Open 3D Engine” are trademarks of The Linux Foundation.