【UE4】Unreal EngineでBPをシンプルに書きたい


# はじめに

:::note info
この記事は、Unreal Engineを使ったことがあり、ブループリント(以後BPと省略します)を使った処理をある程度書いたことがある方を想定しています。
:::

Unreal EngineのBluePrintについて、少しわかってきて、かける量が増えてくると、処理が重複する部分などをよりシンプルにみやすく書けるかが気になってきます。そこで、どのように書くことでより簡潔に書けるかについて、メモとしてまとめていこうと思います。


この記事で書かない部分ですが、BPのグラフネットワークをどのように整えていくかの方針などは、historia様の以下の記事が非常に参考になります。

https://www.slideshare.net/historia_Inc/ss-64773800/14

### 目次
この記事で書くことについて、はじめに簡単にまとめておきます。途中から書きたいこと、まとめておきたいことが増えたらその都度追加していこうと思います。

1. BPマクロライブラリー
2. BP関数ライブラリー 
3. イベントノード


の順に書いていこうと思います。

## BPマクロライブラリー

https://docs.unrealengine.com/4.27/ja/ProgrammingAndScripting/Blueprints/UserGuide/Types/MacroLibrary/

BPマクロライブラリーは、通常BPなどで使えるマクロ機能と同じで、複数のノードから構成される処理を一つにまとめてマクロとして呼び出せる機能です。マクロを使うことで、BPの見やすさが向上します。ここでは、単純に見やすさのメリットだけではなくて、実際の運用でのメリットを紹介します。

BPマクロライブラリーについては、以下の二つの記事がとっても参考になりました。
これだけ読んでいただけると、マクロライブラリーのありがたみがわかると思います。今回は、マクロライブラリーを実際にどのように使っているかについて紹介いたします。

https://soramame-games.com/macro-library-usage

https://techblog.sega.jp/entry/2018/11/26/100000#%E3%83%9E%E3%82%AF%E3%83%AD%E3%81%AF%E6%82%AA%E3%81%84%E3%81%88%E5%96%84%E3%81%A7%E3%81%99Blueprint%E3%81%AB%E9%99%90%E3%82%8B

まず、マクロライブラリーは、エディターのBlueprintsの項目から作れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/4b0fe439-e484-89bc-bcd1-e52ddb543a6e.png)

ここでは、私がマルチデバイス対応する際に多用するノードを例にとって説明していきます。
get platform nameノードとswitch on stringノードを用いることで、ユーザーさんが使っているデバイスを取得できます。私の環境だと、モバイルアプリ向けにアプリをリリースすることが多いので、各プラットフォームで異なる実装をする際に、switch on stringを行うのですが、これを異なるBPから毎度呼び出すのは、Windows iOS Androidの文字列を間違わないように毎回記述したり、コピーペイストしなくてはいけないので、面倒です。そこで、BPマクロライブラリーを作っておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/bac7814d-cfb6-601e-4259-801145d2b0eb.png)
このように、マクロを作っておくと、他のBPから必要なタイミングでどのプロットフォームの処理を実行すればいいのかを一発で指定できます。

例えば、今作ったマクロとBP_CallerというBPから呼び出すことを考えてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/88666e7b-6a56-97a1-727b-2969f5cef269.png)

先ほど製作したマクロの名前を検索パネルで検索することで、該当のマクロを呼び出せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/d2908273-5276-5210-3f04-10d4cee23d77.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/9cca1fe0-c553-98b4-beab-45103be74c86.png)

このようにマクロライブラリーと、この後紹介する関数ライブラリーを利用することで、よく使う処理、またはよく使う文字列をライブラリーとしてまとめておくことで、毎度記述しなくても良くなり効率が上がります。さらに、compact title の設定を使うことで、よく使う定数については、ライブラリーに入れておくことですぐに呼び出すことができます。

同様に、よく出てくるのが、エラーのハンドリング処理です。処理が成功した場合と失敗した場合(エラーメッセージがある場合)それらを同時に処理したいので、以下のようなマクロを組んで使うことがよくあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/87e87ad6-5f82-940d-bcb6-515045a724cc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/2eda9e23-c5b9-bfa2-b538-466806b239e9.png)


ここまで、説明してきたマクロライブラリーは、次に説明する関数ライブラリーと異なり複数の実行pinを作ることができるので、例えばswitch 処理やエラーハンドリングなどの分岐処理があるような処理で使いやすいものでした。しかし、マクロライブラリーは、通常のBPと同じようにクラスを指定して、作る必要があり、作成する際にどのクラスを親クラスにするかによって、マクロライブラリーで呼び出せるものが異なること、マクロライブラリー自体の呼び出しも、呼び出し元のクラスがマクロライブラリーのクラスと同じものを継承したクラスだけに限られてしまう。そのため、ウィジェットでプラットフォームに応じて処理を行うためには、再度ウィジェット用のBPマクロライブラリーを同じように作る必要がある。

詳しくは、先ほどの記事を参考にしていただきたいです。

https://soramame-games.com/macro-library-usage

マクロライブラリーは、参照がさまざまな場所で行われていると、利用したいタイミングで不必要なアセットまでロードされてしまい、メモリーを無駄に使ってしまったり、処理負荷を無駄に上げてしまう原因になります。なるべく細かく依存関係を整理したり、C++の関数を作成することで、複数の実行ピンをもっと関数をどこでも利用できるようになるので、処理負荷が気になる場合は、他の方法を取ると良いようです。

https://historia.co.jp/archives/6411/

## BP関数ライブラリー

https://docs.unrealengine.com/4.27/ja/ProgrammingAndScripting/ProgrammingWithCPP/BlueprintFunctionLibraries/

関数ライブラリーも複数のノードをまとめて、一つのノードに置き換えることができるので、BPを整理するときに役立ちます。
また、マクロと異なり、一度コンパイルすれば後は何度使おうと、呼び出しだけになるので何度も利用する場合は、効率的になります。アクターではないので、どのBPからも呼び出すことができるので、基本的には、関数ライブラリーで作って、関数ライブラリーで作ることができないが、多用されるノードに関してはマクロライブラリーで作るという戦略が良いと思われます。

マクロライブラリーと関数ライブラリーの違いや長所と短所については、以下のリンクなどが参考になると思います。

https://answers.unrealengine.com/questions/30834/whats-the-difference-between-blueprint-macros-and.html

https://historia.co.jp/archives/6411/

関数ライブラリーは、マクロライブラリーと同様に、Blueprintsの項目から作ることができます。ここでは、MyGameInstanceというゲームインスタンを作って、このゲームインスタンを呼び出すための、関数を関数ライブラリーに作っていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/ba71bc70-dc77-a4d8-2568-ca63d72742e4.png)

実装は、以下のようにしてみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/aeda4c4c-15cc-9dd7-9421-a73e58b6a644.png)

この状態では、コンパイルして、先ほどのBP_CallerのBP Editorにて関数名を検索することで、呼び出しができるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/9cca1fe0-c553-98b4-beab-45103be74c86.png)

関数ライブラリーの設定を変えることで、関数の呼び出し方法を変えることができます。その設定が、純粋化です。
例えば、以下のようにpureのフラグにチェックをつける、Compact Node Titleを設定すると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/95777524-2649-0b2e-eb45-6f4b25a67086.png)

以下のように、BP_Caller側の呼び出しがさらにみやすくなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/ea8d24ed-e299-7176-cfb7-c6f45cd5351e.png)

純粋化について、詳しい説明については、以下の記事が参考になりました。概要だけを簡単にまとめると次のようになります。純粋化は単にノードの実行ピンがなくなって使いやすくなるだけではなく、関数の呼び出しのタイミングが非純粋関数と異なります。純粋化した場合、関数の呼び出しは、純粋化した関数を参照する実行ピンが実行される度、毎回呼び出しを行います。一方で、実行ピンを持つ関数の場合、一度実行された関数の戻り値は、一時的に保存され何度、参照しても同じ値になります。

https://historia.co.jp/archives/3929/

Game Instance以外にもよく利用するcast系では、Player contorllerについてもGet ControllerをしてからCastするという二度手間の作業を関数ライブラリーに登録することで簡潔にいつでも簡単に呼び出しができるようになります
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/fb002682-0091-86d5-0209-e5818a9d7a1a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/5afcb4b9-1c33-cf30-4059-2f9914b465c5.png)

ちなみに、マクロライブラリーと関数ライブラリーの呼び出し時間についての比較記事もあったので、こちらに添付させていただきます。

https://hat0xaa.hatenablog.com/entry/2019/04/18/141600

## イベントノード

VaRest Pluginなどのようにネットワークを返して、リクエスト結果をある程度のネットワークの遅延後に取得するような処理の場合、コールバックとして値が帰ってきたらカスタムイベントなど発火させるような形で、値を受け取ることが多いです。以下の資料が参考になります。

https://qiita.com/go_astrayer/items/28f3726e03d2d22087e2

このような処理を行う場合、どうしてもカスタム関数をコールバックイベントを受け取りために、呼び出し元の関数ノードの近くに、結果を受け取ってから処理を行わなくていけないため、見栄えや簡潔さがかけてしまうことがある。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/f28b11dc-3f47-fdb3-b5a7-e457b09dcf69.png)

そこで、create eventノードを使うことで、この問題を解決できます。このcreate eventノードを用い入れば、カスタム関数自体を発火させる対象を一箇所に限定せず、例えば、AndroidとiOSそれぞれと直前の処理の可否に応じて、発火させたいカスタム関数が同じである場合にも便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/1157d405-5153-fe08-99bb-219b02823afa.png)

create eventノードを使うことによって、レスポンスの処理に関しては、呼び出し元のノードを近くに置く必要がなく、他のノードから、同じようにcreate eventノードで呼び出すことができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206272/4903b0ba-b355-1a56-0008-0f6cb98b1faf.png)


# 終わりに
BPを綺麗に簡潔に書く方法は、ここで述べた方法以外にもたくさんあると思うので、
今後もこちらに追記するような形でまとめていければと思っています。また、綺麗に書けることが必ずしも良いわけではなく、それぞれの処理負荷や参照なども考えて行かなければならないと思っています。修正しやすく、より負荷の少ない実装も心がけつつ、他の人が見てもみやすくわかりやすい簡潔なBPを書けるように努力していこうと思いました。

# 宣伝
最後に会社の紹介をさせてください。Alcheでは、VR/Metaverseと言われる空間でライブなどの体験を作っています。これまでの製作などは、以下のHPを見ていただけたら嬉しいです。

https://alche.studio/

弊社では、UE4のプログラマーの方、ネットワークエンジニア、バックエンドエンジニア、インフラエンジニアの方をとっても募集しております。UE4ベースのモバイルアプリ、VRデバイス向けマルチプレーのプロダクトを作っています! 少しでも興味があれば以下の私のTwitter

https://twitter.com/r_etx

もしくは、弊社のHPのお問い合わせよりご連絡いただけたら幸いです!!