【UE5】UnrealEngineのUIにMVVMが使えるようになる？

# はじめに
UE5.1のPreviewが来ましたね。
SpeedTreeがNanite対応だとか、半透明反射できるようになったとかいろいろUpdateありますが
個人的に**UMGでMVVMが使えるようになる**ってとこが一番気になったのでテストしてみました！
MVVMができるようになったら、UI実装でひいひい言いながらノード組むこともなくなりそうでめっちゃ期待！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/a1e7e1e6-55dd-8fd2-cc8a-016393f0e7d8.png" width=50%>
UE5.1ロードマップ

https://portal.productboard.com/epicgames/1-unreal-engine-public-roadmap/tabs/80-unreal-engine-5-1-in-progress

# MVVMが使えるようになってうれしくなる（だろう）こと
1. WBPでTextとかに反映するためのノードを組まなくても、ViewModelの変数とWidgetを紐づけるだけでよくなる
1. ViewModelの差し替えで、どんな感じにUIに値を反映させるか変更できる（適当な値を入れたViewModelのMockが作れる）
1. エンジニアが変数を用意すればUIデザイナ側でBindまで組むことも可能なので、WBP全体がエンジニアの手から離れる（かも）

# テストしてみた時の動作についてまとめたTweet
https://twitter.com/hskkou/status/1579539907965435905?s=20&t=zte-jgA3T-mjpomZpq5hJw

# 公式ドキュメント
ついに公式ドキュメントも用意されたようです！
https://docs.unrealengine.com/5.1/ja/umg-viewmodel/

# 導入方法
1. まずはUnrealEngine5.1をインストール
1. Pluginの有効化
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/f415ddea-0257-f152-3532-50a8b459cf58.png" width=50%>
1. Project名.Build.hで、Dependencyに"ModelViewViewModel"を追加
   **これより下のC++のコードは「UE」の名前でProjectを作ったので、UEのPrefixが入ってます。（わかりづらくてすいません、、、）**
    ``` c++
    // Copyright Epic Games, Inc. All Rights Reserved.
    
    using UnrealBuildTool;
    
    public class UE : ModuleRules
    {
    	public UE(ReadOnlyTargetRules Target) : base(Target)
    	{
    		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
    
    		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "HeadMountedDisplay", "EnhancedInput", "UMG", "ModelViewViewModel" });
    	}
    }
    ```
1. これで導入はできたはず！

# UnrealEngineで使ってみる！
1. ViewModelのC++を作る
    ``` c++
    // 適当にこんな感じの基底を作ってみる
    UCLASS(BlueprintType, Blueprintable)
    class UE_API UUUETestViewModel : public UMVVMViewModelBase
    {
    	GENERATED_BODY()
    };

    ```
1. Compileして、上を継承したBlueprintを作る。適当にBP_TestViewModelという名前にしました。
   上のMVVMViewModelのClassは選択できなかったので、多分今のところはC++で基底を作るしかなさそう？
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/62d37f2b-9ed2-156b-97b7-d1641d0d7c4f.png">

1. 適当にWidgetBlueprintを作成
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/6b580407-0f03-0f81-c95f-bcbbc6ac3f93.png">

1. 上のメニューから「ウィンドウ / View Binding」を選択してViewBindingのWindowを開く。
    ついでに「ウィンドウ / View Model」も選択して開いておく。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/4c08e74b-63da-db89-5cb9-1d4ae80b6f35.png">

1. 「Create ViewModel」を押して、さっき作ったViewModelを設定。
    さきほど作ったBP_TestViewModelがこのWBPに登録されました！
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/088bd442-edfd-dc1c-8b2d-9264cd6c4f05.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/388da759-b5fa-a978-0afb-2e45c8bbcaaf.png)

1. 作ったBP_TestViewModelに戻ります。
    変数や関数が登録できるような見た目をしています。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/f22cbc65-0f76-5adf-4f7b-638ea46aa3eb.png)

1. 適当に変数を追加
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/16cd6e7a-a5f1-56ff-7116-262324f17b71.png)

1. WBPに戻ります。
    適当にTextをDesignに追加
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/5f2cd6ae-64ee-011f-c18d-1ca7772cf4f1.png)

1. ViewBindingで
    「Add Widget」、追加したTextウィジットとTextウィジットのText変数を登録。
    登録したViewModelの変数も登録。
    矢印はたぶん反映できる方向なのでViewModelからViewに反映させる左向きに設定。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/a2b8d9f4-120b-2152-0e31-93561d09d0d6.png)

1. これでViewModelとWBPの連携は完成！

1. 確認のため、適当にLevelBlueprintでこんな感じのノードを組むと、こんな感じに！
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/0ca99191-7b61-b295-5e06-1987e680a772.png)
    ![ダウンロード.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/5a42a092-8f0e-f619-53ab-46e4be02392a.gif)

1. あとはこのViewModelをいい感じに外部から操作すればいい感じに！

# ViewModelのセット方法（たぶん）
WBPにExtensionというなにか拡張できる仕組みがあるっぽく、そこからMVVMViewがとれるので
UMVVMViewを継承したClassを用意してCastすればViewModelを外部からセットできると思います。（未テスト）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/3d453488-c9f1-509e-fd90-35fe138e3df7.png)

# 関数のBind
ViewModelに関数を作ってViewBindingでButtonにBindしようとしても、作った関数がうまく候補に表示されないので
なにか方法が違うのかも？
調査中です、、、

# ViewModel内の変数
ViewModelのBlueprintで宣言した変数（ここでいうStackNum）は、自動でSetterが暗黙定義されているのか、EventDispatcherでSetterが登録できるようです。（いちいちセットする用の関数の定義がいらなくなるので便利！）
UMVVMViewModelBaseでINotifyFieldValueChangedが継承されてるのが原因っぽそうです？
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/98140/0509cc4e-7e53-af08-c8ba-492707875581.png)

# まとめ
今までUMGでUIを作るのが正直億劫（C++でModel組んでも、結局Blueprint側で反映処理を書く必要がある）だったのですが、MVVMの導入によってその心配が一切なくなる未来が見えますね！
既存の様々なデータ構造（GASのAttributeSetとか）にも対応して、ちょいちょいとWBPで設定するだけでUIの反映処理が全部組める日が来るのが待ち遠しいです！