BLOGブログ

2018.04.26UE4/Blueprint

ブループリントマクロのローカル変数

みなさん、ブループリントのマクロ、活用されていますか?
ブループリントのマクロは、ノードグラフを再利用できるだけでなく、ローカル変数による状態保持ができるようになっています。
今回は、ブループリントマクロのこのローカル変数の性質について、簡単なサンプルとともにまとめてみたいと思います。

 ブループリントマクロの特徴

ブループリントマクロには、以下のような特徴があります。

(1) マクログラフ内に配置されたノードは、マクロが配置された場所へ「コピー」される。
(2) オーバーライドできない。外部ブループリントから呼び出すことができない。
(3) 無名のローカル変数を保持することができる。
(4) ローカル変数の値は、マクロが配置されたスコープが有効な間、維持される。
(5) ローカル変数の型として扱えるものが限られている。
– int, floatなどの基本型、Vector, Transform, Rotatorなど。
– オブジェクトの参照やDateTimeなどは、ローカル変数として使うことができない。
(6) ローカル変数の型として、ワイルドカード型を使うことができる。

ここでは、主に(3)~(6)による、マクロの「状態の保持」という性質と、「ワイルドカード型」とについて説明していきます。

ローカル変数による状態の保持

さて、まずはカウンターのサンプルを作ってみたいと思います。
テスト用のレベルを用意し、レベルブループリントにて “Counter” という名前のマクロを新規に作成します。
“Counter” マクロにおいて、以下のようなノードネットワークを組んでみましょう。

ローカル変数に値をセットするには、”Assign” ノードを使います。
ここでは、整数型のローカル変数を1つ使用しており、この変数に1を加算して代入することでカウンターの機能を実現しています。
なお、値をインクリメントするだけであれば、 “++” ノードを使用することも可能です。

動作を確認するため、レベルブループリントに以下のようなノードネットワークを組んでみましょう。

これを実行すると、 “CounterA: 5, CounterB: 3” と表示されます。
よって、「マクロが実行された後、次に実行されるまでローカル変数値が維持される」ことと、
「配置したそれぞれのマクロが独立したローカル変数値を保持している」ことが分かります
「機能」と「状態」を内包している、ちょっとしたオブジェクトのようですね。

マクロのローカル変数の値は、マクロが配置されたスコープが有効な間、維持されます。
配置された場所が関数グラフであれば、関数を抜けるまでの間、
アクタのイベントグラフであれば、アクタが破棄されるまでの間、
レベルブループリントであれば、レベルが破棄されるまでの間になります。

ワイルドカード型とは?

ワイルドカード型は、エディタ上であらゆる変数型のピンを接続することができる型です。
マクロでは、マクロ引数の型として、ワイルドカード型、ワイルドカード配列型などを使うことができます。
ワイルドカード型を利用した分かりやすい例としては、Standard Macrosに含まれる、”ForEachLoop” マクロが挙げられるでしょう。

Arrayがワイルドカード配列型、ArrayElementがワイルドカード型となっています。

なお、1つのマクロ内で扱えるワイルドカードは「1種類」だけのようです。
どういうことでしょうか?

下の図のように、複数のワイルドカード型マクロ引数を持つマクロを作って試してみましょう。
マクロを配置して、1つのピンに値を接続すると、ワイルドカード型を指定したすべての引数が、その型に決定されます。

C++のテンプレート型引数が1つだけ使えるようなもの、と捉えると分かりやすいかもしれません。

ワイルドカード配列型を使ってみよう

ワイルドカード型、ワイルドカード配列型は、マクロのローカル変数の型としても使うことが可能です。
ここでは、ワイルドカード配列型のローカル変数を使って、配列を一定のルールでフィルタするマクロを作ってみましょう。

このマクロでは、入力された配列から、条件に合致する要素だけを取り出して、新たな配列を生成しています。
今回は、ローカル変数の状態が維持されると困るため、最初に配列の中身をクリアしています。
条件に合致するかどうかの判定処理は、以下の図のようにマクロの外で書くようになっています。

これにより、フィルタを使う場所それぞれで、異なるフィルタ条件を設定することができます。
動作を確認するため、レベルブループリントに以下のようなノードネットワークを組んでみましょう。

これを実行すると、”10″, “15”, “13” が表示されます。

なお、Filterマクログラフ内の “ForLoop” の代わりに “ForEachLoop” を使用すると、コンパイルエラーになってしまいます。
こちらは、ワイルドカード配列型を使ったマクロをネストしたときに起きる、既知の問題(UE4.19.1現在)のようです。
今回はForLoopを使うことで回避しています。

ローカル変数利用時の注意

説明してきたとおり、マクロのローカル変数は、配置された場所のスコープにおいて値が維持されます。
マクロの内容によっては、明示的に初期化しないと、前回の値を引き継いで意図しない結果になることがあります。

また、ローカル変数はさまざまな型の変数を複数利用することが可能ですが、
「1つのローカル変数に対する操作を、全て1つのローカル変数ノードから引っ張る必要がある」ことと、
「変数に名前がついてない」ことから、多用するとノードグラフが煩雑で分かりにくくなる恐れがあります。
そもそも、マクロは配置した場所にノードがすべてコピーされるため、
複雑で巨大なマクロを多用すると、ゲームのパフォーマンスに影響が及ぶという問題もあります。

もしマクロが巨大になったり混み入ったりしてきた場合は、
一部を関数化するか、マクロでなくクラスとして実装するなど、リファクタリングを検討しましょう。

それでは、楽しいマクロライフをお送りください。