godot-state-machine-advanced について
Godot の状態機械を実装するためのスキルです。フラット FSM、階層型やプッシュダウン型の設計、並行ロジックを扱い、安全な遷移と状態のライフサイクルを重視するスクリプトや指針を提供します。
主な機能
- 01階層状態の委譲
- 02プッシュダウン状態の積層と再開
- 03遷移ガードと共有コンテキスト
- 04並行状態機械の調整
- 05アニメーションとタイマーの同期
活用シーン
- 01移動と攻撃の入れ子状態の構築
- 02ゲームの一時停止や中断後の再開
- 03不正な状態遷移の防止
- 04移動と武器動作の連携
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ thedivergentai/ game-dev-agentic-skills --skill godot-state-machine-advancedインストール後、Claude Code で/godot-state-machine-advancedを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/hsm-implementation-cookbook.mdmigration-notes.mdscripts/hsm_animation_syncer.gdhsm_concurrent_logic.gdhsm_hierarchical_base.gdhsm_logic_state.gdhsm_pushdown_stack.gdhsm_reentry_aware_state.gdhsm_resource_state_loader.gdhsm_state_context.gdhsm_state_history_logger.gdhsm_state_timer_component.gdhsm_transition_guard.gdpushdown_automaton.gd詳細
利用可能なスクリプト
hsm_hierarchical_base.gd
物理処理と入力をサブステートに伝播する高度な HSM 基底デリゲーターです。
hsm_pushdown_stack.gd
割り込み可能な状態(Pause/Menu)を積み重ねるための、プロ仕様のプッシュダウンオートマトンです。
hsm_state_context.gd
状態間で永続データを受け渡すための、疎結合なコンテキストオブジェクトパターンです。
hsm_transition_guard.gd
不正な状態遷移を防ぐための、専門的な遷移検証ロジックです。
hsm_animation_syncer.gd
状態に基づく移動ロジックを用いて、ロジックと AnimationTree を自動的に同期します。
hsm_concurrent_logic.gd
並列ステートマシン(例:移動+攻撃)のオーケストレーションを行います。
hsm_resource_state_loader.gd
カスタム Godot リソース(.tres)を使用して、データ駆動型の状態定義を行います。
hsm_reentry_aware_state.gd
スタックからの再開処理と、新規エントリーイベントを区別して処理します。
hsm_state_history_logger.gd
状態遷移履歴とスタックの深さを追跡する、デバッグ用リングバッファーです。
hsm_state_timer_component.gd
スタンやダッシュなどの有限状態を自動的に遷移させるコンポーネントです。
必須:階層構造/プッシュダウン/ガードについては、hsm_hierarchical_base.gd、hsm_pushdown_stack.gd、hsm_transition_guard.gd(さらに、末端の振る舞いについては hsm_logic_state.gd)を読んでください。
判断ツリー — どのステートマシンを選ぶ?
| 必要な用途 | 選択肢 | 必須スクリプト |
|---|---|---|
| 少数の排他的な状態で、ネストがない | フラット FSM | hsm_logic_state.gd + 薄い親 |
| ネストされたサブステート(移動/空中/攻撃の子状態) | HSM | hsm_hierarchical_base.gd |
| 割り込みオーバーレイ(スタン/メニュー/会話)の後に再開 | プッシュダウン | hsm_pushdown_stack.gd + hsm_reentry_aware_state.gd |
| 並列の関心事(移動+武器) | 並行型 | hsm_concurrent_logic.gd |
| ティックごとにスコアで最適なアクションを選択 | ユーティリティコストのポーリング | エキスパートパターン §3 + hsm_transition_guard.gd |
絶対にしてはいけないこと(エキスパート向け状態ルール)
階層構造と委譲
- 子に物理処理/入力を伝播するのを絶対に忘れないでください — HSM で親の
_physics_processからchild.physics_update()を呼び出さないと、子のロジックが孤立します。 - 深くネストする(3 レベル超)のは絶対に避けてください — 階層が極端に深いと「ステートスパゲッティ」になります。ロジックがそれほど複雑な場合は、ビヘイビアツリーまたはユーティリティ AI を検討してください。
遷移とライフサイクル
- 先行する exit() なしで enter() を呼び出さないでください — exit ロジックを省略すると、タイマー、トゥイーン、オーディオループがバックグラウンドで動き続け、リソースリークの原因になります。
- 遷移フレーム中に状態を変更しないでください —
enter()内で再入可能なtransition_to()呼び出しを行うと、再帰によるクラッシュが発生します。即座にサブ遷移する必要がある場合は、call_deferredを使用してください。 - 状態名を文字列としてハードコードしないでください —
transition_to("Idel")のようなタイプミスは、気付かれないまま致命的な問題を引き起こします。class_nameに基づくチェック、または定数を使用してください。
アーキテクチャとコンテキスト
- 状態データにグローバルシングルトンを使用しないでください — 状態を
GameManager.player_healthに結び付けると、再利用できなくなります。Contextオブジェクトを渡してください。 - 状態を際限なく積み重ねないでください — プッシュダウンオートマトンでは、スタックオーバーフローを避けるため、すべての
push_stateに終了方法(pop_state)を必ず用意してください。 - 状態への再入を常に新規開始と見なさないでください — スタックからポップして再開する場合は、「エントリー SFX/VFX」を省略することがよくあります。再入フラグを使用してください。
実装 — スクリプトを唯一の信頼できる情報源とする
絶対にインラインの HierarchicalState/push_state のサンプルをコピーしないでください。以前の本文では
exit()を二重に呼び出し、再開メッセージを無視していました。
必須のルート:
- 階層/物理入力の転送:hsm_hierarchical_base.gd
enter({"is_resume": true})によるプッシュ/ポップ:hsm_pushdown_stack.gd- 不正な遷移のブロック:hsm_transition_guard.gd
- コンテキストペイロード:hsm_state_context.gd
プッシュダウンの仕様(スクリプト準拠 — exit は1回、再開メッセージあり):
# 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
エキスパート向けステートマシンのパターン
1. HSM ビジュアライザー(デバッグツール)
専用の Control ノードで _draw() を使用し、ビューポート内に現在のステートスタック/階層を可視化して、すぐにデバッグできるようにします [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. ステートベースのオーディオ(疎結合)
ステートの enter() メソッド内で audio.play() をハードコードするのは避けてください。state_changed をリッスンし、ステート名を AudioStream リソースに対応付けるシンカーを使用します [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. 遷移コスト(ユーティリティ AI)
ステートがコンテキストに基づいて自身の「重み」を評価できるようにします。StateMachine が兄弟ステートのコストをポーリングし、最もコストの低い振る舞いへ遷移します [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)
詳細レシピ(必要に応じて)
LLM の無知ルール:一般的なエージェントが事前に知らない情報は、ここか
scripts/に置きます。削除せず、移動のみ行ってください。
| トピック | リファレンス |
|---|---|
| ステートの仕様+ルーティング | hsm-implementation-cookbook.md |
リファレンス
段階的開示:特定の API を調査する場合に限り公式ドキュメントのリンクを開き、同じ領域のドメインへルーティングする際に関連スキルを読み込んでください。ラティス全体を事前に読み込まないでください。
公式ドキュメント
- シグナルの使用 —
state_changed/遷移のファンアウトを通じて処理し、リスナー(アニメーション、オーディオ、AI)が enter/exit の本体から疎結合になるようにします。 - シーンの構成 — 子ノードに状態を所有させ、シグナルは上へ、呼び出しは下へ伝えることで、ステートマシンが兄弟ノード間の強い結合なしに全体を統括できるようにします。
- Godot のクラスとは — 階層化された AI の振る舞いには、深い継承ツリーよりも、状態ノードの合成と
class_nameを優先します。 - アイドル処理と物理処理 — HSM では、毎ティック、アクティブな子(または階層)に
_physics_process/_processを転送する必要がある理由を説明します。 - SceneTree の使用 —
call_deferredによる遷移で、enter()内の再入によるtransition_to()のクラッシュを回避できます。 - Godot の通知 — 初期
enter()を_readyおよび親のキャッシュに対して安全なタイミングで接続する方法を説明します。 - リソース — 各アクターにスクリプトを組み込まずにモジュール型 AI を実現する、データ駆動型の状態定義(
.tres)について説明します。 - AnimationTree の使用 — ゲームプレイの HSM 状態がブレンド/ステートマシンのグラフに対応する場合の、ロジックから AnimationTree への連携について説明します。
- AnimationNodeStateMachinePlayback — HSM の状態名に連動するアニメーション同期処理で使用する
travel()/start()API について説明します。 - Node — 状態ノードがシーンツリーの一員として継承する、子ノードの検索、処理モード、ライフサイクルフックについて説明します。
- InputEvent — 親ノードがアクティブな状態の
handle_inputに転送する、型付きイベントについて説明します。 - Timer — ワンショットタイマーから遷移シグナルを発行することで実現する、有限時間の状態(スタン、ダッシュ)について説明します。
関連スキル
前提条件
- godot-project-foundations — すべての HSM ルートと子状態シーンが前提とする、シーンの所有権とプロジェクトレイアウトの規約について説明します。
- godot-gdscript-mastery — ガード、遅延遷移、コンテキストオブジェクトに必要な class_name、型付き Dictionary/ペイロード、Callable について説明します。
- godot-signal-architecture — 状態がシグナルを発行すると同時に自身のシグナルをリッスンするような循環グラフを避けながら、遷移イベントをシグナルで上位へ伝える方法を説明します。
補完
- godot-composition — HSM / VSM をコンポジションルートの下に StateComponent として配置し、アクタースクリプトが肥大化するのを防ぎます。
- godot-input-handling — センス層で入力をサンプリングします。状態はグローバルをポーリングするのではなく、handle_input を通じて方向やアクションを受け取ります。
- godot-characterbody-2d — 移動状態は、コンテキスト経由で渡されたアクターの move_and_slide / velocity API を呼び出します。
- godot-animation-tree-mastery — HSM シンカーが状態名を使って遷移するブレンドツリーと AnimationNodeStateMachine グラフを扱います。
- godot-resource-data-patterns — 調整可能な状態 Resource(速度、スタン時間、AI の重み)を、実行時の Node ライフサイクルから分離します。
- godot-2d-animation — 完全な AnimationTree より軽量な同期方法で十分な場合の、Sprite / AnimationPlayer によるプレゼンテーションを扱います。
下流/利用側
- godot-combat-system — ヒットスタン、攻撃の予備動作、死亡スタックは、戦闘キャラクターにおける典型的なプッシュダウン/HSM の利用例です。
- godot-ability-system — 詠唱、チャネリング、クールダウンの各フェーズは、ガード付き遷移や時間制限付き状態に適しています。
- godot-turn-system — ターンの各フェーズや割り込みスタックでは、プッシュダウン/並行マシンのオーケストレーションパターンを再利用します。
- godot-dialogue-system — カットシーンや会話のオーバーレイはゲームプレイ状態の上にプッシュされ、コンテキストを失わずにポップできる必要があります。
マスター
- godot-master — ライブラリのルーター兼ミラー化されたモジュールエントリーです。複数の領域にまたがるアーキテクチャ上の懸念を担当するドメインスキルを調べる際に開いてください。