LoginSignup
18
24

More than 1 year has passed since last update.

UE4 ゲーム制作メモ1

Last updated at Posted at 2022-03-18

初めに

 この記事はUE4初心者が苦労しながら、チーム制作・個人製作で学んだ事を記述していくものとなっています。なので、間違っている可能性・別の方法が良かったりする可能性も普通にあるので、その点だけ注意して下さい。あと、中には「知ってるわ!」みたいな機能もあると思います。その場合は飛ばして下さい。
 作成で使用していたバージョンは「Ver 4.26.2, Ver 4.27.2」ですが、記事を書く上で使用しているのはUE5です。少し配置が変わってるぐらいで仕様自体はあまり変わらないので許して...。

一覧

 とてつもなく多いので量が多いので、スマホの方は下の一覧でタップして下さい。

目次 見出し
マテリアル テクスチャの透明度を適用したい
-- マテリアルパラメータコレクション
-- マテリアルダイナミックインスタンス
-- マテリアルインスタンス
-- マテリアルファンクション
-- マテリアルのノード
-- 便利なショートカット
コリジョン Block, Overlap, Ignore
-- コリジョンプリセット
-- メッシュの判定
-- トレース系の判定
AI AIの作成
-- ブラックボード
-- ビヘイビアツリー
-- ビヘイビアツリー自体を停止したい
-- ビヘイビアツリーのデバッグ
-- 標準のデコレーター・タスクノード
-- ビヘイビアツリーの動き

マテリアル

テクスチャの透明度を適用したい

 Texture Sampleノードを使って、テクスチャ(.pngなど)の透明度設定を適用したい。RGBAピンをベースカラーに繋いでも設定されない。

  • 「詳細 > マテリアル > Blend ModeTranslucent」に設定
  • Texture Sampleノードの「RGBピン」を「ベースカラー」に繋げ、同じく「Aピン」を「オパシティ」に繋げる

テクスチャ 透明度.png

参考サイト

オパシティとラフネスが同時に設定出来ない

 「詳細 > マテリアル > Blend ModeTranslucent」に設定した上で、``Metallic`‘やラフネスを設定したいが、無効状態になっている。

  • 「詳細 > 透過処理 > Lighting ModeSurface TranslucencyVolume」に設定
  • 「Metallic・スペキュラ・ラフネス」が有効状態になるので、それぞれのピンに繋げる

Surface TranslucencyVolume.png

参考サイト

マテリアルパラメータコレクション

 マテリアルパラメータコレクション(以下MPC)を一言で言うなら「複数のマテリアルに共通の値を外部から動的変更可能な機能」です。プログラマー的に言うなら「構造体のStatic変数」、DirectX的に言うなら「コンスタントバッファ」になるのかな~と思います。
 MPCマテリアル専用の構造体みたいなもので、特定のマテリアルに依存せず外部から自由に変更が可能です。逆に言うなら、値を変更すると使用しているマテリアル全てが変わってしまいます。設定出来るパラメータは「スカラー(float)・ベクター(float4/Linear Color)」のみ可能です。勿論、動的に設定が可能ですが、取得も可能です。
MPC.gif

MPCの追加・編集

  • 「追加 > マテリアル・テクスチャ > マテリアルパラメータコレクション」で追加
    MPC作成.png
  • 「詳細 > マテリアル > Scalar Parameters / Vector Parameters」で値を追加
    • 名前と初期値を設定出来ます
      MPC 詳細.png

※ 今回はテストなので、名前が適当になっていますが気にしないで下さい。

マテリアル側

  • 「右クリック > パラメータ > CollectionParameter」でノードを召喚
    MPC マテリアル 追加.png
  • 「ノードの詳細 > 一般 > Collection」で作成したMPCを設定
  • 「ノードの詳細 > 一般 > Parameter Name」でパラメータ名を設定
    MPC マテリアル 詳細.png

今回のテストではVectorパラメータを「ベースカラー」に、Scalarパラメータを「オパシティ」に設定しました。

ブループリント側

  • 「レンダリング > マテリアル > Set Vector Parameter Value / Set Scalar Parameter Value」ノードを召喚
    MPC ブループリント セット 召喚.png
  • 引数を設定
    • Collection -> 作成したMPC
    • Parameter Name -> 上記で設定したCollectionのパラメータ名(選択すると自動的に候補が出てくる)
    • Parameter Value -> 変更する値(VectorならLinear Color構造体、Scalarならfloat型)
      MPC ブループリント セット.png
取得したい場合

Get Vector Parameter Value / Get Scalar Parameter Value」ノードを使用する
MPC ブループリント ゲット.png


 下図のように、MPCは使用しているマテリアル全てに影響を与えるので、違うアクター(マテリアル)であっても全てのアクタが変更されます。かなり便利だと思いますが、どこからでも変更出来るので怖くもありますね。
 といっても、自分としては「1マテリアル1MPC」と考えているので、マテリアルを跨ぐ使い方はあまりしたくない...というようりも、跨ぐ必要性に駆られていないだけなのですが...。
MPC システム.png
 「マテリアルを使用したアクター毎に変更したい」といった場合は、下記のマテリアルダイナミックインスタンスを使う事になります。むしろこちらの方が多いと思いますが...。

参考サイト

マテリアルダイナミックインスタンス

 マテリアルダイナミックインスタンス1(以下MDI)を一言で言うなら「マテリアルの値を動的に変更可能な機能」です。マテリアルを使うなら「コレ!!」といった技術になります。
 MDIマテリアル専用の変数みたいなもので、マテリアルに依存し各アクター毎に自由に動的変更可能な機能です。設定出来るパラメータは「スカラー(float)・ベクター(float4/Linear Color)・テクスチャ(Texture)」が可能です。勿論、動的に設定が可能ですが、取得も可能です。何気にテクスチャも変更できるのはありがたいですね。

MDI.gif

マテリアル側

  • ノードを追加
    • 「右クリック > パラメータ > VectorParameter / ScalarParameter」でノードを召喚
      MDI マテリアル 追加.png
    • もしくは「右クリック > 定数 > Constant / Constant2~4Vector」で定数を追加、「定数を右クリック > ノードアクション > パラメータに変換」でパラメータに変更
      MDI マテリアル 追加2.png
    • または、圧倒的に便利なショートカット「S(scalar) + 左クリック」と「V(vector) + 左クリック」で一発で召喚可能
  • 「詳細 > 一般 > Parameter Name」で名前を付ける(もしくはノードをクリックしてF2
  • 「詳細 > マテリアルエクスプレッションパラメータ > Default Value」で初期値を設定
  • 「詳細 > マテリアルエクスプレッション > Group」でグループを設定しておくべき
    • 設定しておかないと、後述するマテリアルインスタンスでGlobal Scalar / Vector Parameterになってしまう

ブループリント側

作成

  • 「レンダリング > マテリアル > Create Dynamic Material Instance」でノードを召喚
  • 引数の設定
    • ターゲット -> 作成したマテリアルを使用するプリミティブオブジェクト(Static Mesh Componentなど)
    • Element Index -> 複数個のマテリアルを使う場合は設定する必要がある
    • Source Material -> MDI化したいマテリアル(ターゲット側にMDI化予定のマテリアルを既に設定している場合は指定しなくても可)
    • Optional Name -> デフォルトのまま(使った試しが無い)
      MDI ブループリント 作成.png
  • 戻り値(Material Instance Dynamic型)を変数化2
  • 「レンダリング > マテリアル > Set Material」でノードを召喚(ターゲット側にMDI化予定のマテリアルを既に設定している場合は不要)
    MDI ブループリント 作成2.png

変更

「レンダリング > マテリアル > Set Vector / Scalar / Texture Parameter Value」でノードを召喚

「状況に合わせた表示」をONにしている場合、Material Instance Dynamic型から右クリックしないと、MPC側のノードが表示されるので注意

MDI ブループリント 変更.png

取得したい場合

Get Vector / Scalar / Texture Parameter Value」ノードを使用する
MDI ブループリント 取得1.png
もしくは、「Get Vector / Scalar / Texture Parameter Values」ノードを使用する(簡単に確認はしたが要検証)
MDI ブループリント 取得 2.png

引数の設定
  • ターゲット -> Create Dynamic Material Instanceの戻り値(Material Instance Dynamic型)
  • Parameter Name -> マテリアルで追加したパラメータの名前

Name型(直接指定)なので打ち間違いに注意

  • Value -> 変更したい値(各ノードに応じた型(Scalar = float型 / Vector = Linear Color構造体 / Texture = Textureオブジェクト参照))

 下図のようにMDIはマテリアル自体の値を変更をする為、アクターを個別に変更可能です。イメージとしては「スポーンしたアクター毎にマテリアルを持っている」が合っているでしょうか。
 初めて使う前は結構躊躇していたのですが、いざ使ってみると全然簡単という事に気付きました。そのためか、私自身この機能をよく使っています。
MDI システム.png
 動的に変化する場合はこの機能を使うと非常に便利です。逆に、「別に動的じゃなくて静的でいいんだよ」といった場合は、下記のマテリアルインスタンスを使用します。

参考サイト

マテリアルインスタンス

 マテリアルインスタンス(以下MI)を一言で言うなら「マテリアルの値を動的に変更可能な機能」です。こちらもよく使います。プログラム的には「派生クラス」のイメージが近いです。
 基本的にはマテリアルダイナミックインスタンスの静的バージョンの認識で大丈夫です。

特に、コンポーネントにマテリアルを設定する時は、常にMI化して設定するべきです3

MI サンプル3.png
 MIなのですが、「個別に作成せずある程度自由な形でマテリアルを作成し、MIを使って個別に設定する」といった使い方も出来ると思います。つまり、色違い・テクスチャ違いを実装するのに色違い毎にマテリアルを作成せず、ベースのマテリアルをMI化したものを個別に設定するという事です。この考えは派生と同じですよね。

作成

  • 「コンテンツブラウザ > 右クリック > マテリアルアクション > マテリアルインスタンスを作成」で新規作成
    MI 作成.png

追加

 マテリアルダイナミックインスタンスの「マテリアル側」と同じです。つまり、VectorParameter / ScalarParameterで対応します。

変更

  • 「詳細 > パラメータ グループ >(設定したグループ名)>(設定したパラメータ名)」から、変更したいパラメータのみチェックを付けて値を変更
    • チェックのON/OFFで値を保存しつつ無効化する事も可能
      MI 変更.png
      こんな感じで簡単に気持ち悪い岩マテリアルが出来ました。

 下図のようにMIはマテリアル自体の値を変更出来ますが、あくまで静的な変更です。ですが、同じアクターでもSet Material関数を用いて複数個のMIを用いれば、疑似的に動的な変更が可能ではあります。あくまで「1つのMIでは静的変更になるよ」という事です。
 UE4のマテリアルを使い始めて真っ先に使う機能ではないのでしょうか?やはり、使い方自体はとてもシンプルなのがいいですよね。
MI システム.png

 少し変わったパラメーターとして、「StaticBoolParameterStaticSwitchParameterStaticComponentMaskParameter」といったものがあります。名前の通り、静的に決定し、実行中に変更不可のパラメーターノードです。つまり、「MI上で静的に変更する専用ノード = コンパイル時にシェーダーコードが確定するノード」です。少し面白い?ノードです。
Static パラメーター.png

参考サイト

マテリアルファンクション

 名前の通りのマテリアル専用の関数です。例えば、複数個のマテリアルを作成している場合、同じような処理をまとめたい時に使用します。

作成

 「コンテンツブラウザ > 追加 > マテリアル・テクスチャ > マテリアル関数」で作成します。
MF 作成.png

編集

 デフォルトでは最初から「Input In(Vector3)」と「Output Result」が既にあります。いわゆる、引数と戻り値になります。引数を追加したい場合は「右クリック > 関数 > FunctionInput」、戻り値を追加したい場合は「右クリック > 関数 > FunctionOutput」で可能です。
MF 引数3.png
 引数の設定はノード詳細から

  • 「マテリアルエクスプレッションファンクションインプット > Input Name」-> 名前
  • Input Type」-> 型
  • Description」-> 引数の説明表示(後から見ても分かるように記述しておくべきです)
  • 「マテリアルエクスプレッション > Sort Priority」-> 関数を呼び出す側の順番指定
    MF 引数 設定.png

 同じように、戻り値の設定はノード詳細から

  • 「マテリアルエクスプレッションファンクションアウトプット > Output Name」-> 名前
  • Description」-> 戻り値の説明表示(後から見ても分かるように記述しておくべきです)
  • 「マテリアルエクスプレッション > Sort Priority」-> 関数を呼び出す側の順番指定
    MF 戻り値.png

 次に、マテリアル自体の設定は何も選択していない状態で詳細から

  • 「マテリアルファンクション > Description」-> 関数の説明表示(後から見ても分かるように記述しておくべきです)
  • Exposes to Library」-> 関数呼び出し時の検索欄に表示するか?
  • Library Categories Test」-> 関数呼び出し時の検索欄でのカテゴリー
    MF 設定.png

参考サイト

マテリアルのノード

Customノード

 DirectXでお馴染みのHLSLコードで記述する事が可能なノードです。例えば、見た目を簡略化したり、マテリアルエディタ上では実装不能なループ処理を記述する場合に使います。ポストプロセス系のマテリアルなど(例:自作ブルーム)はよく使うのかな?と思います。
 「右クリック > カスタム > Custom」でCustomノードを召喚可能です。
カスタムノード.png
 特に重要なのが、ネット上に存在するコードをそこそこ簡単に引用出来ます4。例として、「ShadertoyNieR:Automata Glith Effect」などのサイトにあるコードを使わしてもらう場合などが上げられます。
 Shadertoy上のコードを移植するには、そもそもGLSLなのでHLSLに変換する必要があります。その時は「GLSLをHLSLに書き換える - Qiita」が非常に参考になります5。変換する時に「VSCode」を使うと、まとめて置き換え出来たり、型に色が付いたりするので出来るのでオススメです。そして、変換したコードをCustomノードに貼り付けた上でキチンと動けば完了です。

使い方

 詳細な使い方はここには載せないので下記を参考にして下さい。ノード自体の使い方や引数・戻り値の設定方法なども記述されています。個人的に注意すべき点が「引数の名前(スペル)は大文字・小文字まで完全一致させる必要がある」です。でないとエラーが発生します。後、UnrealEngine用の変更があったりするのでその点も注意です。例えば


Float2, 3, 4系について

 Float4での一例なのですが、例えば...

  • 分解したい -> BreakOutFloat4Components
  • 作成したい -> MakeFloat4
  • 変換したい -> ComponentMask
    • ややこしいのが、正式名はComponentMaskなのに表示名がMask(~)になってしまう点なんですよね。といっても、検索欄でmaskと入力しても出てくるので良いのですが...。
    • マスクする値を変更するには、ノードの詳細から変更可能です。
      ComponentMask.png
  • 値を確認したい(デバッグしたい)-> DebugFloat4Values
    •  「マテリアル内でデバッグしたい!」という時に使えるのが「DebugScalar/Float2/Float3/Float4Value」ノードです。何気に便利なのですが、あまり有名でない気がします。
      DebugFloat4Values.png

Float4系ノード.png

参考サイト

その他の使えるノード

  • OneMinus
    • 1から引いた数値になり、Subtract(1, X)と同じです。
      OneMinus.png
  • TexCoord
    • テクスチャのUV座標を取得します。テクスチャのスケールを変更する時などに使います。
      TexCoord.png
  • Time
    • マテリアル内での時間経過を取得します。Pannerなどに使用したり色々使えます。
      Time.gif
  • Rotator
    • UV座標を回転する計算を行います。
      Rotator.gif

 まだまだ沢山紹介出来るノードはありますが、もっと知りたい方は下記サイトを覗いてみて下さい。


便利なショートカット

 全て「キー + 左クリック」の形になります。その為、左クリックは省略します。

ショートカットキー 内容
A 足し算
D 割り算
M 掛け算
1 ~ 4 float~float4の定数(Constant, Constant2/3/4Vector)
T テクスチャサンプル
O OneMinus(1-x)
S スカラーパラメータ
U テクスチャUV座標
V ベクターパラメータ

残念ながら、引き算はハブられてしまいました。引き算よりもOneMinusの方が使う機会が多いからですね?

目次へ

コリジョン(当たり判定)

 若干UE4の当たり判定がめんどくさく感じるのは気のせいでしょうか?かなり簡単に実装出来るのですが...何故か使いづらさを感じるこの頃...。

Block, Overlap, Ignore

 いわゆる、「衝突・検知・無視」になります。

  • Block
    • ゲームに登場する物理的に衝突がある物体がBlock(衝突)になります。壁だったり、ボールだったりと実際に触れる物が当てはまります。現実世界での当たり判定ですね。
      COL_collideEvent.png
      Block.gif
  • Overlap
    • これは判定用(センサー的なもの)として使うのが大半だと思います。その為、オブジェクト自身は貫通し、「当たり始め / 終わり」の二通りの関数が用意されています。現実世界で言うなら、自動改札機や自動ドアのセンサー部分に当たります。
      COL_overlapEvent.png
      Overlap.gif
  • Ignore
    • 文字通りの「無視」になり、物理世界には関与しないという事です。例えば、装飾用のオブジェクトやエフェクトなどが当てはまります。
      COL_ignore.png
      Ignore.gif

※ 画像はコリジョンの概要から参照してきました。

プログラム上でのお話

 とりあえず変更するには「詳細 > コリジョン > コリジョンプリセット > NoColision / BlockAll / OverlapAll」で変更可能です。コリジョンプリセットの詳細に関しては後述します。
コリジョンプリセット 簡易.png
 当たり判定をブループリント側で取得するには、「詳細 > イベント > On Component Hit / Begin Overlap / End Overlap」の「+」をクリックすると関数が出現した上で、関数へとフォーカスします。既に存在している場合は「+」が「ビュー」へと変化します。
On Component Hit-Begin Overlap-End Overlap 追加-ビュー.png

  • 関数一覧
    On Component Hit-Begin Overlap-End Overlap.png

注意点

 「Overlap」についてなのですが、検知したいオブジェクト両方とも「Generate Overlap Events」にチェックを入れる必要があります
コリジョン.png
 更に、物理シミュレーション中に「Hit」判定を取りたい場合は、「Simulation Generates Hit Events」にチェックを入れる必要があります。物理シミュレーションは「詳細 > 物理 > Simulate Physics」をONにしている場合です。
物理.png

参考サイト

コリジョンプリセット

  • Collision Enabled
    • No Collision -> 無効化
    • Query Only -> 当たり判定専用(Overlap専用)
    • Physics Only -> 物理挙動専用(検出不要なキャラクターなど)
    • Collision Enabled -> 通常(当たり判定・物理挙動)
      Collision Enabled.png
  • Object Type
    • デフォルトでは6種類あると思います。これはオブジェクトのタイプに合わせて設定を変更するべきです。ただ、変更は任意になります。
      Object Type.png
  • トレース反応
    • 後述する、Line Trace by Channelなどの関数に使います。いわゆる、レイキャストなどのトレース処理で判別するために使用します。簡単に言えばタグになるでしょうか。
      コリジョンプリセット トレース応答.png
  • オブジェクト反応
    • 通常の当たり判定で使用する判別用タグみたいなものです。主にコレを使い分けて当たり判定を管理などします。
      コリジョンプリセット オブジェクト反応.png

 勿論、コレらの設定はBP側で変更可能です。一例ですが、一時的に当たり判定を無くしたいといった場合は、下図のようになります。
当たり判定 OFF.png

コリジョンプリセットの追加

 実はプリセットは独自に追加する事が可能で、「プロジェクト設定 > エンジン > コリジョン」から追加・編集が可能になっています。詳細は下記サイトをご覧ください。
プリセット新規追加.png

参照サイト

メッシュの判定

 例えば、独自にスタティックメッシュのアセットを読み込んだとして、それらの当たり判定をスタティックメッシュ単体に付与する事が可能です。読み込んだスタティックメッシュのアセットを開き、「コリジョンのタブ >「~を追加」」で簡易的なコリジョンを追加する事が可能です。
 簡易的なコリジョンを調整する機能として、「コリジョンのタブ > オートコンベックスコリジョン(凸型分解)」を使い、当たり判定の詳細を弄ることが可能です。
メッシュ コリジョン オードコンベックスコリジョン(凸型分解).png
 ですが、例えば円柱内に空洞がある場合など、うまくいかない場合があります。その場合は、「詳細 > コリジョン > Collision ComplexityUse Complex Collision As Simple」を選択する事でメッシュに完全一致する当たり判定を設定出来ます。表示されない時は、ウィンドウ左上にある「表示 > 複雑なコリジョン」を選択する事で表示されます。
Use Complex Collision As Simple.png
 正確な分、重たくなりますし、物理シミュレーションに対応していないというデメリットも存在するので地形などのオブジェクトにしておくのが無難だと思います。適用した結果が下図になります。
円柱 当たり判定 比較.png

参考サイト

トレース系の判定

 少し注意点があるライントレース(レイキャスト)系のお話です。下図の図形は青が「Overlap」黄色が「Block」になっています。

Line Trace By Channel

 限定的なレイトレースを実行するノードです。

特徴
トレース応答を判断基準にしている為、「Overlap」は反応せずに「Block」のみ反応します

Line Trace By Channel.gif

Multi Line Trace By Channel

 Line Trace By Channelの複数感知版ですが、そこそこややこしいのが、このノードです。

特徴

  • 「Blockオブジェクト」までに存在する複数個の「Overlapオブジェクト」を感知します
  • 「Blockオブジェクト」が感知出来ない場合は「Overlapオブジェクト」を無視します

Multi Line Trace By Channel.gif

参考サイト

Line Trace For Objects

 通常イメージするレイトレースはこのノードになると思います。

特徴
上記ノード(~ Channel)と違い、判断する基準が「Object Type」になっています。なので、その点を気にしないと反応しない事になりかねません。ただし、引数が配列なので複数タイプを判断可能です。

Line Trace For Objects.gif

Multi Line Trace For Objects

 このノードも同じく「Object Type」を判断基準にしている場合のレイトレースになっています。Multi Line Trace By Channelよりもシンプルに反応してくれます。
Multi Line Trace For Objects.gif

参考サイト

目次へ

AI

 敵などに使われるAIです。基本的にはビヘイビアツリーで実装する事になります。詳細は下記サイトを覗いて見て下さい。というより、公式・先人の方々が十分説明されているので、詳細についてはあまり触れません。

AIの作成

 まず、何も考えずAI三点セット(ブラックボード・ビヘイビアツリー・AIコントローラー)を作成します。
AI 作成 一覧.png
 次に、AIを使用したいキャラクターを開き「詳細 > ポーン > AI Controller Class」に作成したAIコントローラーを設定します。

「詳細 > ポーン > Auto Possess AIPlaced in World or Spawned」に設定してください。でないと「AIが動かねぇ~」という事態になりかねません。

AI コントローラー 設定.png
 次に、ビヘイビアツリーの「詳細 > BEHAVIORTREEBlackboard Asset」にブラックボードをセットして、AIコントローラー側で「Run Behavior Tree」ノードを呼び出して完了です。詳しい実行方法は下記サイトをご覧ください。

ブラックボード

 初見だと、「ナニコレ?」となること間違いなしの機能ですが、自分なりの解釈では「インターフェース機能でアクセスするAI専用の構造体」です。例えば、キャラクター情報やビヘイビアツリー全体でアクセスする要素だったり、デコレーターノードで使用したりなど、色々な用途で使用すると思います。詳しい説明は下記サイトを覗いて見て下さい。ブラックボードの立ち位置は下記サイトの画像が参考になります。

 ブラックボードにアクセスするには「Set Blackboard Value as ~」で設定し、「Get Blackboard Value as ~」で取得します。「Key」に関しては「変数へ昇格」で変数化(Blackboard Key Selector構造体)した上で、「詳細 > 変数 > インスタンス編集可能」をONにしておいて下さい。
AI ブラックボード 設定・取得.png
 すると、ビヘイビアツリー側で変数の設定が可能になり、候補にBBのキーが表示されます。また、違う方法も存在するのですが、その方法は「Name型」で直接指定となっているので、関連付けが可能なこちらのやり方をオススメします。
AI BB 変数 関連付け

ビヘイビアツリー

 UnrealEngine君の一押しのAIシステムですが、いくつかの点に気を付ければ普通に使いやすいシステムだと思います。ともかく、資料も充実していますし、公式サイトでの日本語の説明も分かりやすくまとめてあるので、初めて使う方も安心して使う事が可能なはずです。
AI ビヘイビアツリー 全体図(仮)
※ 上図はかなり適当なのでサンプルのようなものです

サービスノード

  • 説明
    • 「ブラックボードの更新用ノード」に当たります
  • 使い方
    • 「イベント Recive Tick AI」をオーバライドして更新処理
    • 「イベント Receive Activation AI」をオーバライドして初期化処理
    • 「イベント Receive Deactivation AI」をオーバライドして終了処理
      AI BTS 関数一覧
  • 注意点
    • 順番によっては呼ばれるのが最後になる場合がある(特に関数)
    • 「詳細 > Call Tick on Search Start」をONにするだけで順番が変わる
    • 「詳細 > Interval」で呼ばれるタイミングを変更できる
    • 「詳細 > Random Deviation」で呼ばれるタイミングにランダム性が付与される
      AI BTS 詳細

デコレーターノード

  • 説明
    • 「ビヘイビアツリーの条件式ノード」に当たります
  • 使い方
    • 「Perform Condition Check AI」をオーバライドして戻り値で可否判断処理を行う
    • 「イベント Receive Tick AI」をオーバライドして更新処理
    • 「イベント Execution Start AI」をオーバライドして初期化処理
    • 「イベント Execution Finish AI」をオーバライドして終了処理
      AI BTD 関数一覧

「詳細 > フローコントロール > オブザーバーを中止」を変更すると、挙動が変わったりするので注意

  • 基本的にはフローコントロールはSelfでOK
    • 例えば、「このタスクで終了したい」場合でBothに設定すると、低優先度(数値が大きい物)が飛ばされるようになる
  • 「詳細 > コンディション > Inverse Condition」は条件を反転する事が可能なので、「~ではない」のような場合に便利

タスクノード

  • 説明
    • 「ビヘイビアツリーの行動処理ノード」に当たります
  • 使い方
    • 「イベント Receive Execute AI」をオーバライドして初期化処理
    • 「イベント Receive Tick AI」をオーバライドして更新処理
      AI BTT 関数一覧

Finish Execute」ノードの呼び忘れに注意。でないと、永遠にタスクが終わらない事に...。

ノードの移動

 何気に便利なデコレーターとサービスノードのドラッグアンドドロップ。あまり紹介されていない気がするのは、気のせいでしょうか?
ビヘイビアツリー ノード D D

参考サイト

ビヘイビアツリー自体を停止したい

 Stop Logicノードを使う事で可能。例えば、プレイヤーが死亡した時にAIを動かしたくない時に使えます。
AI 停止

ビヘイビアツリーのデバッグ

全体的な流れ

 通常のブループリントと同じ「F9」でブレイクポイントを置く事が可能です。デバッグ状態になると、簡単なタスクの動きを監視する事が可能で、赤線は前回の流れを表し、白線は現在の流れを表します。ただし、タスク毎にしか止まらず、「デコレーター」や「タスクまでの途中経過(セレクター)」で停止する事は出来ません。その場合は内部のブループリントでブレイクポイントを置く必要があります。
AI ビヘイビアツリー デバッグ
 勿論、ブレイクポイントを設定していない状態で実行すると、リアルタイムでAIの動きが表示されます。
ビヘイビアツリー デバッグ

デコレータノード

 「じゃあ、全くデコレーターのデバッグが出来ないじゃん。使えねぇ~」と思うかもしれませんが、ちゃんとデコレーターの挙動は表示されます。具体的には、失敗したデコレータの右側に赤い縦線で表示します。なので、連続でデコレータが存在する場合、かなり分かりやすいと思います。
AI デバッグ デコレーター
 更に言うと、後述するブラックボードデコレーターに関しては具体的な値と結果を表示してくれます。
AI デバッグ ブラックボードデコレーターノード

キー

 更に、ブレイクポイントに止まった状態で「ブラックボード > キー」には、その時点での値が「現行値」といった形で表示されるようになります。何気に便利な機能です。
AI デバッグ キー

標準のデコレーター・タスクノード

 元々搭載されていて使えそうなノードを抜粋して記述します。

基本的に自作のブループリントノードはC++と比べ遅くなります。パフォーマンスを気にするならばC++で作成するのも考えておいた方がいいかもしれません。

タスクノード

  • Move Directly Toward
  • Move To
  • Play Animation
    • アニメーション再生を行うノード
    • 何と、ビヘイビアツリー側でアニメーション再生が可能になっています
    • 非常に簡素な敵のアニメーション管理なら使えそうですが、「アニメーションブループリントとビヘイビアツリーの両方で管理をする」といった事は絶対止めた方がいいです
  • Play Sound
    • サウンド再生をするノード
    • ビヘイビアツリー側で再生可能なのですが、まだPlay Animationよりかは使う機会が多いのかなと思います
  • Rotate to face BB entry
  • Run Behavior
  • Wait Blackboard Time
    • 待機をするノード(キーで時間指定)
    • もっとシンプルなWaitノードもあるのですが、時間調整が動的に出来ないのでこちらのノードをオススメします
    • 攻撃後や行動後のクールタイムなどに使用します(デコレーター側にクールタイムノードがあるのですが残念ながら静的指定なので微妙...)

AI ビヘイビアツリー タスクノード


 簡単に紹介しましたが、非常に簡素なキャラに限ってはビヘイビアツリーだけで、アニメーション・サウンド・行動がコントロール出来るので時短になります。ただし、少しでも複雑になってくる場合は流石に、行動・サウンドぐらいにしておいた方がいいとは思います。

デコレーター

  • Blackboard Based Condition
    • ブラックボードの値だけで評価をするノード
    • intやfloatなどは大なり小なりなどの比較も可能です
  • コンポジット
    • Blackboard Based Condition以上、ブループリント未満のロジックを組む事が出来るノード
    • 「ブラックボードの単純比較だけでは物足りないが、ブループリントを作成するまでもない」といった場合に使います
    • And, Or, Notの論理演算を使用する事が可能です
  • Conditional Loop
    • 条件が一致している限り常に処理が続くノード
    • ブラックボードの値を使えるので、特定の条件で常に処理しておきたいといった場合は非常に便利だと思います。
    • ルートから再評価という形でも同じにはなると思いますが、このノードを使うと実行範囲が限られるので、バグを減らせるのかもしれません
  • Cone Check
    • コーンの範囲内に目標が存在するかを判定するノード
    • コーンの半径は静的決定ですが、原点・方向・目標(座標指定)はブラックボードの値を使用できます。
    • パッと思いつくのは、ステルスゲームなどの視野内判定に使えると思います
  • Does Path Exist
    • 2点間の座標でナビメッシュを使った経路探索を行うノード
    • 経路探索に複数の種類があり、それぞれ速度と精度が異なります
    • 勿論、ブラックボードの値を使えるので動的決定です
  • Is At Location
    • 指定の位置に存在するかを確認するノード
    • ある程度の誤差を許容する範囲は静的決定ですが、指定の位置に関してはブラックボードの値を使います
    • ナビメッシュを使って、経路と一致しているかを確かめる事も出来ます

AI ビヘイビアツリー デコレーターノード


 デコレーターノード側も標準でかなり便利なノードが存在します。なのですが、少し不満点を挙げるならもう少し動的に決定出来るノードを増やしてほしいです。特に、Cooldownノードは本当に思います。動的なら間違いなく便利なので紹介していました6

 デコレーターノードの自作ブループリントだけ、速度低下云々については無いみたいです。なので、標準のノードに不便に感じたら、気にせず自作ブループリントノードを作成してしまって構わないと思います。

ビヘイビアツリーの動き

 AI最後として、割とややこしいビヘイビアツリーの挙動のお話です。というのも、普通に使うだけでも色々ゴチャゴチャな動き方をします。
 例えば、デコレーターノードの「詳細 > フローコントロール > オブザーバーを中止」をNone以外にしたり、サービスノードの「詳細 > サービス > Call Tick on Search Start」をONにすると関数の呼ばれるタイミングが変わってきます。ホントにヤヤコシイデス。
 大体の流れを理解しておけば、各関数の意味が分かってくると思います。

前提条件
  • 全てのデコレーターノード
    • 成功判定
    • 「オブザーバーを中止」はNone
  • 全てのサービスノード
    • Call Tick on Search Start」はOFF
    • Interval」は0.001
    • Random Deviation」を0
      • 「tick every 0.00s」と表示するように
      • いわゆる、「イベント Receive Tick AI」を毎フレーム呼び出し
  • 全てのタスクノード
    • 「イベント Receive Tick AI」で「Delay」ノード(Duration: 0.2)を呼び出し、完了後「Finish Execute」しています
  • 全体図は下の画像になります
    AI ビヘイビアツリー 全体図 呼び出し確認

実行前

 まず、AIコントローラー側で「Run Behavior Tree」ノードが実行されます。恐らく、ノード内の処理でルート直下のサービスノードが呼び出されます。ノード内では「下位ノードの情報取得開始」と「実行開始」が呼び出されます。
まとめ1

  1. Run Behavior Treeノード 実行
    1. ルート直下のサービス2 動作
      1. イベント Receive Search Start(下位ノードの情報取得開始?)
      2. イベント Receive Activation(実行開始)

タスク1 実行

 「Run Behavior Tree」が呼ばれた後、ルート直下のサービスで「更新」と「下位ノードの情報取得開始」が呼び出されます。次に、左下にあるデコレーター(下位ノード)が呼ばれ、「状態判定」が行われて成功した時だけ、同ノードの「実行開始」とタスク・サービスノードの処理が実行されます。
まとめ2.png
 下位のデコレーターから上位のサービスへ移行して、サービスの「更新」を呼び出します。更に、下位のサービスへ移行して「実行開始」を呼び出します。ようやく、下位のタスクの「実行開始」を呼び出してタスクの更新処理を始めます。
まとめ3
 タスクの更新ですが、最初にタスクの「更新」が呼び出され、サービスの「更新」が呼ばれるのですが、上から順に呼ばれます。つまり今回は「サービス2 → サービス1」の順番になります。タスクで「Finish Execute」が呼ばれるまで、「タスク1 → サービス2 → サービス1」が繰り返されます。
まとめ4.png
 タスクの終了の合図として「Finish Execute」関数を呼び出し、デコレーターの「実行終了」が呼び出されます。そして、次点のデコレーターへ移行した上で「状態判定」が行われ、成功した場合のみ「実行開始」が呼び出されます。もし、した場合、実行していたタスクにくっついていたサービスの「実行終了」が呼び出されます。
まとめ5.png

文章まとめ
  1. ルート直下のサービスノード2 動作
    1. イベント Receive Tick(更新処理)
    2. イベント Receive Search Start(下位ノードの情報取得開始?)
  2. 下位ノードのデコレータ1 動作7
    1. Perform Condition Check AI(状態判定 ※今回は全て成功)
    2. イベント Receive Execution Start(実行開始)
  3. 上位のサービス2へ移行
    1. イベント Receive Tick(更新処理)
  4. 下位のサービス1へ移行・動作
    1. イベント Receive Activation(実行開始)
  5. タスク1 動作
    1. イベント Receive Execute(実行開始)
    2. イベント Receive Tick(更新処理)
  6. 最上位のサービス2へ移行
    1. イベント Receive Tick(更新処理)
  7. 次の上位のサービス1へ移行
    1. イベント Receive Tick(更新処理)
  8. タスクの更新で「Finish Execute」が呼び出されるまで無限ループ
    1. タスク1を更新
    2. 最上位のサービス2を更新
    3. 次点のサービス1を更新
    4. 下位のノードが見つからないので終了 = 最下位
    5. タスクの更新に戻る(1へ戻る)
  9. タスク1で「Finish Execute」関数を呼び出す(タスク1 更新終了)
  10. デコレーター1へ移行へ移行
    1. イベント Receive Execution Finish(実行終了)
  11. 次点ノードのデコレータ5 動作
    1. Perform Condition Check AI(状態判定)
  12. 失敗した場合
    1. 実行中のサービス1へ移行移行
      1. イベント Receive Deactivation(サービス1 実行停止)
    2. 一番上へ戻る(サービス2 動作)
  13. 成功した場合
    1. イベント Receive Execution Start(実行開始)

タスク3 実行

 前項でデコレータ5が状態判定で成功した場合のお話です。最初に、サービス5へ移行した後に「下位ノードの情報取得開始」が呼び出され、デコレータ3へ移行します。そして、デコレータの「状態判定」で「成功」した後に「実行開始」が呼び出されます。
まとめ6.png
 関連が停止したサービス1へ移行した後、実際に「実行停止」が呼び出されます。停止されたので、次点のサービス5へ移行したうえで、「実行開始」と「更新」が呼び出されます。更に、下位のサービス3へ移行して、「実行開始」が呼び出されます。
まとめ7.png
 サービス関係の準備が整ったので、実際にタスク3の「実行開始」と「更新」が呼び出されます。タスク1の時と同じように、上から呼ばれるので「サービス2 → サービス5 → サービス3」の「更新」が呼び出されます。そして、タスク3の「更新」へ戻ります。
まとめ8.png
 終了の合図がタスク3で呼び出され、デコレータ3へ移行した上で「実行終了」が呼び出されます。次に、次点のデコレータ4へ移行して「状態判定」が呼び出されて成功した場合は、デコレータの「実行開始」が呼び出されます。
まとめ9.png

文章まとめ
  1. サービス5へ移行
    1. イベント Receive Search Start(下位ノードの情報取得開始?)
  2. デコレータ3へ移行
    1. Perform Condition Check(状態判定 ※今回は全て成功)
    2. イベント Receive Execution Start(実行開始)
  3. サービス1へ移行
    1. イベント Receive Deactivation(実行終了)
  4. 次点のサービス5へ移行
    1. イベント Receive Activation(実行開始)
    2. イベント Receive Tick(更新処理)
  5. 最下位のサービス3へ移行
    1. イベント Receive Activation(実行開始)
  6. タスク3へ移行
    1. イベント Receive Execute(実行開始)
    2. イベント Receive Tick(更新処理)
  7. サービス2へ移行
    1. イベント Receive Tick(更新処理)
  8. サービス5へ移行
    1. イベント Receive Tick(更新処理)
  9. サービス3へ移行
    1. イベント Receive Tick(更新処理)
  10. タスク3へ移行
    1. Finish Execute 呼び出し(更新終了合図)
  11. デコレータ3へ移行
    1. イベント Receive Execution Finish(実行終了)
  12. デコレータ4へ移行
    1. Perform Condition Check(状態判定 ※今回は全て成功)
    2. イベント Receive Execution Start(実行開始)

タスク4 実行

 大体の流れは「タスク3 実行」と同じで、呼ばれる場所が異なるだけなので、ある程度は飛ばします。タスクの更新終了(Finish Execute関数を呼んでから)から載せます。
 実行完了の手順は、サービスもデコレータも下位から上位の順で呼び出されます。つまり、「デコレータ4 → デコレータ5」・「サービス4 → サービス5」といった感じに「実行終了」が呼び出されます。
まとめ10.png
 終了手順が呼び出されたら全てのタスクを一巡し終えたので、最初からである「サービス2の更新」から始まります。
 ただし、タスク中に中断・終了した場合などは、最上位から最下位のノード順で「実行終了」が呼ばれます。そして、タスクノードの「イベント Receive Abort」が呼ばれるみたいです。名前の通りですね。

「イベント Receive Search Start」ノードについて

 今回は「下位ノードの情報取得開始」だろうと考えて作成しましたが、間違っているならすいません。呼ばれる順番については書かれているサイトはあったのですが、目的そのものについては「コレだ!」といったものは見つかりませんでした。なので、憶測で決めました。ビヘイビアツリー自体がツリー構造のはずなので、「下位ノードの検索を行う」前に呼ばれるものだと思ったからです。


呼び出しの履歴(テキスト)

 上記の動きは下記のテキストを書き起こしたものです8。念の為、テキストの方も残しておきます。コメント(//)に関しては独自に追記したものです9


サービス2 Search Start // サービス2 下位ノードの情報取得開始?
サービス2 Activation // サービス2 実行開始

// AIコントローラー側でのRun Behavior Tree呼び出し済み

サービス2 Tick
サービス2 Search Start // サービス2 下位ノードの情報取得?

デコレーター1 Condition Check // デコレーター1 成功判定
デコレーター1 Execution Start // デコレーター1 実行開始

サービス2 Tick

サービス1 Activation // サービス1 実行開始
タスク1 Execute // タスク1 実行開始

タスク1 Tick
サービス2 Tick
サービス1 Tick

// タスク1 実行中

タスク1 Tick
サービス2 Tick
サービス1 Tick
// タスク1 Finish Execute呼び出し

デコレーター1 Execution Finish // デコレーター1 実行終了

デコレーター5 Condition Check // デコレーター5 成功判定
デコレーター5 Execution Start // デコレーター5 実行開始
デコレーター5 Search Start // サービス5 下位ノードの情報取得?

デコレーター3 Condition Check // デコレーター3 成功判定
デコレーター3 Execution Start // デコレーター3 実行開始

サービス1 Deactivation // サービス1 実行終了

デコレーター5 Activation // サービス5 実行開始
デコレーター5 Tick

デコレーター3 Activation // サービス3 実行開始
タスク3 Execute // タスク3 実行開始

タスク3 Tick
サービス2 Tick
デコレーター5 Tick
デコレーター3 Tick

// タスク3 実行中

タスク3 Tick
サービス2 Tick
デコレーター5 Tick
デコレーター3 Tick
// タスク3 Finish Execute呼び出し

デコレーター3 Execution Finish // デコレーター3 実行終了

デコレーター4 Condition Check // デコレーター4 成功判定
デコレーター4 Execution Start // デコレーター4 実行開始

デコレーター3 Deactivation // サービス3 実行終了

デコレーター4 Activation // サービス4 実行開始
タスク4 Execute // タスク4 実行開始

タスク4 Tick
サービス2 Tick
デコレーター5 Tick
デコレーター4 Tick

// タスク4 実行中

タスク4 Tick
サービス2 Tick
デコレーター5 Tick
デコレーター4 Tick
// タスク4 Finish Execute呼び出し

デコレーター4 Execution Finish // デコレーター4 実行終了
デコレーター5 Execution Finish // デコレーター5 実行終了
デコレーター4 Deactivation // サービス4実行終了
デコレーター5 Deactivation // サービス5 実行終了

タスク1 Abort // タスクを中断した際に呼び出される(ゲーム終了など) 今回の場合はESCキーで終了

// 全タスク完了


目次へ

次回へ

 予想以上に書く量が多くなってしまい、投稿が遅くなってしまいました。申し訳ありません。実はまだまだ書くことがあるので、次回へ続きます。次回はアニメーションやアセットなどを色々書きます。流石にそこそこの量で分割するので、全3~4回で終わると思います。今回は多すぎたなぁ。今度こそ3月末に上げるぞ~!


 2週間で終わらなくて悲しい事態になってしまいました。自分の悪い癖なのですが、「1記事で書き終わりたい欲」が...ダメですね...最初から下書きを分割しとくべきでした。言い訳するなら、全て動くようにUE5上で確認したり、追加で書きたい事が増えたりするんですよね...。

  1. 公式的には「マテリアルダイナミックインスタンス」なんですが、普段使いでは「ダイナミックマテリアルインスタンス」なんですよね。ですが、今回は公式の言い方で統一します。

  2. MDIの値を変更する時は、この戻り値を使用するためです。変更するタイミングが初期化以外なら、変数化しないと変更できません。※ 今回はテストなので戻り値のピンの値をそのまま使っています(本来はローカル変数にしておくべきかと...)

  3. MI化する利点は「コンパイル時間の短縮・作業の簡易化・シェーダーサイズ軽量化」などが挙げられます。

  4. 個人的にゲームを作成して、SNSに動画を公開する程度なら問題ないと思いますが、少なくとも使用させてもらったURLなどはコメントなどとして記述しておくべきです。

  5. ごく稀に、マクロでHLSL/GLSLを切り替えて使えるコードが存在するのですが、基本的にはこちら側で変換する必要があります。

  6. ふと思ったんですが、静的なノードがどれも時間に関係するノードですね。やはり、時間関係となるとややこしくなるのでしょうか?となると、Wait Blackboard Timeが特殊なだけ?

  7. UnrealEngineのビヘイビアツリーでは、ルート分岐した時は左から実行されます。

  8. 今回の記事で一番メンドクサカッタ作業だったりします。

  9. 方法は完全にゴリ押しで、全てのノードの全ての関数にPrintStringでそれぞれ記入します。そして、アウトプットログの出力された文字をWordコピペして、余計なものは消して、ジャンル分け(着色)して...という悲しい方法ですね。更に、それをQiitaの記述方法に変換するという...。

18
24
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
24