最初に
どうも、ろっさむです。
今回はUE4でパトロールを行う敵AIを作る手法についてまとめようと思います。
本記事では Behaviour Tree
関連や AIPerception
に関する詳細な説明は省きますのでご了承下さい。
興味がある方は以下の記事を参照してください。
【UE4】味方AIの作り方!AIとは何かを学びながら、ブループリントで味方キャラクターを実装しよう
作り方
BTとBBの作成
まずは敵用のBTとBBを作成してください。
BT_Enemy
の方ではBB_Enemy
を設定しておいてください。
今回、BB内で必要となる情報はPlayerのActor情報と、移動地点のVector情報です。
予め設定しておきましょう。
New Key
を押下し、Key Type
をObject
、Base Class
を Actor
にしてPlayerActor
という名前のKeyを作成してください。
また、同様にNew Key
を押下し、Key Type
をVector
にしてRoutePoint
という名前のKeyを作成してください。
これでBBの設定は終わりです。
次にBTの設定を行いましょう。
敵AIのBTの土台を構築
今回のAIでは敵がプレイヤーを発見しているかどうかで行動パターンが異なってきます。
ですので最初のコンポジットノードは Selector
に設定しておきましょう。
ノード名をわかりやすい任意の名前に変更しておいてください。
まずはパトロールを行うパターンを作っていきたいと思います。
コンポジットノードの Sequence
を追加して、Selectorから繋いでください。
パトロールを行っているということは、まだプレイヤーは未発見状態です。
ですので、BBの PlayerActor
には何もSetされていないということになります。
デコレーターノードを使用してプレイヤーが未発見の状態の時のみ、この Sequence
ノードを通るようにします。
このSequence
では、敵は設定されている RoutePoint
を移動することになります。このRoutePoint
は後程設定するように処理を作成します。MoveTo
ノードを追加して、Blackboard Key
をRoutePoint
に設定しておいてください。また、 Acceptable Radius
もできるだけ小さい値にしておいてください。
設定後はSequence
に繋いでください。
次に、プレイヤーを発見した際に、プレイヤーを追従するパターンを作ります。
新しくSequence
を追加してわかりやすい任意の名前を設定した後、Selector
に繋いでください。
こちらのパターンは単純にプレイヤーを追うだけなので、MoveTo
ノードを追加して Blackboard Key
を PlayerActor
に設定するだけです。Acceptable Radius
は任意の値に設定してください。
設定が完了後、Sequence
ノードに繋いでください。
これで一旦土台は完成です。
使用している敵AIControllerのBeginPlay
にてBTを走らせる処理を入れておきましょう。またここでBBの変数を作成し、BBの参照を持っておいてください。
敵に視覚情報を追加する
敵のAIController用BPを開いて、Add Component
から AIPerception
を追加してください。
追加したら、AIPerception
のDetails
を確認し、Scenes Config
の+ボタンを押下してAI Sight config
を追加してください。
これで視覚の機能が備わったことになります。
Sight Radius
は敵を中心とする視覚範囲内の半径にプレイヤーが入ってきたら検知するために使用します。
Lose Sight Radius
はその逆で、この半径を超えると見失ったことになります。
この二つは任意の値に設定しておいてください。
また、Detection by Affiliation
は全てにチェックマークをつけてください。プレイヤーかどうかの判断は今回はキャストで行います。
設定ができたら、OnPerceptionUpdated
とOnTargetPerceptionUpdated
の二つのEventを追加します。前者はプレイヤーを発見した際に使用し、後者はプレイヤーを見失った際に使用します。
まずはOnPerceptionUpdated
を作っていきましょう。
「On Perception Updated」からは発見したアクター群の配列が出力されているため、一つ一つPlayerのBPであるかどうかを確認していく必要があります。ただし、Playerを見つけることができたらその後の処理は行う必要がないため、途中でループ処理を中断できる「ForEachLoopWithBreak」を呼び出します。
"Array Element"として出力されているActorがどのように発見されたか知ることができる「Get Actors Perception」という関数があります。この関数にActorを渡し、ターゲットとして自身のコンポーネントであるAIPerceptionを繋げます。
出力された"info"というデータを分解し、"Last Sensed Stimuli"という配列の0番目(つまり視覚)を取り出します。
これを更に分解し、要素の最後に存在している"Successfully Sensed"(感知が成功しているか)をブランチに繋ぎ成功していれば、次はプレイヤーを登録する処理に移っていきます。
感知されたActorがプレイヤーBPであれば、ブラックボードのキー「PlayerActor」に設定を行います。
設定後は、もう検索ループを掛ける必要がないので、ノードを先ほどの「ForEachLoopWithBreak」にある"Break"へと繋ぎます。
次に、プレイヤーを見失った場合の処理を作成しましょう。
OnTargetPerceptionUpdated
の実装に移ります。
こちらでは出力されている Simulus Successfully Sensed
がfalse
であれば、BBからPlayerActor
の参照を外すという処理だけを行っています。
これでAIController側での処理は完了です。
Splineを使用してパトロールルートを設定する。
Spline Component
を使用することでレベル上に曲線や直線を設定して、今回のAIのようなパトロールルートとして扱うことができます(もちろんそれ以外にもレベル作成周りとかでバリバリ使えます)。
まずはActor
を継承したBPを作成して BP_EnemyPatrolRoute
と名付けます。
ダブルクリックでBPを開き、Add Component
から Spline
を選択します。
追加できたらレベル上に BP_EnemyPatrolRoute
を配置します。配置すると、ぴょろっと点と線が表示されているかと思います。
この点が後々パトロールポイントとなります。点は移動させたり回転させることができます。
点を増やしていくことでルートを作成していきます。
ちなみにルート作成するのにあたって、点を右クリックすると表示される Spline Generation Panel
が役立つかもしれません。
実際に使ってみるとよくわかるのですが、円や曲線、四角形などをぱっと簡単に作成してくれます。
また点の数や長さ、点の追加開始位置なども設定が可能です。
他にも点をもし上下で動かしてしまった場合などに、点を選択して右クリックするとFloorへスナッピングなどできます。
点をもりもり増やしてパトロール経路を作成しましょう....
こんな感じにしてみました。
次の段階に進みましょう。
敵BPに経路用のアクターを設定
パトロールの経路を作成した後は、敵側でそのパトロール経路を取得できるようにしましょう。
任意の敵BPを開いて、変数を追加してください。
名前をRouteActor
にして、型を先ほどパトロール経路作成用のActor、BP_EnemyPatrolRoute
に設定します。
また、レベル上に敵BPを配置した際にエディタ側から設定ができるように Instance Editable
にチェックをつけておいてください。
設定ができたらコンパイルして、レベル上に敵BPをパトロール経路近くに配置してください。
配置したら Details
から先ほどの変数RouteActor
を検索し、レベル上に設置しているBP_EnemyPatrolRoute
を設定してください。
これでレベル上での下準備は終わりです。
BT上でのサービスノードでパトロールポイントを更新する
先ほど作成したBT_Enemy
を開いてください。
開いたら、上部メニューのNew Service
を押下してBTService_BlueprintBase
を継承したサービスノードを新規で作ってください。
名前をBTS_UpdatePatrolPoint
としておきます。
BTS_UpdatePatrolPoint
を開いてください。
Functionsのoverride
からEvent Receive Tick AI
というサービスノード自身がアクティブ状態の際のTickと、Event Receive Activation AI
というサービスノードがアクティブになった際に呼び出されるメソッドを選択してください。
次に今回必要な変数を定義しておきます。
Integer
型のCurrentPatrolPoint
という名前の変数を作成してください。
それから、BBの方で定義しているRoutePoint
に値を設定するために、BT側からKeyを登録するためのBlackboard Key
型のRoutePointKey
という変数も作成してください。
ではまず、Event Receive Activation AI
にて初期化処理から実装していきます。
ただ今回は初期化時にパトロールポイントをランダムに設定するための処理をしています。もし不必要な場合はこのイベント自体なくても大丈夫です。消しちゃってください。
まずは、Controlled Pawn
から敵BPにキャストを行います。成功したらパトロール経路用に設定したRouteActor
からSplineコンポーネントをgetしてください。getしたら、そのSplineで設定されている点の数が何個あるかを取得できるGetNumberOfSplinePoints
を追加します。
この値をMAX値としてランダムなint型を生成するRandomInteger
を繋げ、出力された数値をCurrentPatrolPoint
に設定します。
これで初期化処理は完了です。
次はサービス自体のTick処理です。
初期化処理時と同様にControlled Pawn
から敵BPにキャストを行います。成功したらパトロール経路用に設定したRouteActor
からSplineコンポーネントをgetしてください。getしたら、Splintで設定した何番目の点の位置を取得するかというGetLocationSplinePoint
を追加してください。ここでのPointIndex
にはCurrentPatrolPoint
を設定します。またCoordinateSpace
はWorld
を設定しておいてください。
取得したVector値をBlackboardのRoutePoint
に設定します。Key
にはRoutePointKey
を設定してください。
最後に次のパトロールポイントへ進むための準備をします。CurrentPatrolPoint
をインクリメントして、もしその結果がSpline上での最後のポイントであれば最初のポイントを設定するように0
に初期化します。
これでサービスの処理は完了です。
BTに戻って、パトロール中のSequence
ノードに追加してください。
仕上げとして、BTにて値が切り替わったら処理を中止するように変更
現在の状態でも動くには動きますが、パトロール場所へ移動中にプレイヤーを見かけても無視して移動が終わってから追いかけようとしたり、プレイヤーを見失うことができません。
これを可能にするために、デコレーターに細工をします。
Notify Observer
をOn Value Change
にし、Observer aborts
をBoth
にしておきます。こうすることで、PlayerActor
の値が変わった瞬間に現在実行しているタスクの処理は中断されROOtから判定し直しとなります。Both
は二つのSequence
どちらも中断の対象となるように設定する項目です。
これでBTは完了となります。
結果
実際に実行してみてください。以下のツイートが実装例となります。(Gifでは限界がありました)
Splineを使ってランダムにSplineの点パトロールする敵を作ったー
— ろっさむ (@4_mio_11) November 12, 2020
視界にプレイヤーが入ったら追いかけてくるけど一定距離離れたら(見失ったら)パトロールに戻る pic.twitter.com/0Nd8UlMpKX