【UE4/UE5】ブループリントの意外な仕様や裏技の紹介

# はじめに
Unreal Engine 4/5 の中核であるブループリントは手軽にプログラミングできる一方で、あまり知られていない隠された仕様や機能が数多く存在します。

この記事では、私が知る中で公式ドキュメントには書かれていない便利な機能や注意するべき仕様、他の方が（ほとんど）紹介していないような内容を中心に役立つものを紹介していきます。

# 動作環境
ブループリントそのものの根本的な動作・仕様はUE4/UE5共に変化はないため、ここで紹介するすべてが活用できるはずです。

:::note info
正しく動作しているかについては、執筆時点での最新版である UE5.1.0 で行っています。
:::

# BP仕様編
## ノードのコピペはテキスト
一番最初はよく知られている仕様から。

ノードをコピーすると、クリップボードにはそのノードを表現するあらゆる情報が**独自の書式で表されたテキスト情報**として格納されます。
例えば、アクターを新規作成直後に半透明で配置されている "イベント BeginPlay" ノードをコピーすると、以下のようなテキストが取得できます。
![01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/f22d75ab-59c6-7ef0-8cd0-a946b293124f.jpeg)

```
Begin Object Class=/Script/BlueprintGraph.K2Node_Event Name="K2Node_Event_0"
   EventReference=(MemberParent=/Script/CoreUObject.Class'"/Script/Engine.Actor"',MemberName="ReceiveBeginPlay")
   bOverrideFunction=True
   EnabledState=Disabled
   bCommentBubblePinned=True
   bCommentBubbleVisible=True
   NodeComment="このノードは無効になっているため呼び出せません。\n機能をビルドするにはピンをドラッグしてください。"
   NodeGuid=7D7C5711499C06CBA54F109C2DD6F1BD
   CustomProperties Pin (PinId=8034855E48A4440D8271D98503AA3325,PinName="OutputDelegate",Direction="EGPD_Output",PinType.PinCategory="delegate",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(MemberParent=/Script/CoreUObject.Class'"/Script/Engine.Actor"',MemberName="ReceiveBeginPlay"),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=42BB072246B2ECEF06ACDAB7A91EBDEF,PinName="then",Direction="EGPD_Output",PinType.PinCategory="exec",PinType.PinSubCategory="",PinType.PinSubCategoryObject=None,PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
```

このようにノードを構成する様々な情報がテキスト形式で取得できました。
これで何ができるかというと、このテキスト情報を書き換えてエディター上では出来ないような修正や置換等のカスタマイズを行うことができます。

例えば以下のような配列を作る"Make Array"ノードがあったとします。
![02.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/904ae296-1ffb-2ab6-be77-b27fe67ddcc2.jpeg)
このノードには要素の入れ替えや特定位置に挿入を行える機能はありません。
そこで、このノードをコピーし、テキストエディタにペーストします。

```
Begin Object Class=/Script/BlueprintGraph.K2Node_MakeArray Name="K2Node_MakeArray_0"
   NumInputs=3
   NodePosX=528
   NodePosY=112
   NodeGuid=A5C2D1E045AD50590CAAA4AA892E5457
   CustomProperties Pin (PinId=2B18D1A142D5693A5613AF8A9AF5F20F,PinName="Array",Direction="EGPD_Output",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=Array,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableSet_0 B33823A04D683F11CADB0EA1B31E0FD0,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=9A8E14F14F7D03247031BB8704388B3A,PinName="[0]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cone.Cone",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=7EDE66B548001DC7A5CA0AB43575A4A2,PinName="[1]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cube.Cube",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=01C715DF46136CEBB2CD1596A211FF18,PinName="[2]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/EngineMeshes/Cylinder.Cylinder",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
```

これが"Make Array"ノードの（中身入り）テキスト情報です。

ノードによって細かな情報の違いはありますが、1行目にノードを処理する内部クラス名や名前が入り、2行目以降にそのノードの情報が記述され、後半にピンを構成する細かな情報が記述されています。

この情報をもとに、以下の2例にそってどのようにカスタマイズできるか見てみましょう。

### 編集例1: 要素を入れ替える
このノードでは、``PinName``が"[0]"～"[2]"になっている行があり、実際に格納される順序もこの添字の数値に沿って格納されるようになっています。
そのため、要素を入れ替えるにはこの数字を入れ替えるように書き換えて、全体をコピーしてBPグラフ上でペーストするだけです。

※以下は修正箇所付近のみを抜粋したものです。``PinName``の欄に注目してください。

```
   CustomProperties Pin (PinId=2B18D1A142D5693A5613AF8A9AF5F20F,PinName="Array",Direction="EGPD_Output",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=Array,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableSet_0 B33823A04D683F11CADB0EA1B31E0FD0,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=9A8E14F14F7D03247031BB8704388B3A,PinName="[0]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cone.Cone",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=7EDE66B548001DC7A5CA0AB43575A4A2,PinName="[2]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cube.Cube",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=01C715DF46136CEBB2CD1596A211FF18,PinName="[1]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/EngineMeshes/Cylinder.Cylinder",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
```

![03.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/2a75900a-41e5-d789-4970-c8c41ff0e9b7.jpeg)
無事入れ替わりました。

:::note warn
**この例では**記述されている行はピンの順序と一致しません！
そのため行を入れ替えるだけでは配列の要素は変わらないことに注意してください。
:::

### 編集例2: 要素を挿入する
要素の数を変更する場合、編集例1に加え、以下の2点に注意する必要があります。
* ``PinId``は各ピン毎にユニークな(重複しない)値である必要があります。この値は16進数の固定桁数(具体的にはGUID値)なので、この例では適当な入力ピンの行を複製したのち、``PinId``の下1桁を変えるなどの修正でOKです。（もちろん添字値も修正してください）
* 2行目の``NumInputs=3``となっている部分も修正する必要があります。**入力要素の数が一致しない場合、不足分は空の要素で埋められ、超過分はエラーピン（赤色）として表示され、コンパイルで失敗や警告が発生するようになります。**

以下は[0]の次に2要素分を[0]から複製したものを挿入し、正しく認識されるように修正したテキストデータと結果です。

```
Begin Object Class=/Script/BlueprintGraph.K2Node_MakeArray Name="K2Node_MakeArray_16"
   NumInputs=5
   NodePosX=528
   NodePosY=112
   NodeGuid=A5C2D1E045AD50590CAAA4AA892E5457
   CustomProperties Pin (PinId=2B18D1A142D5693A5613AF8A9AF5F20F,PinName="Array",Direction="EGPD_Output",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=Array,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,LinkedTo=(K2Node_VariableSet_0 B33823A04D683F11CADB0EA1B31E0FD0,),PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=9A8E14F14F7D03247031BB8704388B3A,PinName="[0]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cone.Cone",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=9A8E14F14F7D03247031BB8704388B3B,PinName="[1]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cone.Cone",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=9A8E14F14F7D03247031BB8704388B3C,PinName="[2]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cone.Cone",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=7EDE66B548001DC7A5CA0AB43575A4A2,PinName="[3]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/BasicShapes/Cube.Cube",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
   CustomProperties Pin (PinId=01C715DF46136CEBB2CD1596A211FF18,PinName="[4]",PinType.PinCategory="object",PinType.PinSubCategory="",PinType.PinSubCategoryObject=/Script/CoreUObject.Class'"/Script/Engine.StaticMesh"',PinType.PinSubCategoryMemberReference=(),PinType.PinValueType=(),PinType.ContainerType=None,PinType.bIsReference=False,PinType.bIsConst=False,PinType.bIsWeakPointer=False,PinType.bIsUObjectWrapper=False,PinType.bSerializeAsSinglePrecisionFloat=False,DefaultObject="/Engine/EngineMeshes/Cylinder.Cylinder",PersistentGuid=00000000000000000000000000000000,bHidden=False,bNotConnectable=False,bDefaultValueIsReadOnly=False,bDefaultValueIsIgnored=False,bAdvancedView=False,bOrphanedPin=False,)
End Object
```

![04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/c796be4b-c9e1-8d91-457c-51faea8067a7.jpeg)

以上のようにエディターで出来ない部分の修正が可能になるため、例えば複数のノードに対してデフォルト値の置換やキャスト先クラスの変更など、かゆいところに手が届く編集ができます。

:::note info
ちなみにUE4.26からはブループリントで定義した関数・マクロ・変数も（右クリックメニューから）コピーすることができ、別のブループリントに（右クリックメニューから）ペーストしたり、上記で行ったような方法で中身を修正することも可能です。
:::


## 非Pure関数の戻り値は疑似変数
例として、以下のような処理を組みました。
![05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/0d4484b2-24cf-72d3-4545-024ff71c6024.jpeg)
Eキーを押したときに座標(A)を計算し、1秒後にその座標(A)に点を描画するという処理です。
※Calc Location の中身は表題とは関係ないので割愛します。
BPを理解している人であれば、この処理の挙動は容易に想像できるかと思います。
では以下の状況ではどうでしょう？
![06.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/d2404d8c-f4fc-857d-11ea-4a2feaeb1aa3.jpeg)

Rキーを押すとどういった結果になるのか、というのが今回のポイントです。（見出しでほぼ答えのようなものになっていますが…）

まず、この処理では**エラーは発生しません。**（ノードのつなげ方次第で警告が発生する場合はあります）
なぜなら、すべての（型を持つ）出力ピンは、一番最初にデフォルト値で初期化されていて、それを参照するからです。
そのためブループリント上では未定義の値は原則存在しません。

:::note warn
C++で定義した構造体やクラスでは、初期化時に正しくデフォルト値を設定していないと、処理次第では未定義の値になってしまう場合があります。その際、エディタのログで起動時に警告として出力されます。
:::

上記を踏まえて処理を追ってみると、以下のような挙動となります。

1. 最初にRキーを押すとデフォルト値の座標（今回の場合はゼロ座標）に点が描画される。
2. Eキーを押した後にRキーを押すと、Eキーを押したときに計算された座標に点が描画される。

といった、変数のような挙動になります。

この**変数のような挙動**は原則として非Pure関数やイベントノードの出力ピンのみが該当します。
この挙動を活用することで、無駄に変数を用意する必要なくグラフをスマートにすることが可能になります。

## Pure関数は参照毎に呼ばれる
これはある程度ブループリントになれてきた方はこの意味は理解できていると思いますが、**いくつかの落とし穴**があります。

基本動作の確認として以下のPure関数を作りました。呼ばれるたびに変数をインクリメントして返すという単純なものです。
![07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/fdb09b57-767f-f045-a799-7c772296b027.jpeg)
そして、以下のように呼び出す処理を組んでみました。
![08.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/f01a41db-9779-f689-f9f5-cc46cc9680db.jpeg)
この時点でどういった出力結果が出るか瞬時に理解できる方は、Pure関数の基本的な使い道を知っていると誇っていいでしょう。

というわけで答え合わせです。
![09.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/fd2c0718-d9c2-966c-c0d0-d345a815a44e.jpeg)
Tキーを一度押すと計4回ループ処理が入り、それぞれ``Pure Func``関数が呼ばれるため、上記の出力結果となります。

:::note info
PIE内のデバッグ出力は新しい方が上に来ます。
:::

:::note info
前後に実行ピンのないノードは**すべてPure関数**です。（折りたたんだノードやマクロは除く）
:::

**ここからが本題です。**

### 落とし穴1: 出力ピン
![10.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/54320bd8-af40-11a6-6d6c-e48b133f0cc3.jpeg)
上記のようなPure関数を組みました。
複数の出力ピンは固定値で返し、呼び出される度にデバッグ出力(``Pure func is friend!``)を行うPure関数です。

これを以下のように使用してみます。
![11.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/0ef44dda-e3a8-50da-c6f5-a6c2194d4909.jpeg)
結果はこうなりました。
![12.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/edd5e825-0569-9877-bcea-853dfeae2ab4.jpeg)
``Pure func is friend!``は1度しか呼ばれていません。
これは、ある関数(ノード)が必要としている入力値がPure関数の複数の出力を使用する場合、**1度だけ呼び出し、その結果を使い続ける**という挙動を示します。
つまり、以下のような複雑な計算をしていても参照元となるピンは一つの関数(ノード)からのみのため、1度だけ呼び出されます。
![13.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/3027c1b1-a26d-6cbd-ca2b-70ea357bc19c.jpeg)
逆に複数のノードでそれぞれ参照される場合はその都度呼び出されることになるため、以下の例では2回呼ばれます。
![14.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/8b1b8538-a4ef-7767-b2b7-97278b515f48.jpeg)
特にマップ型の``Find``関数などを使用する場合は上記のような組み方になる事が多いため、一時変数に格納するなど、不要な呼び出しが発生しないように注意する必要があります。

また、**呼び出す度に変化するランダム関数**や**処理状況によって null を返す可能性のあるオブジェクトを返す関数**など、エンジン側が提供しているPure関数も例外ではないため、上記の挙動に注意して組んでいきましょう。

### 落とし穴2: 入力ピン

複数の条件判断として``AND Boolean``や``OR Boolean``など、複数の入力ピンを扱えるノードがあります。
これらも実際は**ただのPure関数**であるため、**すべての入力値が評価（参照）されて初めて結果が出力されます。**
これはつまり、C++などのようなプログラミング言語仕様にある**短絡評価を表現できない**ということです。
汎用プログラミング言語をよく使うプログラマーにとっては注意すべき落とし穴です。

ちなみに短絡評価とは、例えば複数の OR で条件を指定した時、順に評価をしていく途中、ある条件が True を返して結果が確定した場合、**以降の条件評価を行わない**という挙動をする仕様のことです。

https://qiita.com/Dexctersu/items/4581f3936c834a3442ba

### 落とし穴3: Select ノード
内容的には2番目と同じですが、よく陥りやすい失敗例があるため分けました。

![15.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/af448948-46c6-ad91-b7bc-3e4c1422031b.jpeg)

上記は、キャラクターにあるコントローラーがプレイヤーコントローラーだった場合はそれのカメラマネージャーを返し、そうでない場合はプレイヤーインデックス0のプレイヤーコントローラーにあるカメラマネージャーを返すSelectノードです。
ぱっと見問題ないようにみえますが、**入力ピンはすべて評価する仕様**のため、下部のキャストに失敗しても、True入力ピンはお構いなしに nullに対してカメラマネージャーを参照しようとするため、実行時にエラーが発生します。

この場合はカメラマネージャーへの参照をSelectノードの外で行えば解消できます。
![16.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/fd50a37a-ae95-1aaf-7b1e-05b84fd13cff.jpeg)
キャストに失敗(SuccessがFalse)してもキャスト結果は変わらず参照されますが、SelectノードによりFalseピン側を返すため問題なく処理されます。

# BP裏技・小技編
ここからはすぐに役立つ開発効率アップ法やあまり使わないけど意外な仕様を紹介します。

## プロパティのコピペ短縮法
これは意外と知らない人が多いです。

ブループリントに限らず、プロパティ画面の形式で表示されるすべての項目（エディタ・プロジェクト設定も対象）は簡単にコピペできます！

以下の２つの方法があります。

### 右クリックメニューから
プロパティ名の上で右クリックすると、以下のメニューが出ます。
![17.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/e365e90b-c250-7b78-ea1a-1beb7cb490ae.jpeg)
ここからコピーとペーストが出来ます。簡単ですね！
もちろん配列単位や構造体単位でもコピペは可能です。

### Shift+右クリック & Shift+左クリック
上記メニューに書かれているショートカットを活用すると、もっと早くコピペできます！
Shift+右クリックがコピー、Shift+左クリックでペーストです。

### 更に裏技
**実はこのコピペ機能でもテキスト形式でクリップボードに格納されています。**
なので、たくさんの要素がある配列要素の置換や、詳細設定があるノードの修正・設定値の保管も可能です。

:::note info
この機能を活用することで、**本来入力できない箇所に値を入れることも可能**です。
例えば、ソースコードを修正しなければ設定できないコンボボックス欄（例: ``UCommonInputBaseControllerData::GamepadName``）にも、任意のテキストをペーストすることで入力できます。
:::

## 構造体の疑似変数
![18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/a5963148-81ef-2c5e-893b-c24bd4bc2765.jpeg)
"構造体を作成"ノードは基本として入力ピンの値が格納されたデータとして扱うことが出来ます。
上記はその構造体に対しメンバを設定するノードに入力させています。
この出力(``Struct Out``)ピンを利用すると、特定の値を変更した構造体として利用することが出来ます。
これをイベントグラフ内で配置すると、実質変数としての機能を持ち、設定された値が維持されるようになります。

:::note warn
"構造体を作成"ノードから直接参照すると、作成時に設定されている値が取り出せますが、**配列・セット(設定)・マップは例外で変更された値が格納されます。**
これにより視認性や保守性が下がるため、上記の手法を活用するか、後述するコピーノードを活用してください。もちろん無理せず変数を用意して格納するのもありです。
:::

## コピーノード
以下のノードはおそらく知っている人は少ないかと思います。
![19.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/0d5ee6a3-a00c-d32b-f3d6-5cd9f5ca3c8e.jpeg)
この特殊なノードは「**コピーノード**」と言い、文字通り入力値のコピーを出力します。
※コンテナ（配列等）には使用できません。

これの使い方も特殊で、以下のようなケースで使います。
![20.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/512539/45ff0089-813d-6c5f-9e24-dd233c21f775.jpeg)
このケースでは構造体は参照渡しのため、内容の一部を変更して一部の関数などへ渡したい時は、通常なら**専用に複製した構造体のための一時（ローカル）変数**が必要になります。
コピーノードはそれを肩代わりし、ノードをスッキリさせる事ができます。

:::note info
このノード自体は昔から存在していますが、上記のようなケースぐらいでしか用途はなく、加えて使い方を調べてもほとんど見つかりません。
先述の通り処理そのものは他で同じことが実現できる（一時変数にセットしてそれを修正する）のも要因だと思います。
なので（文字通り）裏技として紹介してみました。
:::

# あとがき
それなりにガッツリ書いてしまった。後悔はしていない。

この記事で紹介したものを活用することでよりスマートなブループリントの組み方や開発の効率化ができれば幸いです。