【Unreal Engine】StateTree入門:状態と遷移で、見張りの行動を整理する

作成: 2026-07-23最終更新: 2026-09-06

UE5のStateTreeを、巡回・追跡・警戒待機の3状態で学びます。キー操作で発見と見失いを試し、Task、Transition、条件へのデータの渡し方、Behavior Treeとの使い分けを図解します。

「巡回中にプレイヤーを見つけたら追跡する。見失ったら少し待ち、また見つければ追跡へ戻る」。文章では短く書けるのに、Blueprintへ条件を足していくと、今どの処理をしているのか分かりにくくなることがあります。

StateTreeは、こうした「今の状態」と「次へ移るきっかけ」を整理する仕組みです。敵AIのほか、扉やクエストの進行にも使えます。

巡回、追跡、警戒待機を、発見・見失い・5秒経過のきっかけで切り替える

この記事でわかること

  • StateTreeとBehavior Treeで「読む場所」が違うこと
  • 状態の中身(Task)と、切り替わるきっかけ(Transition)の分け方
  • 巡回・追跡・警戒待機の3状態をキー操作で試す手順
  • データをどこに置き、条件へどう渡すか

今回は、キー操作で「見つけた」「見失った」を切り替える見張りモデルを作ります。まず画面の文字で状態の変化を確かめ、最後に感知や移動へつなぐ考え方を整理します。

  • State、Task、Transitionが何を担当するか
  • Actorの値を、状態が切り替わる条件へ渡す方法
  • 警戒中に再発見したら、5秒の待機を中断する作り方
  • Behavior Treeと、どちらで仕様を表すと読みやすいか

Sponsored

Behavior Treeと、読む場所が違う

Behavior Treeでは、「追う相手がいれば追跡を優先し、それ以外は巡回する」といった優先順位を、枝の並びで表します。

StateTreeでは、「巡回中に発見したら追跡へ」「追跡中に見失ったら警戒待機へ」という状態の切り替わりを中心に読みます。

Behavior Treeは優先する枝、StateTreeは今の状態から移る条件を中心に読む
見たいことBehavior TreeStateTree
どの行動を優先するかSelectorの枝と条件を読む状態の選択条件や遷移を読む
今の処理が終わったらどうするかSequenceの次のTaskなどを読む完了時のTransitionを読む
途中で別の行動へ移れるかDecoratorのObserver Abortsなどで設定状態から出るTransitionで設定

UEのBehavior Treeは、Blackboardの変化などをきっかけに処理を切り替えるイベント駆動です。「毎フレーム木全体を調べるBT」と「必要なときだけ動くStateTree」という分け方ではありません。ここでは、同じ仕様をどちらの構造で読みたいかに注目しましょう。

EnumとSwitchのステートマシンで状態を分けたことがあれば、StateTreeは、その状態ごとの処理と切り替えを専用エディタで組むもの、と考えると入りやすくなります。

状態の中身と、切り替わるきっかけ

まずは、次の4つを押さえます。

状態の箱の中にTaskを置き、入る条件と次へ移るTransitionを設定する
要素意味今回の例
State(状態)今、何をしているかPatrol、Chase、Search
Taskその状態で行う処理文字を表示する、5秒待つ
Condition(条件)値を調べ、成立するか決めるPlayerVisibleがtrueか
Transition(遷移)いつ、どの状態へ移るか見失ったらChaseからSearchへ

遷移(せんい) は、状態が切り替わることです。Transitionでは、調べるきっかけの「Trigger」、行き先の「Transition To」、必要なら「Conditions」を設定します。

条件にも置き場所があります。Enter Conditionsは、その状態へ入れるかを調べます。TransitionのConditionsは、別の状態へ移るかを調べます。入場条件を書いただけで、実行中の状態から自動的に出ていくわけではありません。

もう一つ、Taskの並べ方にも違いがあります。同じStateに置いたTaskは、順番待ちの一覧ではありません。 文字を出しながら時間を数える、というように並行して動きます。今回のSearchでは、この性質を使って「警戒中の表示」と「5秒の待機」を組み合わせます。

最初は「巡回中」と表示してみる

実験には、Third Personなど、Playしてレベル内を見られるプロジェクトを使います。Blueprintで変数とキー入力を追加できれば進められます。

1. プラグインと、実行するActorを用意する

「Edit」→「Plugins」でStateTreeを検索し、StateTreeGameplayStateTreeを有効にして、再起動します。

次にActorを親とするBlueprint BP_GuardStateDemo を作ります。位置が分かるようにStatic MeshのCubeを1つ追加し、プレイヤーの近くへ配置してください。これは状態を観察するための見張り役です。

Boolean変数 PlayerVisible を追加し、初期値をfalse、Instance Editableをオンにします。Booleanはtrue/falseの2値で、今回は「プレイヤーが見えるか」を表します。コンパイル・保存しておきます。

2. SchemaとComponentをそろえる

コンテンツブラウザで右クリックし、「Artificial Intelligence」→「StateTree」から ST_GuardStateDemo を作ります。Schemaには StateTreeComponentSchema を選びます。

Schema(スキーマ) は、そのStateTreeをどこで使い、どんなデータを受け取れるかを決める設定です。今回は普通のActorで実行する組み合わせを使います。

普通のActorとStateTree Componentを、StateTreeComponentSchemaのアセットへ対応させる
  1. ST_GuardStateDemoを開き、Schemaの「Context Actor Class」を BP_GuardStateDemo にする。
  2. BP_GuardStateDemoへ StateTree Component を追加する。
  3. コンポーネントの「State Tree」に ST_GuardStateDemo を指定する。
  4. 「Start Logic Automatically」が表示される版ではオンにする。

Contextは、StateTreeが実行相手として受け取るデータです。今回のActorは、レベルへ配置したBP_GuardStateDemoです。Classを指定しておくと、後でそのActorのPlayerVisibleを条件から選べます。

StateTree AI ComponentはAI Controller用です。今回使う通常のStateTree Componentとは、Schemaも組み合わせも異なります。

3. Patrolを1つ作る

ST_GuardStateDemoのRootを右クリックし、「Add Child State」から Patrol を追加します。Rootは木の入口、Patrolはその子です。

Patrolを選び、詳細の「Tasks」に Debug Text Task を追加します。「Text」を Patrol にし、Text Colorは見やすい色、Font Scaleは読みやすい大きさにします。このTaskは、実行中の状態を文字で確認するために使います。

Patrolの「Transitions」に最初から項目があれば、この段階では削除します。Enter Conditionsも空にします。状態を1つだけ使い、表示を続ける形です。

Rootの子PatrolにDebug Text Taskを置き、レベルのCubeでPatrolの表示を確認する

StateTreeとBlueprintをコンパイル・保存し、Playします。Cubeの近くで Patrol の文字が出れば、アセットを作っただけでなく、配置したActorで実行できたことを確かめられます。

表示されないときは、アセットの割り当て、Schema、Context Actor Class、コンパイル結果を確認してください。Debug Text TaskにActorの指定欄がある場合は、ContextのActorを選びます。これは開発中の確認表示で、完成したゲームのUIは別に用意します。

Sponsored

実践:発見・見失い・再発見を試す

ここから状態を3つに増やします。Nキーを押すたびに、PlayerVisibleをtrue/falseへ切り替えます。

状態今回表示するもの終わるきっかけ
PatrolPatrol(巡回)見つけたらChaseへ
ChaseChase(追跡)見失ったらSearchへ
SearchSearch(警戒待機)5秒たったらPatrolへ。再発見ならChaseへ

今回は「巡回」「追跡」という判断の変化を文字で確かめます。Cubeを歩かせる処理は、後半の「移動する敵へ広げるとき」で考えます。

1. Nキーで「見える」を切り替える

BP_GuardStateDemoの「Class Defaults」で「Auto Receive Input」を Player 0 にします。これで、Play中にこのActorがキー入力を受け取れます。実験用のActorはレベルへ1体だけ配置してください。

Event Graphで次の接続を作ります。

  1. Nイベントを置き、「Pressed」から Set PlayerVisible へ白い実行線をつなぐ。
  2. PlayerVisibleのGetから NOT Boolean を作る。
  3. NOTの出力をSet PlayerVisibleの値の入力へつなぐ。

NOTはtrueとfalseを反転する処理です。最初がfalseなので、1回押すとtrue、もう1回押すとfalseになります。

NキーのPressedで、PlayerVisibleの現在値をNOTで反転して保存する

ここで変更するのは「見えるか」という事実だけです。NキーからPatrolやChaseを直接指定せず、次に作るStateTreeの条件に判断させます。

2. ChaseとSearchを追加する

Rootの下に ChaseSearch を追加します。Patrol、Chase、Searchの3つを、同じ深さの子として上から並べてください。Patrolの中へChaseを入れないようにします。

StateTasks
PatrolDebug Text Task:Text=Patrol
ChaseDebug Text Task:Text=Chase
SearchDebug Text Task:Text=Search、Delay Task:Duration=5.0

SearchのDelay Taskは、5秒たつと完了するTaskです。時間にばらつきを付ける項目があれば0、無期限に待つ項目があればオフにします。3つのStateのEnter Conditionsは、今回すべて空です。

次の図では親子関係を見やすく横に広げています。エディタの一覧では、Rootの下にPatrol、Chase、Searchが同じ深さで並べば大丈夫です。

Root直下に3つのStateを並べ、Searchの中では表示と5秒待機を並行して行う

3. PlayerVisibleを、遷移の条件へ結ぶ

Patrolの「Transitions」に項目を1つ追加し、次のように設定します。

項目
TriggerOn Tick
Transition ToChase
ConditionsBool Compare
Bool CompareのLeftBind → Actor → PlayerVisible
Bool CompareのRighttrue(オン)
Invertオフ

Binding(バインディング)は、値の渡し元を指定することです。「Leftには、このActorのPlayerVisibleを読んで入れてね」と結びます。左にtrueを手入力すると、プレイヤーの入力と関係なく、常にtrueとの比較になるので注意してください。

ActorのPlayerVisibleをBool CompareのLeftへ結び、Rightのtrueと比較する

On Tickは、StateTreeの更新時に条件を確かめるきっかけです。今回はtrueになったら、PatrolからChaseへ移ります。更新されるたびに無条件でChaseへ入り直す、という意味ではありません。

同じ方法で、残りの遷移も作ります。比較に使うLeftは、すべてActorのPlayerVisibleです。

遷移元Trigger条件行き先
PatrolOn TickPlayerVisible=trueChase
ChaseOn TickPlayerVisible=falseSearch
SearchOn TickPlayerVisible=trueChase
SearchOn State CompletedなしPatrol

falseと比べる行はRightをオフにします。すべてInvertはオフです。最初からあったRootへ戻る遷移などは削除し、表の4本だけにそろえてください。

Searchの2本は、再発見してChaseへ移る遷移を上、完了してPatrolへ戻る遷移を下に並べます。Priorityを指定できる場合は再発見をHigh、完了をNormalにし、同時に成立した場合は再発見を優先させます。

発見でPatrolからChaseへ、見失いでSearchへ、Searchからは再発見と5秒完了で行き先が分かれる

4. 5秒待つ途中で、もう一度見つける

コンパイル・保存してPlayし、ゲーム画面をクリックしてからNキーを押します。

操作表示の変化
Play直後Patrol
Nを1回押すChase
もう1回押すSearch
そのまま5秒待つPatrol
Search中にNを押す5秒を待ち切らずChase
再びNを押して見失うSearchへ入り、新たに5秒待つ

ここではSearchへ入った時点から5秒を数えます。再発見して別のStateへ移れば、SearchのTaskもその状態を抜けます。次にSearchへ入ったときは、Delay Taskが改めて待ち始めます。

警戒待機の途中で再発見すると待機を中断し、次に見失ったときは新たに5秒を数える

同じStateに表示とDelayを入れたのは、「Searchと表示しながら5秒待つ」ためです。「文字表示が終わった後でDelayが始まる」という直列処理ではありません。

うまく切り替わらないとき

症状確認するところ
Nを押しても変化しないゲーム画面に入力が届いているか、Auto Receive Input、Pressedの白い線
Play直後からChaseになるPlayerVisibleの初期値、Bool CompareのLeftが固定値になっていないか
Actorの変数をBindで選べないContext Actor Class、変数の公開設定、BlueprintとStateTreeの再コンパイル
Chaseから戻れないChase→Searchがfalseとの比較になっているか
Searchへ入るとすぐ戻るDelayのDuration、不要な完了Taskや遷移を足していないか
Searchが終わらないDelayの無期限設定、On State Completedの行き先
想定外の状態になる3つが同じ深さか、初期選択でPatrolが先に並んでいるか

StateTreeのDebuggerでは、実行中のインスタンスを選び、どのStateが有効になったか、どの遷移を通ったかを確認できます。まずは文字表示で一巡を確かめてから、変化の履歴を追うと読みやすくなります。

Sponsored

データはどこに置き、どう渡すか

今回のPlayerVisibleは、StateTreeの中ではなくBP_GuardStateDemoの変数です。Nキーがその値を変え、StateTreeがContextのActorを通して読みます。

Parametersもありますが、Blackboardと同じ箱だと考えるより、値の役割で分けると理解しやすくなります。

データ使い方の例
Parametersこの見張りに渡す待ち時間や速度などの設定
Context実行相手のActorやAI Controller、その相手が持つ値
TaskやEvaluatorの出力処理で選んだ目的地、集めた周囲の情報など

Evaluatorは、条件やTaskが使う情報を用意するためのものです。今回はActorに必要な値があるため、自作Evaluatorを足さず、直接Bindしました。

図のParametersは、設定を外から渡す場合の例です。今回は待ち時間をDelay Taskへ直接入力しているので、Parametersの追加は不要です。

入力操作がActorの変数を変え、ContextとBindingを通してStateTreeが読む

別々のStateへ同名の変数を作っても、自動で同じメモにはなりません。「値を変える場所はどこか」「条件が読む渡し元はどこか」を決めてから結びます。

移動する敵へ広げるとき

状態の変化が分かったら、発見の入力をAI Perceptionへ、状態中の処理を移動やアニメーションへ広げられます。

AI Controllerで動かす場合は、StateTree AI ComponentとStateTreeAIComponentSchemaを組み合わせます。今回のActor用アセットを、そのまま別Schemaのコンポーネントへ割り当てる手順ではありません。AI用のContextに合わせ、感知結果や移動相手のBindingも設定します。

今回の模型動く見張りへ広げるとき
NキーでPlayerVisibleを反転視覚の発見・見失いで更新する
状態名のDebug Text移動、待機、アニメーションなどのTaskを用意する
Searchで5秒待つ調査先へ移動した後で、5秒待つ状態を作る
CubeのActorAI Controllerが操作するCharacterを用意する

移動にはNavMeshとAI Controllerの設定も必要です。Behavior Tree版から試す場合は、Run Behavior TreeとStateTreeの両方から同じ敵へ移動指示を出さないよう、実験用のControllerを分けます。

「移動してから待つ」は、状態を分ける

1つのStateにMove ToとDelayを並べると、移動と待機を同時に始める構成になります。目的地まで歩く間に5秒が終わっては、到着後の調査になりません。

そこで、親State「調査」の下に「移動」「待機」を置きます。移動の成功で待機へ移り、待機の完了で巡回へ戻る形です。移動失敗の行き先も決めます。

移動と待機を同じ状態に並べる場合と、子状態を分けて順に実行する場合の違い

StateTreeには、子が有効な間、その親も有効になる階層があります。「調査中なら、移動中でも待機中でも再発見で追跡へ戻る」という共通の遷移は、親へまとめる設計ができます。

これはAI Perception記事の「調査先へ着いてから5秒待つ」動きへ広げるための構成例です。今回動かした模型の5秒は、見失ってSearchに入った時点から数えるものです。

どちらを選ぶか

StateTreeかBehavior Treeかを選ぶときは、作りたい敵の仕様を短く書いてみましょう。

仕様の見方試しやすい選択
「待機中に発見したら追跡」「詠唱完了で攻撃」のように、状態の切り替わりを追いたいStateTree
「回復を最優先、次に退避、その次に攻撃」のように、優先順位を枝で読みたいBehavior Tree
すでに動いているAIや、参考にするプロジェクトがあるその構成を土台にする
状態が少なく、短いBlueprintで十分読めるEnumとSwitchのまま始める

どちらでも複雑な行動は表せます。まずは小さな題材を作り、条件を一つ足したときに、変更する場所が分かるかで判断すると、自分のゲームに合う形を選びやすくなります。

仕様の書き方から、StateTree・Behavior Tree・既存構成・EnumとSwitchのどれを選ぶか

おまけ:先に知っておくと良いこと

  • 遷移条件の置き場所に気をつける: StateTreeは「いまどの状態か」が読みやすい代わりに、Transitionを置く場所を間違えると戻れない状態になります。共通の中断条件は親側へ置く と管理しやすくなります
  • データはバインディングで渡す: ParameterやContextを使い、Task間はバインディングでつなぎます。Blueprint変数を経由すると、どの状態がどれを読んでいるか追いにくくなります
  • Behavior Treeと役割で選ぶ: 判断を木で組みたい敵AIはBehavior Tree、状態遷移がはっきりした長寿命のロジックはStateTreeが向きます。両方を無理に置き換えず、状態の効果が長いものから 試すのが現実的です
  • AI以外にも使える: 扉やギミックの状態管理にも使えます

まとめ

StateTreeでは、状態にTaskを置き、Transitionで次へ移るきっかけを決めます。Actorの情報はBindingで渡し、同時に行う処理と、順番に行う処理を分けて組みます。

今回の見張りで、Searchの待ち時間を5秒から2秒へ変えてみてください。再発見への反応は保ったまま、巡回へ戻る早さを変えられます。こうして状態の変化を確かめながら、移動や感知を一つずつ足していきましょう。

参考資料

Unreal Engine このセクションのノート98