ドメイン特化言語の進化形としてのUE4ブループリント

最近あんまりドメイン特化言語 (DSL)について前ほど深く考えてこなかったけど、SQLVisual BasicMax/MSP、HoudiniなどのいろんなDSLに触れてきた言語マニアとして、最近、Unreal Engine 4 (UE4) のブループリントDSL・ビジュアルプログラミング言語としてすごく良くできてるなぁと思う。

f:id:funatsufumiya:20200621234713j:plain

(※上記画像は公式ドキュメントより引用。)

普通のプログラミング言語に慣れてると、ブループリントのようなビジュアル言語って初見では逆にマウス操作が多くて辛そうな気がするけれど、流石いろんな工夫がされていて、かなり普通に使えて生産性が高いと感じる。

以下は、DSL・ビジュアル言語としての特徴に主眼を置いて、ブループリントを他の言語と比較したときに興味深い点を挙げてみた。( ブループリントを知らない人にも読めるように書いたつもりなので、入門としてもどうぞ。 )

実行とデータの分離 + フロー制御 の最強コンビ

以下の図で、白い線が実行ワイヤで、その他のカラフルな線がデータワイヤ。データワイヤは型によって大雑把に色が違う。実行ワイヤが実行される順序や時間的推移を示していて、データワイヤがデータの流れを示す。

ちなみに実行順序は左から右 (→方向)で、上下は実行順序に直接関係はない。

f:id:funatsufumiya:20200621230303p:plain

仮想的にJavaScriptで同じ処理を書くとすればこんな感じ。(※ UE4ではC++で同等の処理を書くこともできるけど、今回はわかりやすさ優先で仮想言語。)

var tableActor;

// tableActor = ...

function BeginPlay(){
    var s = tableActor.GetDisplayName();
    PrintString(s);
}

// BeginPlay();

実行ワイヤとデータワイヤの2種類が分かれているおかげで、一つは、時間の流れとデータの流れが分離されていて分かりやすく、逐次処理が書きやすい。

さらに、実行ワイヤでは各種フロー制御時間遅延 (Delay) が使えて、特に後者を使ってすごく直感的に非同期が実現できるのはすごいと思う。

例えば以下の例だと、AD が同時に表示されたあと、0.5秒後に B、さらに0.5秒後に C が表示される。

f:id:funatsufumiya:20200621225208p:plain

途中にあるSequenceというノードと組み合わせてあるのがポイントで、JavaScriptで仮想的に同様のコードを書くとこんな感じ。

PrintString("A")

setTimeout(() => {
    PrintString("B")

    setTimeout(() => {
        PrintString("C")
    }, 500)
}, 500)

PrintString("D")

このDelayを使ったコードは、UnityなどのC#でいうコルーチンを使ったコードと似ていて、時間推移をすごく自然に表現できるのが素晴らしい。TimelineノードやLerpノードと組み合わせるとさらに自然になる。

また、全く別の例を挙げると、例えばステージ終了演出など、あるイベントを1回だけ実行させたいとき (2回以上呼ばれると困るとき) は、DoOnceを使う。

f:id:funatsufumiya:20200621225824p:plain

これも普通に考えると変数とかを準備したくなるので、ちょっと意外な書き方だと思う。でも慣れると自然。もちろん変数とIF (ブランチノード) を使って書くこともできる。

ブループリントにはこういう痒いところに手が届く機能が多数用意されていて、DSLとしてとても便利。

データ型の特徴を活かした補完機能や、コーディング機能群の充実さ

ちなみにデータワイヤの方にも様々な特徴があって、DSLとして特筆すべき点としては、型が事前にわかるおかげで、Visual StudioとかでいうところのIntelliSense、つまり自動補完が使える。

f:id:funatsufumiya:20200621231103p:plain

また、微妙に型が違うときに 自動的に変換を挿入してくれる便利機能もある。ほかにも型としては配列や構造体、ループも使えるので、普通の言語さながらの書き方もできる。型の特徴はフル活用できる印象。

さらに重複や繰り返しを避ける機能として、関数マクロを独自に作ったり、イベントを独自に定義してイベントドリブンの手法を使うこともできる。

ちなみにC++と連携することもできるので、特に詳細で精密な実装はC++で書けば良く、逆にC++を書かなくてもほとんどのことが実現できる。

ビジュアルプログラミング言語に特有の苦労については、こうした機能やUIの補助で随分楽になっている。整列や検索の充実、後述の差分機能なども嬉しい。

用意されている関数自体が便利

ブループリントにはいくつか便利な関数が準備されていて、例えば Set View Target with Blend なんかは、カメラの移動ができ、かつこの関数単体でイージングもできるスグレモノ。一応他の関数群を組み合わせて同じことができるので、一種のシンタックスシュガー的な感じ。

f:id:funatsufumiya:20200622100950p:plain

こういう気が利く関数が他にも多数あって、ブループリントがDSLとして便利なのは、やはりUE4ゲームエンジンとして生産性を上げる機能や関数を複数搭載しているからだと思う。

マテリアルBPとアニメーションBPは別種として存在

ちなみに、同じブループリントという名前でも、マテリアル記述用のブループリント(マテリアルBP)とアニメーション設定用のブループリント(アニメーションBP)は種類が異なっている。通常のブループリントととは、一部類似性はあるけれど全く別の言語。

f:id:funatsufumiya:20200621232709p:plain

これはマテリアルに関してはGPU上のパイプライン処理される点を考えると別言語になっていて当然だし、一種のDSLとして見たときにすごく簡潔で使いやすい。

ちなみにマテリアルBPには実行ワイヤは存在しない ( 上図の白線は実行ワイヤでなくデータワイヤ )。シェーダーは基本パイプライン処理であることを考えると、データフロー型の方が相性が良く理解しやすいという判断なのだと思う。

( なお従来のシェーダ言語が使いたいときはCustomノードに直接シェーダ言語を書くこともできるし、例えばコンピュートシェーダが必要な場面では、C++から独自シェーダを呼ぶこともできる。また、NiagaraCascadeなどのパーティクルシステムがそもそもGPUパーティクルを扱えることもあり、ブループリントで出来ない処理は意外に少ない。)

デバッグ・差分が視覚的にわかる

f:id:funatsufumiya:20200622010100j:plain

(※上記画像は公式ドキュメントより引用。)

ちなみに、ブループリントはデバッグや差分が目で見てわかるのも便利。

今実行されているノードがどれかわかるシミュレーションモードがあったり、エラーしているノードが視覚的にわかったりと、ビジュアル言語の特性がすごく生かされていると思う。

差分についてはチームで開発するときは必須だと思うのでありがたい。個人的にはブループリントがバイナリで保存されているのが、gitとの相性を考えるとたまにキズかなと思うけれど、ビジュアル言語としては差分やデバッグがちゃんと見れるのは珍しいと思うし、ブループリントを実用的にしてくれている必須機能だと思う。

ちなみに、マテリアルブループリントの数値デバッグ機能もすごく便利。普通、シェーダ言語の結果ってとてもデバッグしづらいので、この機能はGLSLやHLSLとかの普通のシェーダ言語にも標準装備でほしいくらい。

f:id:funatsufumiya:20200622010807p:plain

まとめ:BPのようなビジュアル言語がゲーム以外の分野にもあったらな

さて、早足になったけれど、ブループリントの面白さはなんとなく理解してもらえたかなと思う。こうしてみると本当に汎用性が高いし、速度面でもネイティブ化機能を搭載していたりと実用的。

そういった点も含めて、ブループリントはより汎用言語に近い実用的ビジュアル言語といった感じで、ビジュアル言語の一種のモデルケースといえると思う。これが実際のゲームなどで実用的かつ生産的に使われていると思うと流石だしすごい。

ブループリントを使うとプログラマ以外にも案外簡単にゲームが作れることを考えると、同様のビジュアル言語がゲーム以外の分野でも応用されれば、プログラミングの裾野をより一般の人達にも広げてくれると思うし、本当に生産性が高いものになりそうな気がする。

Max/MSPに初めて触れたときに、こんなビジュアル言語が汎用言語にもあったらな、と思った感想に近いけれど、Max/MSPなどに比べてブループリントでは実行ワイヤが分離されている点やFor Loopなどのフロー制御ができることを考えると、ビジュアル言語のモデルケースとしては、ブループリントはより汎用的で実用的なモデルケースといえるんじゃないかと思う。

もちろん、DSLドメイン特化しているから便利なのであって、それを一つメタな視点で見るのはあんまり意味がない気もするけれど、プログラミング言語史として見たときにブループリントはすごく興味深いと思った。