目次
Boltとは
プログラムを書かずに処理を作成できる、ビジュアルスクリプティングシステム。
UnityがBoltプラグインを買収したことによって、無料で使用できるようになったことが2020年7月に発表されました。
https://blogs.unity3d.com/jp/2020/07/22/bolt-visual-scripting-is-now-included-in-all-unity-plans/
普段C#で書いていて、実際Boltはどうなのかと気になり触ってみることにしました。
作ってみたもの
ゲームの最低限のサイクルを作ることが、Boltの理解につながると思い、
早打ちの射的のような簡単なゲームをC#を使用せずBoltのみで作ってみました。
使用感やこちらを作るのに使用した機能を順番に話していきます。
使ってみた感想
コンパイルで待たされない!
一番印象的だったのが、処理を変えてもコンパイルが走らないことです。
普段C#を編集して、UnityEditorに戻った際、少しのコンパイル時間にイライラします。
BoltのGraphEditorで編集した後、スムーズに実行できるのがとてもよかったです。
UnityのAPIをそのまま呼び出せる!
Unityで用意されているAPIがほとんど用意されているので、違和感なく処理を組んでいくことが出来ます。
普段使用しているメソッド名で検索をかけて、ユニットを追加していくことが可能です。
直感的に編集できる!
複雑な処理を書くとき以外は、直感的に編集できるなと思いました。
また、C#ほど覚えることが多くないので、学習コストも低いと思います。
機能紹介
最低限覚えていれば、ゲームを作れそうだと思った機能を紹介していきます。
Boltの最小単位ユニット
Boltはユニットを配置して繋げていくことで処理を記述します。
赤枠の部分がないユニットもありますが、こちらは基本的に入力につないで使用します。
判定、分岐、ループ処理などはBolt特有だと感じました。
参考に、以下はif分の処理です
ifではなくbranchという表現になっています。
プログラムで書くとこのような感じですね。
1 2 3 |
if(remainBullet > 0) { // trueの場合の処理 } |
FlowMachine
C#で実装するときのComponent(Monobehavior)のようなものです。
FlowMachineをAddComponentして、FlowMacroで処理を書いていきます。
↓Edit Graphでエディターを開く
イベント
C#で実装するときと同じく、Start,Updateから処理を作っていきます。
ライフサイクル、
OnEnable,Start,Update,FixedUpdate,LateUpdate,OnDisable,OnDestroy
物理イベント、
OnCollisionEnter,OnCollisionStay,OnCollisionExit,OnTriggerEnter…
など各種イベントから処理を作っていくことが出来ます。
値の管理方法
以下の種類で、値が管理されます。
種類 | 用途 |
---|---|
Graph | BoltのGraphEditor内で使用できる変数 C#でいうクラスのメンバ変数 |
Object | ヒエラルキーでも扱える変数、C#でいう[SerializeField] |
Scene | シーン内で共有される変数 |
App | アプリ内で共有される変数 |
Saved | デバイスに保存される変数 |
Graphを編集する際、左側からすべての種類の値にアクセスできます。
メソッド
ほかのFlowMachineの処理を呼び出したいと思い、
「Unity Bolt function」で調べてみたところ、目的の情報になかなかたどりつけませんでした。
調べていったところ、カスタムイベントを使用するとやろうとしていたことが実現できそうでした。
まず呼び出し先で処理を作ります。
CustomEventUnitを作成し、2つめの入力でイベント名を指定します。
Argumentsで1以上の値を指定すると、呼び出し元から値を受け取ることもできます。
次に、イベントを呼び出す場合です。
CustomEventTriggerユニットを作成し、
1つ目の入力には先ほど作成したCustomEventUnitの2つ目の入力で指定した文字列を指定
2つ目の入力には呼び出し先のFlowMachineを指定します。
こちらでArgumentsを1以上に設定すると、呼び出し先のイベントに値を渡すことが出来ます。
Timer
Boltで遅延処理を実行する際は、TimerUnitが便利です。
実行、入力、出力がいろいろ用意されているので様々な使い方ができそうです。
参考として、今回作成したゲームの弾切れ時の処理は以下になります。
リロード完了時間をDurationで指定して、弾切れになったときStartに処理が流れてくるようにしておきます。
Startedではタイマー開始時の処理(「Reloading…」を表示する)、
CompletedではDurationで指定した秒数が経過したときの処理(「Reloading…」を非表示にする)
今回は使用しませんでしたが、Tickでは毎フレーム実行が流れてくるので、
Remainingから残り時間を取得して、あと何秒でリロードが終了するかをUIで表示することにも使えそうです。
他にも似たものでCoolDownUnitがあり、Cooldown時間のあるスキルの実行を管理するのにも使えそうです。
時間に関するユニットは他にも、
WaitForSeconds,WaitUntil,WaitWhile,WaitForNextFrameがあるので、大体の遅延処理はこちらでまかなえると思います。
StateMachine
今回作成したゲームの進行管理でステートマシンを使用しました。
遷移は複雑でなく、準備→ゲーム→リザルト→準備といったループになっています。
StateMachineもStateFlowと同様にAddComponentすることで使えます。
以下今回のゲームで使用したStateMachineになります。
コード上でStateMachineを書くより見やすく、メンテナンスがしやすそうだと感じました。
今回のゲームでは準備→ゲーム、リザルト→準備はEnterキーを押したら遷移するという判定ですが、
ゲーム→リザルトでは、すべてのオブジェクトに弾を命中させたら遷移させるようにしています。
ゲーム中のStateFlowに関しまして、上記画像では省略させていただきましたが、以下のようになっています。
OnEnterStateでは、
プレイヤーの入力を受け付けるようにしています。
Updateでは、
経過時間のカウントと、それをUIに適用させています。
機能紹介は以上になります。
やりにくかったこと
C#に慣れているので、やりにくいと感じたこともあります。
複雑なロジックを組む場合は、C#のほうが良い
プログラムは基本上から下に流れていきますが、
Boltは2次元のキャンバスにノードを配置してつないでいくので、上手く配置していかないと後々分からなくなります。
Boltを使うときは、オブジェクト指向の考え方をいったん頭から話したほうが良い
機能紹介でも書きましたが、他のオブジェクトのメソッドを呼びたいと思い調べても、
そのような概念ではやり方が出てきません。
もちろん、継承といった概念もないのでやろうと思わないほうが良いです。
(調べてみたところ、継承、メソッド定義、オーバーライドができるBolt拡張がありました)
https://github.com/LifeandStyleMedia/UAlive
2020/9/27現在、安定板にはまだなっていないそうです。
情報を調べるのが大変だった
基本行き着く先は、ludicのフォーラムかyoutubeでした。
ほとんど英語で、日本語の情報は現時点でチュートリアルくらいしかありません。
使いたい処理は基本C#で調べて、それをBoltに落とし込むスタイルで進めていく必要があったため、
Boltを使うにはまずC#の使い方を知っている必要があると思います。
規模が大きくなると、管理が大変そう
C#だと、IDEの検索、ジャンプ機能で処理を追っていくことが可能ですが、Boltではそういったことが出来ません。
特にCustomEventとなると、どこから呼ばれているのか追えなくなると思います。
大変にならないように管理するノウハウも作っていく必要があります。
バージョン管理の点においても、差分が目で見て分からなかったり、
コンフリクトが起きた際に、コードのように解消できない点も苦しいと思います。
Bolt活用方法
活用できそうと思ったことを一つずつ上げていきます。
プロトタイプ作成
ゲームの面白さや動きの感触を確認するためのもので、
コードを書いても使い捨てになりますし、
Boltで処理や値の管理がぐちゃぐちゃになっても後に困らないので、プロトタイプの作成には適していると感じています。
StateMachine
コード上でステートの切り替えを書くと、全体像が把握できなくなるので、
処理は基本C#で書いて、状態管理だけBoltのStateMachineで管理する使い方が出来そうだと感じました。
AssetBundleに入れる
Boltはプログラムではなく、アセットで管理されているためコンパイルがいらないので、
アプリ更新なしに処理を変えるという使い方もできるのではないかと感じました。
試しに、リソースからFlowMachineに動的にFlowMacroを追加できるかやってみました。
AssetFlowMacro
1 2 3 |
var flowMachine = gameObject.AddComponent<FlowMachine>(); var macro = Resources.Load<FlowMacro>("AssetFlowMacro"); flowMachine.nest.SwitchToMacro(macro); |
問題なく呼ばれました。
まとめ
コアな処理はエンジニアがC#で書いて、
ゲームデザイナーが処理を組み合わせてゲームを作っていく使い方ができると理想的だと感じます。
結論としましては、
すべてをBoltで実現させるのではなく、Boltをツールとしてうまく使いながらゲームを作っていくのが良いとおもいます。
Boltで実現できることはC#で実現可能ですが、C#でできてもBoltでできないことのほうが多いです。
今後Boltがどういった使われ方をされていくのかが楽しみです。