はじめに
ブループリントを使用したAIの紹介という教材で勉強
ブループリントを使用した AI の紹介
プロジェクトと環境を作成する
プロジェクト作成
- ゲーム
- サードパーソン
レベル作成
- StaticMesh のスケールZ を 2.5 へ。地面に沿うように配置し直す
- StaticMesh を Alt + 移動でコピーして図のように合計9つにする
- 互いの設置物が重ならない程度に全体を移動させる
- ライトがベイクされていて、設置物を動かしたあとでも影が残っている。これから行う事に対して影響はないが気になるのでライトをビルドする
- あとはプレイしておかしなところがないか確認する
- 🤔関係ないですが、カメラにシャッタースピードがあるようで、いい感じにボケて躍動感が演出されてますね。
AIの理論
AI のプロセスは3つの主要なセクションに分けることができる
🤔ここは動画ではなく記事ですね。
基礎
- 知覚 SENSE
- AIの現在の状態のスナップショットを取得する為のもの
- 視覚や触覚のような物理的な感覚を含む
- または他のAIからブロードキャストされる「目に見えない」情報のようなより抽象的な情報
- フィルタリングも考慮される。別の感覚に優先度がある場合にある情報を無視するようなもの。
- 🤔映画 フォーカスでも人間は1つの事を考えてる時に別のこと考えられないと説明しながらスリを教えるシーンが有りましたね
- 思考 THINK
- AIについて考えるときは思考について考えがちだが、思考は大きな全体像の一部にすぎない
- 思考プロセスは知覚によって収集された情報を受け取り、現在の自分が置かれているワールドのビューを作り出す
- 自身の内的なゴールを使用する事で、思考プロセスは次に情報とゴールを受け取り行動の流れを決定する
- 行動 ACT
- 知覚で情報を受け取り思考による決定を受け取ると、行動は何らなの行動を行う
- 走ったりジャンプのような見えるものから、他のAIとのコミュニケーション、不可視のウェイポイントをドロップしたりといった目に見えないものもある
- 行動はAIにおける最も目に見えやすい部分
- 行動が終わるとループバックし知覚セクションを再開する
🤔ABC理論の勉強をしているようです
ループ
- 行動が発生すると知覚にループバックする。知覚セクション→思考セクション→行動セクションのループを繰り返す
- AIが有効である場合も上記のループを繰り返す
- 独立して動作するセクションはなく、相互に依存しながら動作する
- 情報なしでは決定せず、決定なしだと行動もしない。
AIキャラクターを作成する
- AIの構築を始める前にAIでコントロールする専用のキャラクターを作成する必要がある
- さらにそのキャラクターをAIでコントロールする事を明示的に定義する
- 上記の目的を達成するために、2つの新規BPを作成する
- さらにゲーム開始時にはAIコントローラーはAIキャラクターを所有できるようにする
ThirdPersonのコンポーネントについての解説
- ThirdPersonを選択して右クリック、アセットをブラウズしてコンテンツブラウザ上に表示させる
- ダブルクリックでBPを開くとイベントグラフが表示される
- これがプレイヤーの入力の処理に関するもの
- ビューポートに移動してコンポーネントの解説
これでキャラクターを構成する各コンポーネントとAIに関連するコンポーネントがわかりました。これらを頭に入れて既存のアセットをベースに独自のAIキャラクターを作成します
AIキャラクターの作成
- ThirdPersonCharacter を右クリック→複製
- 名前を AI_ThirdPersonCharacter にする
- AI_ThirdPersonCharacter をダブルクリックしてエディタを開く
- 必要のないもの全てを削除する
- F7 でコンパイルし問題が発生しない事を確認
- BPを保存しBPエディタを閉じる
AIコントローラーを追加
- コンテンツブラウザで新規ブループリントを作成。親クラスは AIController
- 名前を AIC_ThirdPersonCharacter とする
- AI_ThirdPersonCharacter をダブルクリックで開く
- PawnのセクションのAI Controllerの項目に作成した AIC_ThirdPersonCharacter を設定する
AIC_ThirdPersonCharacter の動作を確認
- レベルに配置する
- プレイしてAIキャラクターを見てみるとIDLE状態のまま特に動かない
ナビゲーションの理論
- シーン内でAIを動き回らせるために何が必要か?
- そしてなぜそれが単なるAからB地点のように単純でないのかについて見ていきます
- ワールド内でのナビゲーションを実現するために必要なアクタについての考察
- リアルタイムエクスペリエンスでは事前計算によるソリューションが適切である理由を理解
AIが障害物を避けてシーン内を動き回るためには情報が必要
- 環境内のナビゲーションはAIの基本タスクだが、単なるAからB地点の移動のように単純ではない
- ナビゲーションには2つの重要な要素がある
- パス検索
- 障害物回避
- このモジュールでは対象物は静止しているものとする
- AIがワールド内を動き回ることができるためにはナビゲーションメッシュが必要
ナビゲーションメッシュ
- 環境内でのナビゲーションを補助するために使用される
- メッシュとはAIが移動可能な領域を定義する凸型ポリゴンの集合
事前計算データ
- ナビゲーションメッシュは事前計算(ベイク)される。
- 事前計算されたナビゲーションを使うためには、レベルに Navmesh アクタを追加する必要がある
- シーンへのナビゲーションメッシュの追加は簡単
- ナビゲーションメッシュの生成は、プロジェクトでAIをセットアップする際に最も頻繁に行うタスク
ナビゲーションメッシュを生成する
- ナビメッシュバウンスボリュームを Modeパネルからレベルに配置する
- ブラシ設定を x 4000, y 4000, z 400 にする
- シーン全体を囲むようにナビメッシュを移動させる
- Pキーを押すとナビゲーションメッシュで移動可能となる範囲が可視化できる模様(黄緑の色)
- ナビゲーションをベイクする
まとめ
ベイクされたナビゲーションの長所と短所
- 長所
- 事前計算されたナビゲーションは高速で結果が予測可能
- 静的なオブジェクト回避により、負荷の高い実行時のコリジョン検出を回避
- 🤔?
- ナビゲーションの実装が簡単で全てのAIに対して有効
- 短所
- ナビゲーションメッシュは静的。実行前に領域が変更されない事が前提
- 🤔動くステージとかでは使えないということですね
- 静的なナビゲーションメッシュではプレイヤーやAIがのような動的なオブジェクトを回避できる利点はない
- 🤔?
- ナビゲーションメッシュ固有のインテリジェンスはない。パス検索アルゴリズムを含む、他のシステムからのデータに依存
- ナビゲーションメッシュは静的。実行前に領域が変更されない事が前提
NavmeshBounceボリュームを使用する
- 動画の最初でNavmeshBounceボリュームを生成する
- 🤔すでに前のモジュールで行ったことを行うため軽くスキップ
- パスをビルドする前なら障害物を動かすと自動的にパスが計算されナビゲーションも自動計算される
- 大きなレベルの場合この自動計算機能を無効化したい場合もある、それを無効化する機能がある
- 🤔動画の通りプロジェクト設定→レベルエディタとたどることはできず、項目名で検索しても出てこない
- 大きなレベルの場合この自動計算機能を無効化したい場合もある、それを無効化する機能がある
- 最後に厳密にコントロールする方法を確認する
- ナビメッシュモディファイアボリュームをナビメッスバウンスと連動させる事によって
- 複雑なシーンの場合に有用
- ナビメッシュモディファイアボリュームをナビメッスバウンスと連動させる事によって
ナビゲーションメッシュで利用可能な重要オプションの解説
- ナビゲーションメッシュのいくつかのプロパティはリキャストナビメッシュ(RecastNavMesh)を使用して調整可能
- NavmeshBounce Volumeを配置した場合、リキャストナビメッシュ(RecastNavMesh)も配置されている
- 詳細 → 表示 → オフセットを描画ではナビゲーションの表示を上下させることができる
- NavmeshBounce Volumeを配置した場合、リキャストナビメッシュ(RecastNavMesh)も配置されている
- ナビメッシュモディファイアボリュームについて
- 現時点では作成したナビゲーションメッシュはAIが移動できるのかできないのかの2択の情報しかない
- 上記をより細かく設定したい場合は?
- 例えば横切れなくもないが危険なルートである事を表現したいなど
- 上記のようなルートは他のルートがない場合のみに選択する
- この目的でナビメッシュモディファイアボリュームは使える
- これはモードパネルより配置する事が可能
- 設定は Area Class から行う
- マウスカーソルを合わせると解説が出てくるのでわかりやすい
- 別のパスが存在しない限り横断してはいけない設定に変更してみた図
- ナビメッシュモディファイアボリュームの解説が終わったようなので削除
- 例えば横切れなくもないが危険なルートである事を表現したいなど
NavMesh エージェント
- ナビゲーションメッシュで移動範囲を定義する事には成功したが、飛んでいたり泳いでいるキャラクターを追加したい場合はどうすればよいか?
RecastNavMeshについて
- RecastNavMesh-Default を選択する。
- 詳細パネルの世代(Generation)にはエージェントに関連する多くの項目がある
- エージェント半径 35.0(後で確認するためメモ)
- エージェントの高さ 144.0 とある(後で確認するためメモ)
- この設定が与える降下を確認するためナビゲーション表示フラグを有効にする。
- 画面緑の表示。なければPキー、または 表示 → ナビゲーション
- 詳細パネルの世代(Generation)にはエージェントに関連する多くの項目がある
エージェント半径について
- コンテンツブラウザのAI_ThirdPersonCharacter をダブルクリックしてエディタを開きカプセルコンポーネントのシェイプを確認
- カプセル半径 42.0 とあり、RecastNavMeshのエージェント半径よりも大きい
- RecastNavMeshのエージェント半径を42にする
- 可動範囲が狭まっている事がわかる
- 🤔一瞬?となったが、力士(半径大)と子供(半径小)がこの道を歩くなら力士の方が可動範囲が狭まりますね。
- この値を上げると小道から緑がなくなり可動範囲ではなくなる。
- 可動範囲が狭まっている事がわかる
エージェントの高さについて
- レベル上の適当なキューブを持ち上げる
- NavMeshが更新され持ち上げられた下の部分も可動範囲に変わっている事がわかる
- プレイヤーを持ち上げたキューブの下に移動させる
- キューブがそこまで上に持ち上がっていない為、キャラクターは地面にめり込んでいる
- この原因はエージェントの高さが適切ではないため
- コンテンツブラウザのAI_ThirdPersonCharacter をダブルクリックしてエディタを開きカプセルコンポーネントのシェイプを確認
- Capsule Half Height(カプセル半分の高さ) 96とあり2倍すると192、RecastNavMeshのエージェントの高さ144よりも大きい事がわかる。
- 🤔なぜに半分?半径に合わせたのかな
- Capsule Half Height(カプセル半分の高さ) 96とあり2倍すると192、RecastNavMeshのエージェントの高さ144よりも大きい事がわかる。
- RecastNavMeshのエージェントの高さを192にする
- もはやキューブの下が可動範囲でなくなったことが確認できる
エージェントの半径と高さはプロジェクト設定からも設定可能
- エージェント半径と高さを設定したが、プロジェクト設定からも調整可能
サポートされているエージェント設定について
- この値は配列
- 適当に2つ作成し、1つを展開した図
- エージェントの高さや半径があり
- ジャンプが可能かどうかを設定するフラグ等がある
- サポートされているエージェントは配列のため複数持つ事ができ
- それぞれが異なるNavMeshを使用する事ができる
- 🤔ここで飛んだり泳いだりのエージェントを設定するんですね
メッシュをナビゲートする
- ナビゲーションメッシュが定義できたので、次はそれを実際に使用する思考力をAIに設定する
- 簡単なところから始める
カスタムイベント作成
- AI_ThirdPersonCharacterのイベントグラフを開きカスタムイベントを追加。名前を RandomWander とする
- SimpleMovetoLocation を配置しRandomWanderの実行ポートと接続
- Simple Move To locationにはコントローラーとゴールの情報が必要
- コントローラーを取得する為、Get Controller を配置し、Simple Move to locationのControllerと接続
- デフォルトだとアタッチされているオブジェクトがターゲットになり、使用されているコントローラーを返す
- ここではAIのコントローラーを取得している
- ゴールを取得するため、GetRandomReachablePointInRadiusを配置
- GetRandomReachablePointInRadius の RandomLocation出力を SimpleMoveToLocation のGoalに接続
- ノードを見やすく再配置する
作成したカスタムイベントを呼び出す
- Event BeginPlay を配置
- Set Time By Event を配置し Event BeginPlay と接続
- タイマーで呼び出すイベントの情報が必要
- タイマーで呼び出すイベントの情報として RandomWander を接続
- どれくらいの間隔で RandomWander を呼び出すか設定するため、Time を 1.5(秒)に設定
- この関数にループしてもらうため、Loopingにチェックを入れる
プレイして確認
- コンパイルする
- プレイして確認すると、ランダムにAIが動き回っている事が確認できる
- 🤔との事だったが、微動だにしないため焦る
- 別プロジェクトを作成し、同じ手順で作成し直してコンパイルしても動かず
- 別プロジェクトでサポートされているエージェント設定を改めて RecastNavMesh を設定すると動いた
- 元のプロジェクトに戻り再現させるべくプレイすると何も設定変更していないのに動く。
- 再現できず原因特定できず。
- しかし動かないときは サポートされているエージェント設定 を見直すと動くことがあることがわかった
NavMeshとゲームプレイデバッガ
デバッガをアクティブにする
- プロジェクト設定 → エンジン → アポストリフィがデフォルトで紐付いている
- しかしアポストロフィーはコンソールにも紐付いている可能性もある
- 別のものに紐付けるか削除する。(私はアウトプットログから実行しているためこちらは削除した)
- ゲームをプレイ中にアポストリフィーを押す
- 🤔アポストロフィーだと何故か表示されないため、@マークで行うことに
デバッグ内容について
- 右上には現在選択されているデバッグアクタが示されている
- いろいろな表示
ctrl + ~
でHUDのオン/オフctrl + Tab
えDebugMessageの表示切り替え0
Navmesh の表示/非表示1
AI の表示/非表示- Controller Name: AIが使用しているコントローラー名
- Pawn Name: コントロールされているPawnの名前
- Movement Mode(移動モード): Walking
- など左上に記載のあることの説明
- 🤔
ctrl + ~
は機能しないですね。USキーボード前提になっていて勝手が違うのかな
AI Perception の理論
ワールドを知覚する
- AIがインテリジェンスを持っているという印象を適切に与えるには、AIが周囲の環境を知覚できるようにする
- UEのAI Perception システムはアクタが何を知覚し、ワールドのどの部分を知覚しているかを制御する
- 収集された情報はイベント駆動形のシステムによって処理されシンプルかつ軽量
- BPや様々なシチュエーションにおいて幅広く使用可能
AI Perception システムの構成要素
- 2つの主要なコンポーネントがある
- AI Perception
- どの感覚が使用可能か?つまり何を知覚できるかを決定する
- AI Perception Stimuli Source
- アクタにどの「知覚可能」なプロパティがあるかを決定
- AI Perception
AI Perception
- 知覚システムがどの知覚を使用できるか
- 何を「参照」できるかを設定する事が可能
- 1つのコンポーネントには複数の感覚を追加する事ができる
- Dominant Sense プロパティを使用して知覚されるアクタの場所を特定する際にある感覚を他の感覚よりも優先させる事ができる
- 例えば「視覚」を「聴覚」よりも優先させたいなどの使用例がある
AI Perception Stimuli Source
- AI Perception システムがどの感覚を登録するかを決定する
- 例えばAI Perception Stimuli Sourceを視覚の刺激として設定する
- 1つのコンポーネントには複数の刺激を登録する事ができる
イベント及び刺激
- ワールド内のアクタは刺激を発生する
- アクタがどの刺激を発生させるか設定
- AIは自身が知覚可能な情報に基づきその情報について思考し、必要に応じ行動を起こす
- On Target Perception Updated イベントを使用する
On Target Perception Updated イベント
- AlStimulus 構造体を通じて資格された刺激について有用な情報を提供する
- 他にもプロジェクトで使用できる便利な知覚イベントが有る
- イベントリストはAI Perceptionページで参照可能
AI Perception
AI Perception コンポーネントと、それを使用して AI に対する認識を生成する方法を説明するドキュメントです。
AIの知覚方法
- AIが何かを知覚するためには、ワールド内に Stimuli Sourceコンポーネントが存在する必要がある
- AIが知覚する情報をプレイヤーは出力する
- AI の視覚がAI Perception Componentに入る
- On Target Perception Updated イベントが発動する
AI Perceptionの設定
AI_ThirdPersonCharacterでの作業
- AI_ThirdPersonCharacter をダブルクリックしてエディターを開く
- コンポーネントを追加で AI Perceptionを追加
- 詳細パネル AI Perceptionの項で + (add)して、Ai Sight config を追加
- 追加したものを展開すると詳細が設定可能
- Sight Radius (視野半径) は最大視野距離を定義可能で、AIがターゲットを近くできる範囲を意味する
- Lose Sight Radius(視野喪失半径)はすでに見えている対象が見え続ける最大視野 距離を定義する
- Peripheral Vision Half Angle Degrees はAIの視野のコーンを定義する。
- キャラクターの前方ベクトルを基準としている
- 🤔視野角ですかね。馬の視野角は350度どかそういった感じの
- Detection by Affilation セクションを展開し Detect Neutrals(中立を検出)をオンにする
- デフォルトだと全てのオブジェクトは中立のため
- 🤔これはAIのチュートリアルなのでやらないが、敵、味方、中立など役割を付ける事ができて見つけられる対象もこれで設定可能のよう
- デフォルトだと全てのオブジェクトは中立のため
- Max Age はいつまで知覚し続けるかという時間
- Age は視認→消失のプロセスのうち消失してからの経過時間。視界に入っているうちは 0固定
- コンパイル→保存してエディターを閉じる
ThirdPersonCharacter(自キャラ)での作業
- ThirdPersonCharacterをダブルクリックしてエディターを開く
- コンポーネントを追加で AI Perception Stimuli Source を追加
- 詳細よりAuto Register as source にチェック
- Register as source for Sense の +(add)をクリックしてAi Sight config を追加
- コンパイル→保存してエディターを閉じる
AI Perception とゲームプレイ デバッガ
作業の前に
現在ランダムに動いているAIを無効化する
- Begin Play イベントから出ている接続を解除
- コンパイル→保存してエディターを閉じる
- プレイしても動き回っていない事が確認できる
Temporal AA(TAA)を無効化する
ゲームプレイデバッガーで Perceptionのみを有効にして表示する
- プレイ中に Numpad を押し 5 だけが有効になるようにする
- Sight = 1 は完全に見えている事を示している
- Age = 0 は現在の状態であり経過時間を示している
- 視界に入り続けている間はAgeは 0 のまま
- AIの視界から消える
- age が時間とともに上がっていくのがわかる
- Tabを押してから上空から見下ろすと、外側に2つの円があることがわかる
- AI_ThircPersonCharacter をエディターで開く
- Sight Radius 1000, Lose Sight Radius 1500に設定する
- コンパイル→保存してエディターを閉じる
- AI_Character をレベルの隅っこに配置し直す
- プレイ→ゲームプレイデバッガーで再度確認
- 視野が現実的なスケールになっている事がわかる
AI Perception イベントを使用する
- AIが知覚したものについての情報を収集する
- そのために On Target Perception Updated イベントを使用する
- デバッグモードでステップ実行し何が得られているかを見ていく
AI ThirdPersonCharacter セットアップ
- Perceptionイベントを設定する
- このイベントにはAIが使用する有益な知覚情報が含まれる Stimulus変数が渡されている
- AI_ThirdPersonCharacter をダブルクリックしてエディターを開く
- AI Perceptionを選択し、詳細のイベントから On Target Perception Updated の + をクリックする
- ノードが追加される 🤔この追加方法は初めてですね
- On Target Perception Updated の Actor ピンには知覚した対象のアクタへの参照が入っている
- Actor はBP内で頻繁に参照する為、変数へ昇格させ変数名は PerceivedActor とする
- 🤔Perceived Actor 認識された行動者という事ですね
- Stimulus を分解する
- 展開すると多くの情報が含まれている事がわかる
- ここでは単純に Successfully Sensed (Bool)にのみ注目する
- True は正常に認識したとき
- False は消失したとき
- ブランチを配置し、図のように接続
- Successfully Sensed の結果に応じて2つの処理を分岐させられるようになった
- 言い換えるとこれによって視認初めと消失のタイミングが得られるようになった
- Successfully Sensed の結果に応じて2つの処理を分岐させられるようになった
消失時のイベントを作成
消失後に暫く経つと探すのを諦めるようにする。そのためにカスタムイベントを配置しそのイベントが呼ばれた時にPerceived ActorをNullにする処理を作成
- Custom Event を配置し TargetLost という名前にする
- Set Timer by Event を配置し Branch のFalseとつなぎ Event には TargetLost とつなぐ
- 時間は5秒に設定する
- PerceivedActor を Null にするため変数をグラフにSet で配置
- Alt 押しながらドラッグで Set で配置
- TargetLost と接続する
- 何をセットするかを指定していない為、結果 Null となる
- 次に True パートの実装を行うが、1つの事のみ行う。それは False の際に設定したタイマーをキャンセルする事
- Clear and Invalidate Timer by Handle を配置する
- このノードにはタイマーのハンドルが必要
- タイマーのハンドルは Set Timer by Event の Return Value から取得できる為これを変数にしておく
- Set Timer by Event の Return Value を変数に昇格させる。名前は TargetLostTimer とする
- TargetLostTimer を Get で配置し図のとおり接続
- ctrl を押しながらドラッグで Get で配置
- コンパイル→保存してエディターを閉じる
最後に
自分の感想などは先頭に🤔を入れることにした。絵文字で目立つしなんて便利なんだ。色々な使い方ができそう。疑問とか喜怒哀楽も表現できる。素晴らしい。
しかし少しおさらいの意味を込めてこのシリーズのブログ読み返したけど、えらく読みづらいな😅
整理整頓しなければ使えない。一旦終わらせてどうするか考えよう
メモ
- このコースではプロジェクトファイルをダウンロードする事も可能。それは参照用として用意したものとのこと
- Target self は、指定がなければアタッチされているキャラクターとなる
英語メモ
- ブロードキャスト メッセージを複数の受信者に同時に転送する事
- ベイク 事前計算
ショートカット
- レベルエディタで地面にスナップ
- レベルエディタで表示→ナビゲーションのショートカットは P でPathの頭文字の模様
- イベントグラフでF7はコンパイル
- イベントグラフで B + クリック でブランチを配置する
- プレイ中に Pause キーを押すと一時停止できる