thedivergentai/gd-agentic-skills

godot-state-machine-advanced

Expert blueprint for hierarchical finite state machines (HSM) and pushdown automata for complex AI/character behaviors.

First seen Feb 10, 2026

Installation

$ npx skills add thedivergentai/gd-agentic-skills --skill godot-state-machine-advanced

Summary

  • Expert blueprint for hierarchical finite state machines (HSM) and pushdown automata for complex AI/character behaviors.
  • Covers state stacks, sub-states, transition validation, and state context passing.
  • Use when basic FSMs are insufficient OR implementing layered AI.
  • Keywords state machine, HSM, hierarchical, pushdown automata, state stack, FSM, AI behavior.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from thedivergentai/gd-agentic-skills · top by installs.

npx skills add thedivergentai/gd-agentic-skills

Browse all from thedivergentai/gd-agentic-skills

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Stars 678
License LICENSE
Default branch main
Open issues 0
Status Active

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 13,243 B
  • docs SUMMARY.md 3,977 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 350 installs

SKILL.md

Available Scripts

[hsmhierarchicalbase.gd](scripts/hsmhierarchicalbase.gd)

Advanced HSM base delegator for propagating physics and input to sub-states.

[hsmpushdownstack.gd](scripts/hsmpushdownstack.gd)

Professional Pushdown Automata for interruptive state (Pause/Menu) stacking.

[hsmstatecontext.gd](scripts/hsmstatecontext.gd)

Decoupled context object pattern for passing persistent data between states.

[hsmtransitionguard.gd](scripts/hsmtransitionguard.gd)

Expert transition validation logic to prevent illegal state changes.

[hsmanimationsyncer.gd](scripts/hsmanimationsyncer.gd)

Automated Logic-to-AnimationTree syncing with state-based travel logic.

[hsmconcurrentlogic.gd](scripts/hsmconcurrentlogic.gd)

Orchestration for parallel state machines (e.g., Move + Attack).

[hsmresourcestateloader.gd](scripts/hsmresourcestateloader.gd)

Data-driven state definition using custom Godot Resources (.tres).

[hsmreentryawarestate.gd](scripts/hsmreentryawarestate.gd)

Handling resume-from-stack logic vs fresh entry events.

[hsmstatehistorylogger.gd](scripts/hsmstatehistorylogger.gd)

Debug ring-buffer for tracking state transition history and stack depth.

[hsmstatetimercomponent.gd](scripts/hsmstatetimercomponent.gd)

Auto-transition component for finite states like Stun or Dash.

MANDATORY: For hierarchy / pushdown / guards read [hsmhierarchicalbase.gd](scripts/hsmhierarchicalbase.gd), [hsmpushdownstack.gd](scripts/hsmpushdownstack.gd), [hsmtransitionguard.gd](scripts/hsmtransitionguard.gd) (plus [hsmlogicstate.gd](scripts/hsmlogicstate.gd) for leaf behaviors).

Decision Tree — Which Machine?

Need Choose MANDATORY scripts
Few exclusive states, no nesting Flat FSM [hsmlogicstate.gd](scripts/hsmlogicstate.gd) + thin parent
Nested sub-states (Move/Air/Attack children) HSM [hsmhierarchicalbase.gd](scripts/hsmhierarchicalbase.gd)
Interrupt overlays (stun/menu/dialogue) then resume Pushdown [hsmpushdownstack.gd](scripts/hsmpushdownstack.gd) + [hsmreentryawarestate.gd](scripts/hsmreentryawarestate.gd)
Parallel concerns (locomotion + weapon) Concurrent [hsmconcurrentlogic.gd](scripts/hsmconcurrentlogic.gd)
Pick best action by score each tick Utility cost polling Expert pattern §3 + [hsmtransitionguard.gd](scripts/hsmtransitionguard.gd)

NEVER Do (Expert State Rules)

Hierarchy & Delegation

  • NEVER forget to propagate physics/input to children — In an HSM, failing to call child.physicsupdate() from the parent's physics_process orphans child logic.
  • NEVER use deep nesting (>3 levels) — Extreme hierarchy creates "State Spaghetti." If logic is that complex, consider a Behavior Tree or Utility AI.

Transitions & Lifecycle

  • NEVER call enter() without a preceding exit() — Skipping exit logic leaves timers, tweens, or audio loops running in the background, causing resource leaks.
  • NEVER modify state during a transition frame — Re-entrant transitionto() calls inside enter() cause recursion crashes. Use calldeferred if immediate sub-transitioning is required.
  • NEVER hardcode state names as strings — Typos like transitionto("Idel") are silent killers. Use classname based checks OR Constants.

Architecture & Context

  • NEVER use global singletons for state data — Coupling states to GameManager.player_health makes them non-reusable. Pass a Context object.
  • NEVER push states indefinitely — In a Pushdown Automaton, every pushstate MUST have a retirement plan (popstate) to avoid stack overflow.
  • NEVER assume state re-entry is always a fresh start — Resuming from a stack pop should often bypass "Entry SFX/VFX"; use re-entry flags.

Implementation — Scripts Are Source of Truth

Do NOT copy inline HierarchicalState / push_state samples. Prior body double-exit()ed and ignored resume messages.

MANDATORY route:

  • Hierarchy / physics-input forward: [hsmhierarchicalbase.gd](scripts/hsmhierarchicalbase.gd)
  • Push / pop with enter({"isresume": true}): [hsmpushdownstack.gd](scripts/hsmpushdown_stack.gd)
  • Illegal transition blocking: [hsmtransitionguard.gd](scripts/hsmtransitionguard.gd)
  • Context payload: [hsmstatecontext.gd](scripts/hsmstatecontext.gd)

Pushdown contract (from script — single exit, resume msg):

# See hsm_pushdown_stack.gd — do not reimplement
func push_state(state_path: String, msg: Dictionary = {}) -> void: ...
func pop_state() -> void:
    # old.exit(); stack.back().enter({"is_resume": true})
    pass

Expert State Machine Patterns

1. HSM Visualizer (Debug Tool)

Use a specialized Control node with _draw() to visualize the current state stack/hierarchy in the viewport for immediate debugging [3, 11].

class_name HSMVisualizer extends Control
@export var state_machine: Node

func _draw() -> void:
    var font := ThemeDB.fallback_font
    var pos := Vector2(20, 20)
    # Recursively draw active state names...
    draw_string(font, pos, "Active: " + state_machine.current_state.name)

2. State-Based Audio (Decoupled)

Avoid hardcoding audio.play() inside state enter() methods. Use a syncer that listens to state_changed and maps state names to AudioStream resources [12, 13].

class_name StateAudioSyncer extends Node
@export var state_machine: Node
@export var audio_map: Dictionary # { "Jump": preload("jump.wav") }

func _ready() -> void:
    state_machine.state_changed.connect(_on_state_changed)

func _on_state_changed(_old, new_state: Node):
    if audio_map.has(new_state.name):
        $AudioPlayer.stream = audio_map[new_state.name]
        $AudioPlayer.play()

3. Transition Cost (Utility AI)

Enable states to evaluate their own "weight" based on context. The StateMachine polls sibling costs and transitions to the lowest-cost behavior [17, 18].

# CostState.gd (Base)
func get_cost(context: Dictionary) -> float:
    return 10.0 # Default weight

# UtilityStateMachine.gd
func _physics_process(_d: float) -> void:
    var best_state: Node = current_state
    var low_cost: float = INF
    for child in get_children():
        var cost = child.get_cost(context)
        if cost < low_cost:
            low_cost = cost
            best_state = child
    if best_state != current_state:
        transition_to(best_state.name)

Deep recipes (on demand)

LLM-ignorance rule: if a general agent would not know it before reading, it lives here or in scripts/ — never delete, only move.

Topic Reference
State contract + routing [hsm-implementation-cookbook.md](references/hsm-implementation-cookbook.md)

Reference

Progressive disclosure: open Official Documentation links only when researching a specific API; load Related Skills when routing to a peer domain — do not preload the whole lattice.

Official Documentation

  • Using signals — Drive statechanged / transition fan-out so listeners (anim, audio, AI) stay decoupled from enter/exit bodies.
  • Scene organization — Child-node state ownership and signal-up / call-down so the machine orchestrates without sibling hard-coupling.
  • What are Godot classes — Prefer composed state nodes + class_name over deep inheritance trees for layered AI behaviors.
  • Idle and Physics Processing — Why HSMs must forward physicsprocess / process into the active child (or hierarchy) every tick.
  • Using SceneTreecalldeferred transitions avoid re-entrant transition_to() crashes inside enter().
  • Godot notifications — Safe wiring timing for initial enter() relative to _ready and parent caches.
  • Resources — Data-driven state definitions (.tres) for modular AI without baking scripts into every actor.
  • Using AnimationTree — Logic-to-AnimationTree travel when gameplay HSM states map to blend/state-machine graphs.
  • AnimationNodeStateMachinePlaybacktravel() / start() APIs used by animation syncers tied to HSM state names.
  • Node — Child lookup, process modes, and lifecycle hooks state nodes inherit as scene-tree citizens.
  • InputEvent — Typed events parents forward into handleinput on the active state.
  • Timer — Finite-duration states (stun, dash) via one-shot timers that emit transition signals.

Related Skills

Prerequisites

  • godot-project-foundations — Scene ownership and project layout conventions every HSM root and child state scene assumes.
  • godot-gdscript-mastery — class_name, typed Dictionaries/payloads, and Callables needed for guards, deferred transitions, and context objects.
  • godot-signal-architecture — Signal-up transition events without circular graphs where states emit and also listen to themselves.

Complements

  • godot-composition — Drop HSM / VSM as a StateComponent under a composition root instead of bloating the actor script.
  • godot-input-handling — Sense-layer sampling; states receive directions/actions via handle_input rather than polling globals.
  • godot-characterbody-2d — Locomotion states call moveandslide / velocity APIs on the actor passed through context.
  • godot-animation-tree-mastery — Blend trees and AnimationNodeStateMachine graphs that HSM syncers travel into by state name.
  • godot-resource-data-patterns — Tunable state Resources (speeds, stun durations, AI weights) separate from runtime Node lifecycle.
  • godot-2d-animation — Sprite / AnimationPlayer presentation when a lighter sync path than a full AnimationTree is enough.

Downstream / consumers

  • godot-combat-system — Hit-stun, attack windup, and death stacks are classic pushdown / HSM consumers on fighters.
  • godot-ability-system — Cast, channel, and cooldown phases map cleanly to guarded transitions and timed states.
  • godot-turn-system — Turn phases and interrupt stacks reuse pushdown / concurrent machine orchestration patterns.
  • godot-dialogue-system — Cutscene and dialogue overlays push over gameplay states and must pop without losing context.

Master

  • godot-master — Library router and mirrored module entry; open when discovering which Domain Skill owns a cross-cutting architecture concern.