AgentSkillsCN

godot-state-machine-advanced

提供层次化有限状态机(HSM)与下推自动机的专家蓝图,用于复杂的AI/角色行为。涵盖状态栈、子状态、转换验证与状态上下文传递。在基础FSM无法满足需求,或需实现分层AI时使用。关键词:状态机、HSM、层次化、下推自动机、状态栈、FSM、AI行为。

SKILL.md
--- frontmatter
name: godot-state-machine-advanced
description: "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."

Advanced State Machines

Hierarchical states, state stacks, and context passing define complex behavior management.

Available Scripts

hsm_logic_state.gd

Expert HSM base class with state stack management and validation.

pushdown_automaton.gd

Stack-based state machine for interrupt-resume behavior (pause menus, cutscenes).

MANDATORY: Read hsm_logic_state.gd before implementing hierarchical AI behaviors.

NEVER Do in Advanced State Machines

  • NEVER forget to call exit() before enter() — Transition without exit? Previous state cleanup skipped = resource leaks (timers, tweens). ALWAYS exit → enter.
  • NEVER use push_state() without pop_state() — Pushed 100 interrupt states? Stack overflow + memory leak. EVERY push needs matching pop.
  • NEVER modify state during transitiontransition_to() called inside exit()? Re-entrant transition = undefined behavior. Flag transitions, execute after current completes.
  • NEVER skip state validationtransition_to("AttackState") but state doesn't exist? Silent failure OR crash. Validate state exists before transition.
  • NEVER forget to process child states — Hierarchical state with sub-states? Parent update() must call current_child_state.update() for delegation.
  • NEVER use string state names in codetransition_to("Idel") typo = silent failure. Use constants OR enums: transition_to(States.IDLE).

gdscript
# hierarchical_state.gd
class_name HierarchicalState
extends Node

signal transitioned(from_state: String, to_state: String)

var current_state: Node
var state_stack: Array[Node] = []

func  _ready() -> void:
    for child in get_children():
        child.state_machine = self
    
    if get_child_count() > 0:
        current_state = get_child(0)
        current_state.enter()

func transition_to(state_name: String) -> void:
    if not has_node(state_name):
        return
    
    var new_state := get_node(state_name)
    
    if current_state:
        current_state.exit()
    
    transitioned.emit(current_state.name if current_state else "", state_name)
    current_state = new_state
    current_state.enter()

func push_state(state_name: String) -> void:
    if current_state:
        state_stack.append(current_state)
        current_state.exit()
    
    transition_to(state_name)

func pop_state() -> void:
    if state_stack.is_empty():
        return
    
    var previous_state := state_stack.pop_back()
    transition_to(previous_state.name)

State Base Class

gdscript
# state.gd
class_name State
extends Node

var state_machine: HierarchicalState

func enter() -> void:
    pass

func exit() -> void:
    pass

func update(delta: float) -> void:
    pass

func physics_update(delta: float) -> void:
    pass

func handle_input(event: InputEvent) -> void:
    pass

Best Practices

  1. Separation - One state per file
  2. Signals - Communicate state changes
  3. Stack - Use push/pop for interruptions

Reference

  • Related: godot-characterbody-2d, godot-animation-player

Related