この記事では、Godot Engineのアドオン「LimboAI」を使用して、階層型ステートマシンを作成する方法についてまとめていきます。
※今回の記事ではビヘイビアツリーについては触れませんので、ご注意ください
LimboAIのインストール
LimboAIは、AssetLibからインストールが可能です。
① エディタ上部から[AssetLib]タブを開きます
➁ 「Limbo」等で検索をかけ、ヒットしたLimboAIを選択します
③ ダウンロードウィンドウのアセット名が、自身の使用しているGodotのバージョンと
一致していることを確認します。(今回は4.3)
④ ダウンロードボタンをクリックし、ダウンロードを開始します。

ダウンロードが完了すると、インストール前の設定ウィンドウが開きます。
デフォルトでLimboAIにはデモが付属していますが、不要な場合はエディタ上部の[<]ボタンからアセットの内容を表示し、[demo/]フォルダのチェックを外します。
その他、競合やインストール先のフォルダに問題が無ければ[インストール]ボタンをクリックします。
LimboAIのインストールが完了すると、エディタのタブに[LimboAI]が追加されます。
今回の記事では扱いませんが、LimboAIでは、このタブのエディタでビヘイビアツリーの編集を行っていきます。

ステートマシンの作成
まずはステートマシンの基本的な構成方法について、簡単な例でまとめていきます。
ノードの追加
ステートマシンを追加したいノードの配下に、[+]ボタン (ショートカット:Ctrl+A)から、
階層型ステートマシンとして制御を行う[LimboHSM]ノード、
その子に制御対象のステートとなる[LimboState]ノードを以下のツリー構成で追加します。

※今回の例は簡単にするため2つのステートのみですが、さらに多くのLimboStateノードや、LimboHMS自体を子として追加・制御することも可能です。
遷移設定とステートマシンの初期化
extends Node2D
# ステートマシンと使用するステートノードを宣言
@onready var limbo_hsm = $LimboHSM
@onready var true_state = $LimboHSM/TrueState
@onready var false_state = $LimboHSM/FalseState
# ステート切替用のインプットフラグ
var input_flag = false
func _ready():
## ステートマシンの初期化処理 ###
# 状態間の遷移を設定
limbo_hsm.add_transition(true_state, false_state, &"to_false")
limbo_hsm.add_transition(false_state, true_state, &"to_true")
# ステートマシンの初期化と有効化
limbo_hsm.initialize(self)
limbo_hsm.set_active(true)
func _process(delta):
# 遷移テスト用。ui_accept(Enter/Spaceなど)入力時に、input_flagを有効化
if Input.is_action_pressed("ui_accept"):
input_flag = true
else:
input_flag = false
# ラベルに現在のステートとインプットフラグの状態を表示
$VBoxContainer/CurrentState.text = limbo_hsm.get_active_state().name
$VBoxContainer/InputFlag.text = str(input_flag)
<LimboHSM>.add_transition :
・ステートマシン(LimboHSM)にステート遷移の設定を追加するメソッド。
・主となる引数は、<遷移元LimboState>,<遷移先LimboState>,<イベント(文字列名)>で、
指定イベントがディスパッチされた際に遷移が実行されます。
<LimboHSM>.initialize :
・ステートマシンの初期化を実行するメソッド。
・実行HSM自身と、その配下にある全てのLimboStateで、LimboState._setupを実行します。
※ _setup() :LimboStateにおいて、初期化中に 1 度呼び出される仮想メソッド
<LimboHSM>.set_active :
・ステートマシンの有効(true)/無効(false)を設定するメソッド。
・trueに設定時、initial_stateで、update_modeに従ったステート処理が開始されます。
・initial_state:
初期状態。未設定の場合はLimboHSM の最初の子が割り当てられます。
・update_mode:
ステートマシンの更新タイミングの設定を行います。
モードは列挙型UpdateModeで指定し、の初期値は1(PHYSICS)です。
IDLE = 0:Idleプロセス中(_process)にステート マシンを更新
PHYSICS = 1:物理プロセス中(_physics_process)にステート マシンを更新
MANUAL = 2:メソッドupdateの呼び出しによる手動で更新タイミングを指定
各ステートの処理定義
LimboStateには4つの仮想メソッドがあり、ステート固有の処理は主にそのメソッド内に処理を記述していく形で定義します。
仮想メソッド | 概要 |
---|---|
_setup() | LimboStateのセットアップ時に1度呼び出されます |
_enter() | ステートに遷移した時、1度呼び出されます |
_exit() | ステートが終了した(別状態への遷移やノード削除)時、1度呼び出されます |
_update(delta) | このステートが有効な時、フレーム毎に呼び出されます |
TrueState:
extends LimboState
@onready var state_test_node = $"../.."
# LimboStateのセットアップ時に1度呼び出されます
func _setup():
print("ステート: ", name, " のセットアップを実行します")
# ステートに遷移した時、1度呼び出されます
func _enter():
print("ステート: ", name, " に遷移しました。")
# ステートが終了した(別状態への遷移やノード削除)時、1度呼び出されます
func _exit():
print("ステート: ", name, " が終了しました。")
# このステートが有効な時、フレーム毎に呼び出されます
func _update(delta):
# ルートノードの変数input_flagの状態を参照し、イベントをディスパッチ
if state_test_node.input_flag == false:
dispatch(&"to_false")
FalseStateは、 _update()内のdispatchするイベントのみを差し替えて以下のように記述します
# <コード上部はTrueStateのスクリプトをコピペしてください>
# このステートが有効な時、フレーム毎に呼び出されます
func _update(delta):
# ルートノードの変数input_flagの状態を参照し、イベントをディスパッチ
if state_test_node.input_flag == true:
dispatch(&"to_true")
<LimboState>.dispatch():
・指定したイベント(文字列名型で指定)をディスパッチするメソッド。
・<LimboHSM>.add_transition 設定があれば、このディスパッチによって遷移が実行されます
動作確認
ここまでの作業が完了したら、F6で現在のシーンを実行してみましょう。
ui_acceptに割り当てられているEnterキーやSpaceキーを押すと、ステートの遷移を確認できます。
おわりに
この記事では、LimboAIを使用したステートマシンの構成方法について、基本的な内容に絞ってまとめました。
より詳細な内容や、使用した各種ノード・メソッド等で使用可能なオプションについては、以下公式ドキュメントページ等をご参照ください。
コメント