【Unreal Engine】Behavior Tree入門:巡回する敵が、プレイヤーを見つけて追ってくる

作成: 2025-12-12最終更新: 2026-09-06

巡回する敵がプレイヤーを見つけると追跡し、見失うと巡回へ戻るAIを作ります。SelectorとSequence、Blackboard、Taskの実行中・成功・失敗を図解し、途中で行動を切り替えるObserver Abortsまで説明します。

敵が部屋を歩き回り、プレイヤーを見つけると追ってくる。壁の裏に隠れると、追うのをやめてまた歩き出す。こうした敵を作るときは、移動そのものに加えて「いつ、どの行動へ切り替えるか」を考える必要があります。

Behavior Tree(ビヘイビアツリー) は、その行動の選び方を木の形で組む仕組みです。「相手が見えているなら追う。そうでなければ巡回する」という方針を、枝の条件と並び方で表せます。

他のエンジンでいうと: Unity にも Godot にも、これに当たる標準機能はありません(アセットや自作で補います)。 エンジンに最初から入っている ことが、UEでAIを組むときの利点になります。

この記事では、まず巡回だけを動かし、次にプレイヤーの発見と追跡を足します。最後は近づいたときに「攻撃」と表示させ、行動が切り替わったことまで確かめましょう。

敵AIの方針を、条件と行動の木として整理するイメージ

この記事でわかること

  • AI Controller・Behavior Tree・Blackboardの役割分担
  • SelectorとSequenceで木を読み解く方法
  • 巡回だけを先に動かし、あとから追跡の枝を足す手順
  • Observer Abortsで、行動の途中でも切り替える仕組み

この記事で作るもの

  • 周囲の行き先を選び、移動して2秒待つ敵
  • プレイヤーを見つけると巡回を中断して追い、近づくと攻撃のログを出す
  • 見失うと追跡を中断して巡回へ戻る

UE5のThird Personテンプレートを使う単独プレイの例です。攻撃アニメーションやダメージは、このAIの判断が動いてから追加できます。

Sponsored

AIを動かす3つの役割

Behavior Treeだけでは、画面の敵は動きません。操作する役、行動を選ぶ役、判断材料を覚える場所を組み合わせます。

AI Controller・Behavior Tree・Blackboardと敵の体の役割分担
部品今回の役割
AI Controller敵の体を操作し、Behavior Treeを起動する
Behavior Tree条件を見て、巡回や追跡などの行動を選ぶ
Blackboard「誰を追うか」「どこへ歩くか」を保存する

操作される体は Pawn と呼びます。今回使う Character は、歩行などの移動機能を持つPawnの一種です。「AI Controllerが中の人、Characterが体」と考えると、役割を分けやすくなります。

Blackboardは、AIが読む共有メモのようなものです。たとえば、目で相手を見つける処理が「追う相手」を書き込み、追跡の処理がそこを読んで移動します。相手を見つけた処理から、巡回・追跡のすべてへ個別に指示を送る必要がなくなります。

SelectorとSequence:木の読み方

木は上の Root(入口) からたどります。同じ親につながる子は、左から順に試します。まず覚えたいのが、子の結果をどう扱うかを決める2種類です。

Selectorは成功したところで終わり、Sequenceは失敗したところで終わる
種類子が成功したら子が失敗したら
Selectorそこで成功して終わる右の子を試す。全部失敗なら失敗
Sequence右の子へ進む。全部成功なら成功そこで失敗して終わる

Selectorは「どの行動を選ぶか」 を表します。左に「プレイヤーを追う」、右に「巡回する」を置けば、追う条件が成立する限り、そちらが先に選ばれます。

Sequenceは「選んだ行動を、どんな手順で進めるか」 を表します。巡回なら「行き先を決める→そこまで移動する→少し待つ」です。移動が失敗した場合は、その後の待機へ進まず、この手順を失敗として終えます。

「実行中」は、成功でも失敗でもない

移動など、ひとつの行動を担当する部品を Task と呼びます。敵が目的地へ歩いている間、移動のTaskは 実行中(Running / In Progress) です。移動指示を出しただけでは成功にならず、到着するまで次のTaskへ進みません。

Move Toは到着するまで実行中になり、成功してからWaitへ進む

これはSelectorでも同じです。左の追跡が実行中だからといって、右の巡回も始めるわけではありません。ひとつのTaskの結果を待ち、成功か失敗が決まってから先へ進むのが、今回の木の基本です。途中で行動を変える方法は、後半で足します。

Task・Decorator・Serviceの違い

木の部品には、行動するものだけでなく、条件を確認するものもあります。

Taskは行動、Decoratorは枝に入る条件、Serviceは枝の実行中に繰り返す確認を担当する
部品役割
Task行動を実行するMove Toで移動、Waitで待機
Decorator枝やTaskを実行してよいか確かめる「追う相手がいる」なら通す
Service取り付けた枝が動いている間、繰り返し確認する一定間隔で相手との距離を測る

Decoratorは条件、Taskは行動と分けると、木を読んだときに判断の流れが見えます。ただし、Taskが実行直前に対象を確かめ、無効なら失敗を返すこともあります。

Serviceは、その枝を離れると止まります。どの行動中でも必要な確認なら、両方の枝をまとめる親へ付けます。今回の発見と見失いは、後ほど説明するAI Perceptionの通知を使うため、Serviceを自作する必要はありません。

敵と、歩ける場所を用意する

敵の見た目と操作役を作る

Third Personテンプレートを開き、作業用に「AI」フォルダを作ります。

  1. 「BP_ThirdPersonCharacter」を複製し、「BP_Enemy」と名付けます。敵の見た目や歩行アニメーションを流用できます。
  2. BP_Enemyを開き、EventGraphの入力処理などのノードを削除します。複製元のプレイヤーはそのまま使います。
  3. 新しい「Blueprint Class」を作り、「All Classes」で「AIController」を検索して選びます。名前は「BP_EnemyAIController」です。
  4. BP_Enemyの「Class Defaults」で、下表の操作設定を指定します。Character Movementの2項目は、「Components」でそのコンポーネントを選んで設定します。
設定
AI Controller ClassBP_EnemyAIController
Auto Possess AIPlaced in World or Spawned
Auto Possess PlayerDisabled
Use Controller Rotation Yawオフ
Character Movement → Orient Rotation to Movementオン
Character Movement → Max Walk Speed300

Possessは、ControllerがPawnの操作を受け持つことです。Auto Possess AIの設定により、レベルへ置いた敵にも、あとから生成した敵にもAI Controllerが付きます。向きの設定は、歩く方向へ敵を向かせるためのものです。UEの距離は標準ではcmなので、Max Walk Speedの300は秒速3mにあたります。

AIが歩ける地図を作る

レベルの平らな場所へ敵を1体置き、「Nav Mesh Bounds Volume」を追加して、敵とプレイヤーが歩く床全体を覆います。P を押し、床に緑の範囲が表示されるか確認してください。

この NavMesh は、AI用の「歩ける場所の地図」です。見た目が床でも、ここに地図がなければMove Toで歩けません。緑にならない場合は NavMeshの記事 を先に確認しましょう。

NavMeshのある床では、障害物を避けた移動経路を探せる

BlackboardとBehavior Treeを作る

コンテンツブラウザを右クリックし、「Artificial Intelligence」から「Blackboard」と「Behavior Tree」を作ります。名前は「BB_Enemy」「BT_Enemy」です。

BB_Enemyを開き、「New Key」で次の2項目を作ります。Keyは、保存する項目の名前です。

Key名型と設定保存するもの
TargetActorObject、Base ClassをActor追いかける相手
PatrolLocationVector次に歩く座標

Object には、「レベルにいる、あの相手」を指し示す参照を入れます。名前の文字列を入れる欄ではありません。Vector はX・Y・Zの3つの値で、ここでは場所を表します。

BT_Enemyを開き、「Blackboard Asset」にBB_Enemyを指定して保存します。これで、木から2つのKeyを選べるようになります。

Sponsored

まず巡回だけを動かす

行き先を選ぶTaskを作る

BT_Enemyの「New Task」から「BTTask_BlueprintBase」を選び、「BTT_FindPatrolLocation」と名付けます。Task側で変数「PatrolKey」を作り、型を Blackboard Key Selector、「Instance Editable」をオンにします。

Blackboard Key Selectorは「どのKeyへ書くか」を選ぶための型です。Instance Editableを有効にすると、木に置いたTaskの詳細から「PatrolLocation」を指定できます。

TaskのEventGraphに「Event Receive Execute AI」を追加します。このイベントが「このTaskを始めて」という合図です。「Controlled Pawn」には、操作している敵の体が渡ってきます。

まずは次の接続で、書き込む座標を選びます。

  1. Controlled Pawn → Get Actor Locationの「Target」。敵の現在位置を取得します。
  2. 現在位置 → Get Random Reachable Point in Radiusの「Origin」。「Radius」は1000(半径10m)にします。
  3. 「Select Vector」を追加し、「A」へRandom Location、「B」へ敵の現在位置、「Pick A」へ探索のReturn Valueをつなぎます。
探索成功なら見つかった地点、失敗なら現在位置を選ぶ

Get Random Reachable Point in Radiusは、周囲から歩いて行ける地点を探します。Return Valueがtrueなら見つかった、falseなら見つからなかったという意味です。Select VectorはtrueならA、falseならBを選ぶので、失敗時には現在位置を行き先にできます。

次に、選んだ座標を書いてTaskを終えます。

  1. Receive Execute AIの白い実行線 → Set Blackboard Value as Vector → Finish Execute。
  2. Set Blackboard Value as Vectorの「Key」へPatrolKeyのGet、「Value」へ先ほどのSelect VectorのReturn Valueをつなぎます。
  3. Finish Executeの「Success」をオンにします。
選んだ座標をPatrolKeyの項目へ書き、Finish ExecuteでTaskの成功を返す

Finish Executeは、Taskが終わったことを木へ返すノードです。ここでの成功は「行き先を書けた」という意味で、敵が到着したという意味ではありません。移動は次のMove Toに任せます。

探索に失敗した場合も、現在位置へ移動したあとで待ち時間に入れます。すぐに探索をやり直し続けるのを避けられます。Compileして保存してください。

巡回の木を組み、起動する

BT_EnemyでRootから線を引き、Selectorを追加します。その子にSequenceを置き、「Node Name」を「巡回する」にします。Sequenceの子を、左から次の順で並べます。

BTT_FindPatrolLocation → Move To → Wait

ノード設定
BTT_FindPatrolLocationPatrol KeyをPatrolLocation
Move ToBlackboard KeyをPatrolLocation、Acceptable Radiusを50
WaitWait Timeを2.0、Random Deviationを0
巡回のSequenceは、行き先の決定・移動・2秒の待機を順に実行する

Move Toの Acceptable Radius は、「どれだけ近づけば到着とみなすか」です。位置を完全に一致させようとせず、ある程度近づいたところで止めます。

最後にBP_EnemyAIControllerのEventGraphへ「Event On Possess」を追加し、「Run Behavior Tree」へつなぎます。「BTAsset」はBT_Enemyです。

AI ControllerのOn PossessからBT_Enemyを起動する

On Possessは、操作するPawnを受け持ったときに呼ばれます。この時点で木を起動すれば、どの敵を操作するかが決まった状態で巡回を始められます。

Compile・保存してPlayしましょう。敵が歩き、到着すると2秒待ち、また次の場所へ歩けば成功です。 まだプレイヤーを見つける処理はないので、近づいても巡回を続けます。

プレイヤーを見つけたら、Blackboardへ覚える

巡回が動いたら、敵に視覚を足します。AI Perception は、見えた相手や聞こえた音などの情報を受け取る仕組みです。今回は視覚だけを使います。

視線が通ると相手を覚え、壁で遮られると相手を消す

見える距離と角度を設定する

BP_EnemyAIControllerに「AI Perception」コンポーネントを追加します。「Senses Config」に「AI Sight config」を1つ追加し、次の値を設定します。

項目値と意味
Sight Radius1500:新しく相手を見つける距離の上限
Lose Sight Radius1800:見つけた相手を、距離で見失う上限
Peripheral Vision Half Angle60:正面から左右に各60度、合計約120度
Detection by Affiliation → Detect Neutralsオン:チーム設定をしていない相手も対象にする
Starts Enabledオン

Sight Radiusの1500は15mです。Lose Sight Radiusは、壁の裏でも見える設定ではありません。 視線を遮る壁で隠れれば、距離内でも見失います。検証用の壁は「Visibility」チャンネルをBlockするものにします。

プレイヤー側のBP_ThirdPersonCharacterでは、次を設定します。

  • 「Class Defaults」のActorの「Tags」へ「Player」を追加する。Component Tagsとは別です。
  • 「AI Perception Stimuli Source」を追加し、「Auto Register as Source」をオン、「Register as Source for Senses」へ「AISense_Sight」を追加する。

Stimuli Sourceは、感知される側として登録するための部品です。ここでPlayerタグも付けておくと、感知したActorがプレイヤーかどうかを判別できます。BP_EnemyにはPlayerタグを付けません。

通知から、相手と視覚の結果を受け取る

BP_EnemyAIControllerに関数「ApplySightUpdate」を作ります。入力は「SeenActor」(Actor Object Reference)と「bSensed」(Boolean)の2つです。Booleanはtrue/falseの値で、ここでは見えているかどうかを表します。

AI Perceptionコンポーネントの詳細にある「On Target Perception Updated」の「+」からイベントを追加し、ApplySightUpdateを呼びます。

  • イベントのActor → 関数のSeenActor。
  • Stimulus → Break AIStimulus → Successfully Sensed → 関数のbSensed。
感知通知のActorとSuccessfully SensedをApplySightUpdateへ渡す

Stimulusは、今回の感知結果をまとめたデータです。「Successfully Sensed」がtrueなら見つけた、falseなら見失った通知として扱います。ここでは視覚のみを設定しているため、音の通知が混ざることはありません。

相手を記録する・消す

ApplySightUpdateの中では、まずSeenActorを「Actor Has Tag」のTargetへ渡し、「Tag」をPlayerにします。結果をBranchのConditionへつなぎます。関数の入口から、このBranchへ白い実行線をつなぎ、Trueの場合だけ2つ目のBranchへ進めてください。2つ目のConditionにはbSensedを渡します。

SeenActorとbSensedは、関数の入口にある出力ピンから取り出せます。Actor Has Tagは値を調べるノードなので、白い実行線は通しません。 白い線は処理の順番、色付きの線は相手や判定結果などのデータ、と分けて読むと接続を追いやすくなります。

Playerタグがある相手について、見えたかどうかで処理を分ける

2つ目のBranchのtrue側では、「Get Blackboard」(TargetはSelf)の戻り値から「Set Value as Object」を作ります。「Key Name」はTargetActor、「Object Value」はSeenActorです。

発見時にGet BlackboardのTargetActorへSeenActorを保存する

false側では、「見失った相手が、いま追っている相手か」を確かめてから消します。

  1. Get Blackboardの戻り値から「Get Value as Object」を作り、「Key Name」をTargetActorにします。
  2. そのReturn ValueとSeenActorを「Equal (Object)」の2つの入力へつなぎます。このノードは、同じ実体を指していればtrueを返します。
  3. 3つ目のBranchを置き、Equal (Object)の結果をConditionへつなぎます。bSensedで分けたBranchのFalseから、白い実行線をつなぎます。
  4. 3つ目のBranchのTrueから「Clear Value」へ進め、「Target」にGet Blackboardの戻り値、「Key Name」にTargetActorを指定します。False側はつなぎません。
見失った相手が追跡中の相手と同じ場合だけTargetActorを消す

Get Value as Object、Set Value as Object、Clear Valueの Targetは、Get Blackboardで得たBlackboard Component です。これは、このAIのメモを読み書きするための部品です。敵のCharacterをつなぐ場所ではありません。

「追っている相手を見失ったときだけ消す」としておけば、別のActorの通知で追跡対象が消えるのを防げます。保存と消去の処理は、同じApplySightUpdate内で、bSensedのBranchのTrue側・False側へそれぞれつなぎます。

ここまでをCompile・保存します。Play中にBT_Enemyを開き、Blackboardの TargetActorが、発見時にプレイヤーになり、見失うとNoneへ戻れば成功です。Noneは「参照する相手が入っていない」状態です。敵の行動はまだ巡回のままでも構いません。

Sponsored

追跡の枝を足し、途中で切り替える

攻撃の代わりに、ログを出すTaskを作る

新しいTaskを作り、「BTT_Attack」と名付けます。「Event Receive Execute AI」から「Print String」へつなぎ、「In String」を「攻撃」にします。続いて「Finish Execute」へつなぎ、「Success」をオンにします。Compileして保存してください。

攻撃TaskはPrint Stringで攻撃と表示し、成功を返して終わる

これで、木が攻撃のTaskを選んだタイミングを画面やOutput Logで確認できます。今回はダメージを与えず、判断が正しく動くかを見ます。

巡回より左に、追跡の手順を置く

BT_EnemyのSelectorに、もうひとつSequenceをつなぎます。「Node Name」を「追って攻撃する」にし、巡回より左へ置いてください。子は「Move To」「BTT_Attack」「Wait」の順です。

左の追跡を優先し、条件が成立しなければ右の巡回を実行する木

追跡側のMove Toは、次の設定にします。

設定
Blackboard KeyTargetActor
Acceptable Radius200
Reach Test Includes Agent Radiusオフ
Reach Test Includes Goal Radiusオフ
Track Moving Goalオン
Allow Partial Pathオフ

到着判定へ双方のカプセル半径を加えない設定にし、約2mまで近づいたら次のTaskへ進ませます。Track Moving Goalは、移動中の相手に合わせて経路を更新する設定です。Allow Partial Pathは「目標の途中までしか行けない経路」を許可する項目で、今回はオフにします。

攻撃後のWaitは「Wait Time」を1.0、「Random Deviation」を0にします。相手が近くにいる間は、次の攻撃まで約1秒待つようになります。実際のダメージ処理を足す場合は、攻撃時にも相手・距離・向きを確かめる処理が必要です。

条件が変わったら、今の行動を中断する

「追って攻撃する」のSequenceを右クリックし、「Add Decorator」→「Blackboard」を追加します。以下を指定します。

設定
Blackboard KeyTargetActor
Key QueryIs Set
Notify ObserverOn Result Change
Observer AbortsBoth

Is Setは、追う相手が入っているかを確かめる条件です。しかし、条件を付けるだけでは、実行中の巡回を途中で止めることはできません。

そこで使うのが Observer Aborts(条件を監視して中断する設定) です。Bothには、今回ほしい2方向の中断が含まれます。

  • 相手が入ったら、右の巡回を中断し、左の追跡へ切り替える。
  • 相手が消えたら、この追跡の枝を中断し、巡回へ戻る。
Bothは発見時に巡回を、見失い時に追跡を中断して行動を切り替える

「見つける処理がBlackboardへ書く→条件が変わる→実行する枝が切り替わる」という流れになりました。視覚の処理は相手の情報を更新し、行動の切り替えは木に任せられます。

動かして、木と値を見比べる

BT_Enemyを開いたままPlayし、エディタ上部のデバッグ対象から、置いた敵の実体を選びます。実行中のノードの強調表示と、Blackboardの現在値を見ながら、次の順で試してください。

操作期待する結果
敵の視界へ入らずに待つ巡回のMove ToとWaitを繰り返す
敵の正面へ入るTargetActorにプレイヤーが入り、追跡へ切り替わる
近くで止まる「攻撃」が約1秒間隔で表示される
壁で敵の視線を遮るTargetActorがNoneになり、巡回へ戻る

一度Playを止め、Observer Abortsを「None」に変えて試すのも理解の助けになります。敵が巡回のWaitをしているときに正面へ出ると、相手の情報は更新されても、実行中の待機は中断されない様子を確認できます。見終えたらBothへ戻しましょう。

視界の外では巡回が、正面に入ると追跡が強調され、BlackboardのTargetActorも変わる
つまずき見るところ
敵が1歩も動かない緑のNavMesh、AI Controller Class、Auto Possess AI、On Possessの起動処理
巡回地点を選んだあと止まるTaskのPatrol KeyがPatrolLocationか、Finish Executeを呼んだか
TargetActorがずっとNonePlayerのActor Tag、Sightの距離と角度、Detect Neutrals、Stimuli Source
相手は入るが巡回を中断しない左右の枝の順、DecoratorのObserver Aborts
見失っても追い続けるClear Valueへ進んでいるか、そのTargetがBlackboardか
壁越しに見つかる壁のVisibilityがBlockか
攻撃が続けて表示される攻撃後のWait Timeが1.0か

敵を増やす場合、TargetActorは敵ごとに別のBlackboardへ入ります。確認するときは、デバッグ対象が意図した敵かも見てください。

見失ったあとに最後の場所を調べさせたい、音にも反応させたい場合は AI Perceptionの記事 へ進めます。隠れる場所を選ばせるなら EQS、状態と遷移を中心に組む方式も見たいなら StateTree が次の候補です。

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

  • 並び順がそのまま優先順位: Behavior Treeは左から順に試します。「見つけたら追跡」は「巡回」より に置きます。条件が合っていても、並びが逆だと期待どおりに動きません
  • 参照は最初にそろえる: AI Controller・Behavior Tree・Blackboardのつながりが1つでも抜けていると、見た目は組めていても動きません。Blackboard Keyの 名前と型 も、TaskやDecoratorと一致させます
  • 切り替わらないときはObserver Aborts: 追跡を即中断したい、見つけた瞬間に切り替えたい。こうした「途中で割り込む」動きは、Self / Lower Priority / Both の違いで決まります
  • 1つずつ足す: 巡回だけを先に完成させ、動くのを見てから追跡の枝を足します。まとめて組むと、どこが原因か分からなくなります

まとめ

Behavior Treeでは、Selectorで行動を選び、Sequenceでその手順を進めます。Blackboardに置いた判断材料をDecoratorが読み、Taskが移動や待機を担当します。

今回の敵で特に確かめたいのは、「プレイヤーを見つけた」という情報と、「巡回をやめて追う」という行動が分かれていることです。まず値が変わったか、次に枝が切り替わったか。この順で見れば、行動が増えても原因を追いやすくなります。

参考資料

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