ブループリントで関数、マクロ、イベント、インタフェース(Unreal Engine 4.25)


前提

身内用に書いたものを、暫定的に公開しているものです。何かおこったら閉じるかもしれません。

前の記事に書いた知識(ブループリントの最低限の知識)は持っている、という前提で書いてます。特に説明のない限り、Unreal Engine 4.25 で FirstPerson テンプレートで作成したプロジェクトのレベルブループリントのイベントグラフ上で操作をする前提です。

参考までに javascript や C# で同じ処理を書いたらどうなるかを併記しておきます(厳密にいうと同じではないこともあります。あくまでイメージです…)。

準備

外部オブジェクト(ブループリント)の参照の例の説明のために、FirstPerson プロジェクトにブループリントを追加しておきます。

キューブのアクターをひとつ画面内に配置して、右の詳細から「ブループリント/スクリプトを追加」を押して、ブループリントを追加してください。

デフォルトで Cube_Blueprint という名前のブループリントになりますが、ここではそのままの名前で作成したものとして説明します。

配置したキューブは、可動性を「ムーバブル」にして、Simulate Physics と Enable Gravity にチェックが入った状態にしておいてください。

関数

ブループリントの「関数」は、他の言語でいうところのインスタンスメソッド(オブジェクト内にスコープが閉じた関数)とだいたい同じです。上の図の Sum というノードが作成した関数のノードです。

関数の作成

関数を作成するには、ブループリントエディタで、左側のマイブループリントにある「関数」の右の「+」を押します。(以下の説明は、レベルブループリントを編集している前提です)

作成すると、関数名が「新規関数_0」みたいになるので、好きな名前に変更しておきます。

関数を作成すると、上のように作成した関数の中身を編集するための、イベントグラフとは別のタブが開きます。このタブの中に、関数の処理の中身を(ブループリントで)書きます。

引数、返り値の追加

引数を追加するには、右側にある「インプット」の項目の横にある+を押します。返り値は「アウトプット」で追加できます。

引数や返り値を追加すると、自動的にノードにピンが追加されたり、返り値を返すノードが追加されたりします。

関数の中身の作成

関数の中身は、イベントグラフを編集するときと同じように、ノードを配置して線でつないでいきます。

上の図では 0 から Number までの数字の和を計算して返す処理を書いてみてます。

関数の中だけで使う変数は、ローカル変数として作成します。上の例では、計算結果を入れる Value 変数をローカル変数にしています。ローカル変数ではなく「変数」のほうに変数を追加すると、それはインスタンス変数になります。(関数内にスコープが閉じません)

引数は値を取り出すことができるだけで、変数として使うことはできません。変数として使いたい場合は、「ローカル変数へ昇格」して変数化してから使います。返り値についても同様です。

関数を書く上での制約

自作の関数の中から、別の関数を呼び出すことはできます。しかし、右肩に時計のマークのついている「潜在的」な関数を、自作の関数の中で使うことはできません。(厳密には不可能ではないですが、追加の設定をするとか、C++で何か書くみたいなことが必要です)

「潜在的な」というのは、処理に待ち時間がある(例えば上のDelayのような)関数のことです。これらの関数を呼び出したいときは、イベントとして処理を記述する必要があります(原則)。

また、関数の中にイベントノードは設置できません。そのため、後述するイベントのバインドや SetTimerByEvent のようなイベントを引数に取る必要のあるノードをつかう場合は、CreateEvent というノードを使う必要があります(イベントディスパッチャーの項目で後述します)。

関数の呼び出し

自作の関数は、同じブループリント内からは(原則)制限なく呼び出せます。

自作の関数を呼び出すときは、左側にある「関数」の中にある項目をエディタ内にドラッグドロップします。画面内を右クリックして関数名(ここえは Sum )と入力することでも、関数のノードを追加できます。

レベルブループリントから他ブループリントの自作の関数を呼び出す

自作の関数は、デフォルトでは同じブルーブループリント(オブジェクト)内からしか呼び出せません。オブジェクトをまたがって関数を呼び出したい場合は、オブジェクトの参照という操作を行う必要があります。

以下、この記事の上のほうの「準備」のところで作成した、Cube_Blueprint とレベルブループリントを使って説明します。

Cube_Blueprint に上のような感じで Move という関数を作ります。

次にメインの(3Dの)画面と、レベルブループリントが両方見える状態にウィンドウを配置します。

そして、画面内に配置した Cube アクター(Cube_Blueprintを作成したベースになるアクター)を画面内で選択します。その状態で、レベルブループリント内で右クリックすると「Cube_Blueprint の参照を追加」というメニューがあるので、それを押します。これで、画面内に配置されているアクターを変数としてレベルブループリント内で使えるようになります。

画面内に追加された Cube_Blueprint ノードからピンを引き出すと、Move 関数のノードを配置できます。

このようにして、外部のブループリントの関数を呼び出せます。ここまでの操作が正しければ、Enter キーを押したときに、Cube が空中に一瞬浮く(そして落ちる)ようになるはずです。

Move 関数が検索で出てこないときは、Cube_Blueprint やレベルブループリントのコンパイルボタンを押してみてください。

Cube が移動しない場合は、Cube の詳細の「可動性」がムーバブルになっているか確認します。Cube が落下しない場合は、Simulate Physics と Enable Gravity にチェックが入っているか確認します。

レベルブループリント以外のブループリント間で自作の関数を呼び出す。

レベルブループリントでは上記の方法で他のブループリントを参照できますが、レベルブループリントではないブループリントから他ブループリントを参照するときは、もう少し複雑な手順が必要になります。

下記の記事は他のブループリントのイベントを呼び出す手順ですが、関数の呼び出しも基本的に同じなので、興味があれば参照してみてください。

マクロ

マクロは、複数のノードをまとめて名前を付けたグループのようなものです。関数に似ていますが、関数と違って実行ピンを含むような処理である必要はありません。また複数の実行ピンを入力にもつこともできます。

マクロの作成も、右側のマイブループリントの中にある「マクロ」の右側にある+を押すことで作成できます。

上記は、二つのベクトル間のマンハッタン距離を計算するマクロの例です。実行ピンがまったくありませんが、マクロであればこのようにして作成することが可能です。

マクロのノードの配置も、関数と同様にして行えます。上の例はマクロ以外のところがかなり複雑になっていますが、プレイヤーがCubeにぶつかったときに、プレイヤーとCubeの間のマンハッタン距離を計算しています。深い意味はありません・・・。

マクロは基本的には作成したブループリント内でしか使えません。プロジェクト全体で使うマクロを作りたい場合は、ライブラリ化する必要があります。詳しくは下記をどうぞ。

イベント

UE4 で言うところのイベントとは、キーが押されたとか、アクターが何かにぶつかったといような、何らかの状態の変化が起こったときに呼び出される関数のようなものです。他言語でいうところのコールバック関数やデリゲートに相当するものです。

イベント(のノード)を追加するときは、他のノードを追加するときと同じように、イベントグラフのエディタ内で右クリックして追加します。しかし、イベントのノードは「イベントの追加」や「インプット」などの項目の中など、いくつかの項目に点在していて探すのが結構面倒です。あらかじめイベント名をWEBとかドキュメントで検索して見つけておき、その名前を使って検索したほうが楽なことが多いです。公式ドキュメントでは下記に多少まとめられています。

例えば、Enter キーを押すか離したときに呼び出されるイベント(の実装)をブループリントの中に追加したい場合は、Enter で検索すると見つけられます。

配置するとこうなります。イベントのノードは、上のようにノードの色が赤と決まっています。

この実装(グラフ)をレベルプリントに作成すると、Enter キーが押されたときに、システムが自動的に Enter の赤いノードの実行を開始します。Enter ノードの先に実行するコード(ノード)があると、そのノードが実行されます。その結果、PrintText のノードが実行されて、画面に Hello と表示されることになります。

イベントは、先に説明した関数とは違って、個別のイベントのタグが開いたりはしません。イベントは一括して「イベントグラフ」の中に配置されます。

UE4のイベントという用語について

UE4のイベント周りの用語は、他の言語と微妙に違っていて混乱しやすいです。上の記事で「イベントの呼び出し口」とかわざわざ書いているのはそのせいです。UE4 でいうところの「イベント」は「(イベントの)デリゲート」または「(イベントの)コールバック関数」と読み替えて、イベントディスパッチャーのほうを「イベント」だと思えばだいたいあってる気がします。詳しくは下記の記事でいろいろ検証されているので、そちらを参照してください。

以下の記事で「イベント」というときは、UE4でいうところの「イベント」(イベントノード)のことを指します。

システムで用意されているイベント

イベントを使うには、あらかじめどんなイベントが用意されているかを知っている必要があります。システムで呼び出されるイベントは、以下にまとめられています。



(上記画像は公式ページからの引用です)

実装するブループリント(のクラス)によって、呼び出されるイベントは異なります。例えば、BeginPlay のイベントは、プログラムの開始時(もしくはオブジェクトが作成された時に)あらゆるブループリントに対して呼び出しがなされますが、OnActorBeginOberlap イベントはアクター(のサブクラス)のブループリントだけ呼び出されます。レベルブループリントに OnActorBeginOberlap イベントを実装しても、システムから呼びだされることがないため、実質的に実行されません。

他のブループリントに紐づいたイベントを実装する

他のブループリントを参照してイベントを実装することもできます。たとえば、レベルブループリントの中で、レベルの中に配置されているアクターのイベントを実装するようなこともできます。

やや手順が複雑になるため、詳しくは以下を参照してください。

イベントに関するいろいろ

イベントの作成、呼び出し、バインド、引数について、ざっくり書いておきます。

新たなイベントを作る

システムで用意されているイベントを使う(イベントを実装する)のではなく、自分で新たなイベントを作成することもできます。イベントを作るには、イベントの呼び出し側(コールバックの呼び出し側)ことを、UE4ではイベントディスパッチャーを使って作成します。

関数や変数などと同じように、マイブループリントの中のイベントディスパッチャーの横の+を押すと作成できます。

作成したイベントディスパッチャーの項目をイベントグラフ内にドラッグドロップすると、メニューが出て作成するノードを選べます。ここで「イベントを呼び出す」を選ぶと、関数を呼び出すのと同じ要領で、作成したイベントの呼び出しができます。

呼び出されたイベント(のコールバック関数)に処理を追加するには、メニューから「イベント」を選びます。

配置されたイベントに PrintText をつないで、イベントノードに Enter イベントをつないでみたところです。これで、Enter が押されると MyEvent イベントが呼び出され、その下に追加した MyEvent_Event イベントの実行ピンの先の PrintText が実行されます。

配置した「イベント」のノードは、デフォルトで _Event という接尾語がついて、イベントディスパッチャーとは違う名前になります(イベント名は変更することはできます)。イベントノードはあくまでコールバック関数なので、イベントディスパッチャーとは違う名前にする必要があるということです。

詳しくは下記のドキュメントを参照してください。

イベントディスパッチャーにイベントを関連づける(バインド)

イベントディスパッチャーを複数のイベントに割り当てて、イベントディスパッチャーに対応するイベントの呼び出しが行われたときに、複数のイベントが呼び出されるようにすることができます。

バインドの処理は、Javascript でいうところの AddEventListener() や、C# でいうところのデリゲートの追加(下記)みたいなものです。上のブループリントを無理やり C# 的に書くと下記のような感じです(たぶん)。

delegate.cs
public event MyEvent MyEvent_Event = delegate() {
    System.Console.WriteLine("Hello");
};
...
public void BeginPlay_Event(){
    this.OnActorHit += this.MyEvent_Event;
}

C# や Javascript と同じで、イベントのバインドは「追加」扱いになります。上の図の例でいえば、OnActorHit (アクターが他のアクターとぶつかったときのイベント)にすでにバインドされている他のイベントはそのままバインドされた状態のままになり、新たに MyEvent_Event を追加でバインドするという感じになります。

バインドするイベントとは「イベント」と書かれた赤い線でつなぎます。バインドされたイベントディスパッチャーに対応するイベント呼び出しがあったときに、バインド先のイベントの呼び出し口(赤い線でつながっている左側にあるイベントの呼び出し口)の先の実行ピンが実行されます。つまり、上の図であれば OnActorHit イベントが(システムで)呼び出されたときに、MuEvent_Event が呼び出されて、その先につながっている PrintText ノードが実行されます。

このとき、赤い線がバインドする側から左側に出ているのがちょっと違和感あるかもしれませんが、MyEvent_Event を関数へのポインタ(関数本体への参照)だと思って、それをイベントディスパッチャー(コールバック関数の呼び出し側)に引数として渡していると考えると良いかもしれません。

他のブループリントのイベントに、自分のブループリントのイベントディスパッチャをバインドすることもできます。また、自分のブループリントのイベントに、他のブループリントのディスパッチャをバインドすることもできます。詳しくは下記をどうぞ。

イベントディスパッチャの引数と返り値

イベントディスパッチャには引数を追加することもできます。しかしイベントディスパッチャに返り値は追加できません。つまり、返り値のあるイベントは作れないということです。

当然ですが、バインドするイベントとバインド元のイベントディスパッチャーの引数(の型と数)は同じである必要があります。

引数付きのイベントのバインドをすると、上のような感じになります。引数が異なるイベントとイベントディスパッチャーの間で赤い線をつなげようとすると、エラーと言われて繋げられません。

イベントディスパッチャーにバインドされたイベントを解除する

バインドの解除もできます。

ただし、自分で作成したイベントのノード(たとえばMyEvent_Eventノード)は複数設置することができません。関数の定義を複数書くことができないのと同じようなものです。

そのため、個別にイベントを解除しようとすると、上のようにバインドしているコードと同じ場所にバインド解除のコードを書くか、CreateEvent ノードを使う必要があります。

バインドされているイベントを全部解除する場合は、上記のようにイベントとの接続は必要ありません。

余談:インタフェース

クラスにインタフェースを追加するには、ブループリントのグラフエディタの上にある「クラス」を押して、詳細の「インタフェース」の項目で追加できます。

これを押してからの、

これです。

こんな感じでインタフェースを追加すると、

上のインタフェースの項目に、実装すべき関数一覧がでます。

あとは、右クリックで「関数を実装」で実装できます。

これ、知らないと永遠にわからんやつですわ。

さらなる余談:Construction Script について

レベルブループリント以外のブループリントのエディタ画面には、上に Construction Script というタブがあります。ここに書いた処理は、オブジェクトをレベルエディタに配置したときに(実質的にはコードをビルドした時点で)実行されます。主に、アクタ内の変数やコンポーネント(メッシュ、マテリアル、テクスチャなど)の初期設定をする用途に使うことが多いです。

初期化は BeginPlay イベントで書くこともできますが、Construction Script はアプリの実行前に実行されることから、アプリの起動時間を短縮することができたり、アプリを実行するより前に(エディタ上で)ビルド結果を確認できるという利点があります。

具体的な例については、下記の記事を参照してください。

続いた

次は基本的なイベントと、タイムライン、タイマーについてです。