はじめに
Unreal Engine 4/5 の中核であるブループリントは手軽にプログラミングできる一方で、あまり知られていない隠された仕様や機能が数多く存在します。
この記事では、私が知る中で公式ドキュメントには書かれていない便利な機能や注意するべき仕様、他の方が(ほとんど)紹介していないような内容を中心に役立つものを紹介していきます。
動作環境
ブループリントそのものの根本的な動作・仕様はUE4/UE5共に変化はないため、ここで紹介するすべてが活用できるはずです。
正しく動作しているかについては、執筆時点での最新版である UE5.1.0 で行っています。
BP仕様編
ノードのコピペはテキスト
一番最初はよく知られている仕様から。
ノードをコピーすると、クリップボードにはそのノードを表現するあらゆる情報が独自の書式で表されたテキスト情報として格納されます。
例えば、アクターを新規作成直後に半透明で配置されている "イベント BeginPlay" ノードをコピーすると、以下のようなテキストが取得できます。
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"ノードがあったとします。
このノードには要素の入れ替えや特定位置に挿入を行える機能はありません。
そこで、このノードをコピーし、テキストエディタにペーストします。
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,)
この例では記述されている行はピンの順序と一致しません!
そのため行を入れ替えるだけでは配列の要素は変わらないことに注意してください。
編集例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
以上のようにエディターで出来ない部分の修正が可能になるため、例えば複数のノードに対してデフォルト値の置換やキャスト先クラスの変更など、かゆいところに手が届く編集ができます。
ちなみにUE4.26からはブループリントで定義した関数・マクロ・変数も(右クリックメニューから)コピーすることができ、別のブループリントに(右クリックメニューから)ペーストしたり、上記で行ったような方法で中身を修正することも可能です。
非Pure関数の戻り値は疑似変数
例として、以下のような処理を組みました。
Eキーを押したときに座標(A)を計算し、1秒後にその座標(A)に点を描画するという処理です。
※Calc Location の中身は表題とは関係ないので割愛します。
BPを理解している人であれば、この処理の挙動は容易に想像できるかと思います。
では以下の状況ではどうでしょう?
Rキーを押すとどういった結果になるのか、というのが今回のポイントです。(見出しでほぼ答えのようなものになっていますが…)
まず、この処理ではエラーは発生しません。(ノードのつなげ方次第で警告が発生する場合はあります)
なぜなら、すべての(型を持つ)出力ピンは、一番最初にデフォルト値で初期化されていて、それを参照するからです。
そのためブループリント上では未定義の値は原則存在しません。
C++で定義した構造体やクラスでは、初期化時に正しくデフォルト値を設定していないと、処理次第では未定義の値になってしまう場合があります。その際、エディタのログで起動時に警告として出力されます。
上記を踏まえて処理を追ってみると、以下のような挙動となります。
- 最初にRキーを押すとデフォルト値の座標(今回の場合はゼロ座標)に点が描画される。
- Eキーを押した後にRキーを押すと、Eキーを押したときに計算された座標に点が描画される。
といった、変数のような挙動になります。
この変数のような挙動は原則として非Pure関数やイベントノードの出力ピンのみが該当します。
この挙動を活用することで、無駄に変数を用意する必要なくグラフをスマートにすることが可能になります。
Pure関数は参照毎に呼ばれる
これはある程度ブループリントになれてきた方はこの意味は理解できていると思いますが、いくつかの落とし穴があります。
基本動作の確認として以下のPure関数を作りました。呼ばれるたびに変数をインクリメントして返すという単純なものです。
そして、以下のように呼び出す処理を組んでみました。
この時点でどういった出力結果が出るか瞬時に理解できる方は、Pure関数の基本的な使い道を知っていると誇っていいでしょう。
というわけで答え合わせです。
Tキーを一度押すと計4回ループ処理が入り、それぞれPure Func
関数が呼ばれるため、上記の出力結果となります。
PIE内のデバッグ出力は新しい方が上に来ます。
前後に実行ピンのないノードはすべてPure関数です。(折りたたんだノードやマクロは除く)
ここからが本題です。
落とし穴1: 出力ピン
上記のようなPure関数を組みました。
複数の出力ピンは固定値で返し、呼び出される度にデバッグ出力(Pure func is friend!
)を行うPure関数です。
これを以下のように使用してみます。
結果はこうなりました。
Pure func is friend!
は1度しか呼ばれていません。
これは、ある関数(ノード)が必要としている入力値がPure関数の複数の出力を使用する場合、1度だけ呼び出し、その結果を使い続けるという挙動を示します。
つまり、以下のような複雑な計算をしていても参照元となるピンは一つの関数(ノード)からのみのため、1度だけ呼び出されます。
逆に複数のノードでそれぞれ参照される場合はその都度呼び出されることになるため、以下の例では2回呼ばれます。
特にマップ型のFind
関数などを使用する場合は上記のような組み方になる事が多いため、一時変数に格納するなど、不要な呼び出しが発生しないように注意する必要があります。
また、呼び出す度に変化するランダム関数や処理状況によって null を返す可能性のあるオブジェクトを返す関数など、エンジン側が提供しているPure関数も例外ではないため、上記の挙動に注意して組んでいきましょう。
落とし穴2: 入力ピン
複数の条件判断としてAND Boolean
やOR Boolean
など、複数の入力ピンを扱えるノードがあります。
これらも実際はただのPure関数であるため、すべての入力値が評価(参照)されて初めて結果が出力されます。
これはつまり、C++などのようなプログラミング言語仕様にある短絡評価を表現できないということです。
汎用プログラミング言語をよく使うプログラマーにとっては注意すべき落とし穴です。
ちなみに短絡評価とは、例えば複数の OR で条件を指定した時、順に評価をしていく途中、ある条件が True を返して結果が確定した場合、以降の条件評価を行わないという挙動をする仕様のことです。
落とし穴3: Select ノード
内容的には2番目と同じですが、よく陥りやすい失敗例があるため分けました。
上記は、キャラクターにあるコントローラーがプレイヤーコントローラーだった場合はそれのカメラマネージャーを返し、そうでない場合はプレイヤーインデックス0のプレイヤーコントローラーにあるカメラマネージャーを返すSelectノードです。
ぱっと見問題ないようにみえますが、入力ピンはすべて評価する仕様のため、下部のキャストに失敗しても、True入力ピンはお構いなしに nullに対してカメラマネージャーを参照しようとするため、実行時にエラーが発生します。
この場合はカメラマネージャーへの参照をSelectノードの外で行えば解消できます。
キャストに失敗(SuccessがFalse)してもキャスト結果は変わらず参照されますが、SelectノードによりFalseピン側を返すため問題なく処理されます。
BP裏技・小技編
ここからはすぐに役立つ開発効率アップ法やあまり使わないけど意外な仕様を紹介します。
プロパティのコピペ短縮法
これは意外と知らない人が多いです。
ブループリントに限らず、プロパティ画面の形式で表示されるすべての項目(エディタ・プロジェクト設定も対象)は簡単にコピペできます!
以下の2つの方法があります。
右クリックメニューから
プロパティ名の上で右クリックすると、以下のメニューが出ます。
ここからコピーとペーストが出来ます。簡単ですね!
もちろん配列単位や構造体単位でもコピペは可能です。
Shift+右クリック & Shift+左クリック
上記メニューに書かれているショートカットを活用すると、もっと早くコピペできます!
Shift+右クリックがコピー、Shift+左クリックでペーストです。
更に裏技
実はこのコピペ機能でもテキスト形式でクリップボードに格納されています。
なので、たくさんの要素がある配列要素の置換や、詳細設定があるノードの修正・設定値の保管も可能です。
この機能を活用することで、本来入力できない箇所に値を入れることも可能です。
例えば、ソースコードを修正しなければ設定できないコンボボックス欄(例: UCommonInputBaseControllerData::GamepadName
)にも、任意のテキストをペーストすることで入力できます。
構造体の疑似変数
"構造体を作成"ノードは基本として入力ピンの値が格納されたデータとして扱うことが出来ます。
上記はその構造体に対しメンバを設定するノードに入力させています。
この出力(Struct Out
)ピンを利用すると、特定の値を変更した構造体として利用することが出来ます。
これをイベントグラフ内で配置すると、実質変数としての機能を持ち、設定された値が維持されるようになります。
"構造体を作成"ノードから直接参照すると、作成時に設定されている値が取り出せますが、配列・セット(設定)・マップは例外で変更された値が格納されます。
これにより視認性や保守性が下がるため、上記の手法を活用するか、後述するコピーノードを活用してください。もちろん無理せず変数を用意して格納するのもありです。
コピーノード
以下のノードはおそらく知っている人は少ないかと思います。
この特殊なノードは「コピーノード」と言い、文字通り入力値のコピーを出力します。
※コンテナ(配列等)には使用できません。
これの使い方も特殊で、以下のようなケースで使います。
このケースでは構造体は参照渡しのため、内容の一部を変更して一部の関数などへ渡したい時は、通常なら専用に複製した構造体のための一時(ローカル)変数が必要になります。
コピーノードはそれを肩代わりし、ノードをスッキリさせる事ができます。
このノード自体は昔から存在していますが、上記のようなケースぐらいでしか用途はなく、加えて使い方を調べてもほとんど見つかりません。
先述の通り処理そのものは他で同じことが実現できる(一時変数にセットしてそれを修正する)のも要因だと思います。
なので(文字通り)裏技として紹介してみました。
あとがき
それなりにガッツリ書いてしまった。後悔はしていない。
この記事で紹介したものを活用することでよりスマートなブループリントの組み方や開発の効率化ができれば幸いです。