UMG ViewModelについて

# 概要
UnrealEngine5.1がリリースされて多くの機能が追加されました。
その中でUMGについて、UMG ViewModelというプラグインが追加れていたのでこの機能について使ってみた感想の記事です。

検証環境
UE5.1
UMG Viewmodel Version 1.0

# UMG ViewModelとは
公式ドキュメントはありますが、現段階(2022/11/20)では英語です。

https://docs.unrealengine.com/5.1/ja/umg-viewmodel/

また、MVVMの概念自体は色々な記事がありますのでそちらを参考にしてください。

https://ja.wikipedia.org/wiki/Model_View_ViewModel


## 公式ドキュメント
一通りドキュメントを読み、手を動かしてみての何となくの理解になります。
## セットアップ
公式ドキュメントのRequied Setup以下の手順が全てですが、かなり多いです。
途中にサンプルコードやViewModelの初期化説明があるので長いですが、セットアップ手順を大雑把にまとめると
1. UMG ViewModelプラグインを有効にする
1. C++でUMVVMViewModelBaseを継承してクラスを作成する
1. 必要なメンバ変数・関数を定義する
1. UMG DesignerでViewmodelsを開きC++で作成したViewModelクラスを追加する
1. UMG DesignerでViewBinding設定をする

になります。
以前のバインディングは毎Tick呼ばれていて処理負荷で不安があるという話でしたが、ViewModelでは値が変わったときだけウィジェットに通知されるようです。

## テスト結果
公式ドキュメントのサンプル通りをプログレスバーのUMGに適用したものです。
キー入力でViewModelの値を増減することで、バインディングしたプログレスバーの表示も連動して変わっています。
![test.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/7f6a2712-d770-caaf-dce2-22d28d443258.gif)


# 使用するメリット
## ViewModelさえ公開すればいい
UIに変更を加えるためにはViewModelさえ公開すればいいので、UserWidgetに関する関数を全て隠せます。
余計な物が見えなくて安全です。

## アーティストとプログラマの作業分担
UIを作成する上では、表示部分(View)とデータ部分(Model)に分けることができ、アーティストは表示部分を担当しプログラマはデータ部分を担当し分業することが多いと思います。
バージョン管理システム(Git,Perforce)で作業しているとUserWidgetを編集する際にチェックアウトすることになりますが、UMGは複数同時にチェックアウトし編集することができません。
アーティストが表示を変更しているときはプログラマが作業出来ず、プログラマがBluePrint作業しているときはアーティストが作業できません。
BPはアーティスト、C++はエンジニアと分ける手がありますが、ウィジェットに関する操作をC++で行うようにするとアーティスト側の作業によってウィジェットが変わった時に問題が出るかもしれません。
そこでViewModelとバインディングを駆使すればウィジェットとデータを完全に分離でき上手く作業分担ができるかもしれません。

## デリゲートも使える
UMVVMViewModelBaseもUObjectなのでデリゲートを持てます。
通常と同じようにグローバルデリゲートを宣言し、メンバ変数に定義します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/068c36e0-7fd0-0a6e-5c82-13e9de5e9f04.png)

セッター内で呼び出せばビューモデルの値変更時に任意の処理を呼び出せます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/2b036c81-bee3-ae95-c9a9-0668fb736fec.png)

ブループリントからもバインドできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/6b46ba27-cd98-e3de-cdf1-b5d99a3292f6.png)

単純なバインディングでは難しいマテリアルの値設定や演出をアーティストのBPに任せることができそうです。

## ViewModelだけを先に作成する
ViewModelウィンドウで、追加したViewModelを選択しCreationTypeをManualにすることで、自分のアプリケーションコード内で作成したViewModelインスタンスを設定することができます。（ドキュメントのCreate Instanceの部分）
ウィジェットを生成する前にアプリケーションコードから値を設定しておけるので、UI表示しないので生成しておきたくないけど値は更新したい場合に役立ちます。アプリケーションコード側ではウィジェットが生成されてるかどうかを意識せずに済みます。
イベントの進行によって表示物が増える系のUIではViewModelを先に作って値を設定しておくみたいな使い方ができるかもしれません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/e0d322d9-f018-5d20-68a4-7ceba74628fa.png)

具体的なコードを示すと
1. レベルビューポートのBeginPlayでViewModelクラスのインスタンスを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/c9242751-5920-873e-4e03-fea4f45f4fc6.png)
2. 適当なコードでViewModelの値を更新
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/1817a4a7-5bc6-926d-5e12-1cabb4a5d0f0.png)
3. ウィジェット生成時にViewModelをセット
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/157391/dddadee2-8f71-1190-28b3-b4a8b72df762.png)

:::note warn
注意点としてAddToViewPort後にビューモデルをセットするドキュメント通りの手順では作成したウィジェット側のViewModelがヌルの状態でアクセス違反のクラッシュが発生しました。
:::

# 不便な点
+ ViewBindingsで何か設定しても保存・コンパイルするまでは反映されません。
    + Compileアイコンが「？」に変わらないので気付きにくいです。
+ バインディングした型が間違っていても保存・コンパイルするまでエラーが出ません。
    + 保存・コンパイルするまで反映されてないからだと思われます。
+ ViewModelsやViewBindingウィンドウで誤って消したバインディングをUndoで戻せません。

バインディングしたのに値が反映されないと思ったときは保存したか確認するといいかもしれません。

# 感想
セットアップは大変ですが利点は多いので使ってみていいと思います。
現状Betaなので不具合やクラッシュがあるかもしれないことには注意が必要です。
不具合は見つけたら積極的に報告していきましょう。