Let's Enjoy Unreal Engine

Unreal Engineを使って遊んでみましょう

UE5 UMG ViewModelを利用してBPオンリーのMVVMをしてみる

UE5.3が正式にリリースされてしばらく経ちました。新機能も沢山あり、ひとつひとつの機能を把握するのも大変です。

その中の機能のひとつとしてUMG ViewModelというものがあります。これは元々UE5.1から使えていたものでしたが、C++オンリーでしか利用できないというものでした。それが遂にUE5.3でブループリントからでも利用可能になりました!UMG ViewModelを使うことで、ロジックがよりシンプルになり、依存関係をできるだけ少なくしたUMGウィジェットを実装することができるようになります。

docs.unrealengine.com

が、このUMG ViewModelをブループリントで利用した場合、FieldNotify変数という実質必須部分の機能を使うとクラッシュが発生するという問題が5.3リリース後からありました。

それが遂にUE5.3.1リリースで解消されました!

UE-195496 Runtime crash when the FieldNotify variable is set in BP.

これでUMG ViewModelがBPオンリーでも利用できるようになりました。というわけで今回はUMG ViewModelをBPからサクっと利用する方法についてです。なるべくわかりやすく解説するために、公式のものよりかなり簡易的に解説しておりますので、ご了承ください。

MVVMとは

まずUMG Viewmodelを利用する前にMVVM(Model View ViewModel)について理解しておく必要があります。MVVMはMVC(Model View Controler)モデルと呼ばれるUIの機能を分解して、設計を行い、実装を行う手法があり、それを更に発展させたものです。

qiita.com

MVCからMVVMになり、ModelとViewの機能については変化ありませんが、ViewModelという概念が出てきます。ViewModelが出てくることによって、ModelとViewへと素早く変更された情報を通知することができるようになります。

qiita.com

細かい解説は記事を参考にしてみてください。

qiita.com

UMG ViewModelを使う準備

それでは早速UE5でUMG ViewModelを使ってみましょう。まずはプラグインをオンにします。UIカテゴリーにある「UMG Viewmodel」プラグインをオンにして再起動しておきましょう。

次にブループリントクラスの作成を行います。そこで親クラスの選択は『MVVMViewModelBase』クラスを選択してブループリントを作成します。名前は『BP_ViewModel』とシンプルなものにします。

これでMVVMの『ViewModel』クラスができました。次は『View』クラスにあたるUMGウィジェットを作成します。ここで作るウィジェットは特別なものではなく『UserWidget』継承のウィジェットブループリントであればOKです。名前は『WBP_LifeGauge』という名前にして、中には『Canvas Panel』に『ProgressBar』ウィジェットを持つだけのウィジェットブループリントにします。

最後に『Model』にあたるBPはなんでもいいですが、ここではわかりやすいようにTPテンプレートの『BP_ThirdPersonCharacter』を使うようにしましょう。これで『Model』、『View』、『ViewModel』にあたるクラスがそれぞれ作成できました。

これでパーツは揃いました。早速MVVMが出来るように設定をしていきましょう。

ViewModelにFieldNotify変数を作る

早速『BP_ViewModel』クラスを編集していきましょう。まずはfloat変数を2つ作ってみます。ここでは『CurrentLife』と『MaxLife』という変数を作成します。初期値は両変数とも『100.0』としておきましょう。

そして変数を作成すると、変数表示右端部分に見慣れないベルマークがあることに気づきます。

このベルマークが白く塗りつぶされていた場合『FieldNotify』と呼ばれる専用の変数となります。『FieldNotify』変数は名前の通りフィールドへ通知を行うことができる変数です。フィールド通知とはつまり、変数の中身が変更された時に関連性のあるものに対して自動的に通知を行う仕組みです。ここでは『ViewModel』が『Model』や『View』に対してその変更を通知できると考えていれば大丈夫です。

次にFieldNotity変数は関数としても通知が機能することが可能です。ただしその場合は以下の条件を満たしておく必要があります。

  • Pure 関数であること
  • Const とマークされていること
  • 1 つの値のみを返すこと
  • 入力変数を取らないこと

これを満たしている関数であれば変数と同様に利用可能なので非常に便利です。以下は『CurrentLife』と『MaxLife』を使ってそのパーセンテージを返す関数です。この関数も変数と同様にベルマークをチェックしておく必要があります。

最後にFieldNotify変数を選択した状態でフィールド通知というコンボボックスを選択すると、そのFieldNotify変数を変更した際に、その変更を通知する変数と関数を指定することができます。以下の画像では『CurrentLife』変数を変更すると『GetLifePercent』関数も自動的に呼び出されてその内容が通知されることになります。

これで『ViewModel』クラス側の準備ができました。次は『View』側に設定を入れていきます。

Viewに設定をバインドする

次は『View』クラスとなるUMGウィジェットを開きます。デザイナータブを開き、メニューから『ウィンドウ』で『ビューモデル』と『バインディングを表示』を選択して開いておきましょう。

『ビューモデル』パネルから『+ビューモデル』ボタンを押して、作成した『BP_ViewModel』クラスを選択しましょう。既にFieldNotify変数、関数として登録されているものだけが表示されるはずです。

これでViewModelとして登録ができました。

次に『バインディングを表示』パネルから『+ウィジェットを追加』を選択して、ウィジェットを追加します。ここでは『WBP_LifeGauge』が『ProgressBar』ウィジェットを『LifeGauge』という名前で持たせていたので、それを選択します。

『LifeGauge』というウィジェットが追加されたので、今度は鉛筆マークの空白部分を選択すると、ウィジェットに対してバインド可能なプロパティが一覧で表示されます。

『ProgressBar』ウィジェットは『Percent』というプロパティを弄ることで直接ゲージのパーセンテージを変更できますので、ここに対してバインドを行いましょう。

更に今度は右側の『フィールドが選択されていません』という部分をクリックすることで、バインド可能なFieldNotify変数と関数が表示されますので、ここでは『GetLifePercent』関数を使って直接パーセンテージを取得できるようにします。

ウィジェットのプロパティとビューモデルのFieldNotify変数、関数とのバインドができると始めて機能が上手く動作するようになります。更に真ん中のコンボボックスを選択すると、フィールド通知をどのように行うかを選択することが可能です。それぞれのバインド設定がどのように行われるかは公式のドキュメントを確認してみてください。

これで『ViewModel』から『View』へと自動的にフィールド通知を行うことができるようになりました。

Modelから変更を行い、通知する

いよいよ最後となりますが、『Model』クラス側から『ViewModel』を使って実際に通知を行ってみましょう。今回は『BP_ThirdPersonCharacter』内に『BP_ViewModel』クラスのオブジェクト変数を作成し、それをBeginPlay内で生成しています。

『Construct Object From Class』で直接ViewModelを作成し、それを『Create Widget』で作成したウィジェットに対して、直接『BP_ViewModel』クラスを渡せるようになっているはずなので、作成したものを渡してあげましょう。そのまま『Add to Viewport』で画面に表示します。

そのままだとウィジェットが表示されるだけで特に変化がありませんので、Tickで操作してあげましょう。毎Tickごとに『CurrentLife』を更新します。『CurrentLife』はFieldNotify変数となっているので、セットを呼び出すと自動的にブロードキャストされて、その変更がバインドされたViewModelからViewへと通知されるようになっています。

これで実行してみると、どんどんゲージが減っていっていることがわかります!

今回はTickごとに変更しているので、あまり利点がわかりませんが、通常のUMGが行うプロパティバインドと違い、FieldNotifyはその変更が行われた時にしか呼び出されませんので、イベントドリブンとして動作していることがわかります。つまり負荷も小さいということですね。

まとめ

BPのみでUMG ViewModelが使えていることがわかりました。UMG ViewModelはロジックとビューとデータをそれぞれ分割して管理することができる非常に合理的なシステムです。しかもイベントドリブンでの更新となっていますので、パフォーマンス面でも有利であり、まさしくゲーム向けの仕組みと言えます。

BPでは負荷が高く、どうしてもC++で作っていたという方も安心して使えるはずです。複雑なUIを作る際には必ず役立つ仕組みだと思いますので、ぜひ使いこしていきましょう。