HTN Agent component

HtnAgentComponent is the single runtime component that connects HTN planning to an entity. Internally it owns the entity’s HtnWorldState and a planning/execution state machine (HtnPlanRunner), implements HtnAgentRequestBus (control + WorldState read/write), and listens on the asset bus for Domain hot reload. In the Editor it is EditorHtnAgentComponent, whose property panel adds an Open in HTN Canvas button. The component provides the exclusive HtnAgentService—one per entity.

Configuration fields

Editor nameTypeDefaultPurpose
Domainasset referenceemptyThe compiled domain asset to execute (.htndomain_compiled)
Root TaskstringemptyPlanning entry task name; empty uses the Domain’s own root task
Auto StartbooltrueAutomatically request the first plan on activation (once the asset is ready)
Continuous ModebooltrueReplan after a plan completes so the AI keeps running
Opportunistic ReplanbooltrueRecompute in the background on WorldState change; preempt when the MTR is strictly better
Replan Intervalfloat0.5Minimum interval for opportunistic/continuous replans (throttle, seconds)
Backoff Initialfloat0.5First backoff duration after a planning failure (seconds)
Backoff Maxfloat5.0Backoff ceiling (seconds); exponential backoff = initial × 2^consecutive-failures, capped here
Max Decomposition Stepsuint324096Per-solve decomposition step cap; exceeding it yields StepLimitExceeded

Tuning tips

  • Static / low-frequency AI: set Continuous Mode = false and call RequestPlan() explicitly from script when needed—saves CPU.
  • Reactive AI: keep Opportunistic Replan = true and drive replans by writing WorldState from sensors; use Replan Interval to suppress thrash (no need to replan every frame when an enemy position changes every frame).
  • Large, complex Domains: on StepLimitExceeded, first check for overly deep/wide method trees (the compile-time V15 complexity warning flags these); raise Max Decomposition Steps only after that.
  • The global per-frame planning budget is an independent dimension shared by all agents; see htn_setPlanningBudget in Debugging and the Settings Registry in Reference.

State machine

stateDiagram-v2 [*] --> Idle Idle --> Planning: RequestPlan / continuous-mode throttle expiry Planning --> Executing: plan found Planning --> Failed: NoPlan / StepLimitExceeded Executing --> Idle: plan complete (non-continuous) Executing --> Planning: step failure / recheck failure / better opportunistic plan Failed --> Planning: WorldState change / backoff expiry Executing --> Idle: Stop()
StateMeaningKey transitions
IdleNo active planning/executionRequestPlan() or continuous-mode throttle expiry → Planning
PlanningIncremental decomposition (consumes the global per-frame budget)Success → Executing; failure → Failed
ExecutingRuns operators step by stepPer step: condition recheck → ApplyOnStart effects → operator Begin/Tick → on success ApplyOnSuccess effects and next step; a step failure triggers replan
FailedPlanning failure backoffExponential backoff; a WorldState write or backoff expiry → back to Planning

Planning is time-sliced: each frame the system hands decomposition steps to agents in FIFO rotation (default 2048 global steps/frame). Large-Domain planning spans multiple frames, but the result is byte-identical to a one-shot solve.

Control API: HtnAgentRequestBus

Addressed by entity id. Full definition in the public header HtnPlanner/HtnPlannerBus.h:

class HtnAgentRequests : public AZ::ComponentBus
{
public:
 // Run control
 virtual void RequestPlan() = 0; // request a plan now
 virtual void RequestReplan() = 0; // explicit replan (ExplicitRequest)
 virtual void Stop(bool abortCurrentOperator) = 0; // stop; optionally abort the in-flight operator
 virtual bool IsExecuting() const = 0;
 virtual AZStd::string GetCurrentStepName() const = 0; // debug / UI

 // WorldState read/write (main entry point for scripts and sensors)
 virtual bool SetWorldStateValue(AZ::Crc32 key, const HtnValue& value) = 0;
 virtual AZStd::optional<HtnValue> GetWorldStateValue(AZ::Crc32 key) const = 0;
 virtual AZStd::optional<HtnValueType> GetWorldStateKeyType(AZ::Crc32 key) const = 0;

 // Script operator completion report (for the Script.SendOperatorEvent bridge only)
 virtual void CompleteCurrentOperator(uint32_t operationToken, bool success) = 0;
};

Examples:

// Force an immediate replan (e.g., a high-priority event arrived)
HtnPlanner::HtnAgentRequestBus::Event(
 agentEntityId, &HtnPlanner::HtnAgentRequests::RequestReplan);

// Stop the agent and abort the current operator
HtnPlanner::HtnAgentRequestBus::Event(
 agentEntityId, &HtnPlanner::HtnAgentRequests::Stop, /*abortCurrentOperator*/ true);

WorldState read/write details, value types, and script-side equivalents are in WorldState and scripting.

Event notifications: HtnAgentNotificationBus

Also addressed by entity id; connect to observe the agent’s planning/execution events (UI, audio, animation hooks):

class HtnAgentNotifications : public AZ::ComponentBus
{
public:
 virtual void OnPlanStarted(const HtnPlan& plan);
 virtual void OnPlanStepStarted(uint32_t stepIndex);
 virtual void OnPlanStepCompleted(uint32_t stepIndex, bool success);
 virtual void OnPlanCompleted();
 virtual void OnPlanFailed(HtnPlanFailReason reason);
 virtual void OnReplanTriggered(HtnReplanReason reason);
 virtual void OnTraceRecorded(const HtnDecompositionTrace& trace); // debug agent only
};
  • The bus is reflected to BehaviorContext so scripts can attach a handler—except OnPlanStarted and OnTraceRecorded, which carry structures unsuitable for the script boundary.
  • OnTraceRecorded fires only for the agent selected as the debug target (see Debugging).

Lifecycle and Domain hot reload

  • Activate: connects HtnAgentRequestBus; queues the Domain asset load if its id is valid.
  • Asset ready / hot reload: builds the import-linked domain view. On first bind it initializes WorldState and the state machine; on hot reload it migrates WorldState to the new schema (preserving values for same-name, same-type keys) and triggers a replan. If Auto Start is set, a plan is requested once binding completes.
  • Deactivate: unregisters from the global scheduler, stops, and disconnects buses.

So editing and recompiling a Domain asset while the game runs makes agents hot-reload and replan automatically, keeping externally written facts wherever possible.


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.