はじめに
UE5.1がリリースされてから少し経ちますが、MVVMでUI表示を管理できるようになるということで実際にやってみたことなどをまとめました。
以前書いた記事
UMG ViewModelを使うことでできるようになることを以前まとめました!
https://qiita.com/HSKKOU/items/abc562a9decf7409322e
公式ドキュメント
いつのまにか日本語になってる!
https://docs.unrealengine.com/5.1/ja/umg-viewmodel/
ViewModel in ViewModel
ViewModelを登録したWidgetBlueprintでは、「Set VM [ViewModelのClass名]」という関数が自動で作られるので
以下のように指定のWidget(ViewModel登録済みの)に対してViewModelをViewBindingで登録することができます。
これができるということは、UIを細かい単位で開発しつつ使いまわすことがBlueprintノードを構築しなくても簡単に作ることができるということなので、実は結構重要な機能だと思ってます。
欲を言えば、ViewModelの配列がいい感じに使えるようになるとさらに使いやすくなるので次以降のアプデに期待ですね。
UIでリストやグリッド表示するときにリスト内のWidget数を配列の要素数に応じて勝手に変わってくれるような仕組みがあるといいなぁと。
公式ドキュメントでもネスティングが推奨されているので、有用な方法だと思います。
Command
UMG ViewModelはあくまでWidgetとViewModelのそれぞれの変数をいい感じにBindして同期させるような仕組み(Widget <-> ViewModel)です。(間違ってたらすいません)
なので大きくくくると以下のことができるということです。
・Widget <- ViewModel:Modelの変更をViewModelで監視してWidgetに反映する(例:PlayerのHPの変化をUIに反映する)
・Widget -> ViewModel:Widgetの変更をViewModelに通知してModelに反映できる(例:ユーザが入力したPlayer名をデータに反映する)
これに該当しないことで、「ボタンがクリックされたことをModelに通知」を実装しようと思うとViewBindingではできなさそうな感じがします。
そこでいざViewModelにEventDispatcherを追加しようと思ったら、EventDispatcherを登録する場所が見当たらないんですよね、、、
そこで簡易的にですがViewModelにDelegateを持たせてWidget側でTriggerできるように、DelegateをC++でラップしBlueprintで扱える構造体を作って対応しました。
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnTestMVVMSkillDynamicMulticastDelegate, class UTestSkill*, Skill);
DECLARE_DYNAMIC_DELEGATE_OneParam(FOnTestMVVMSkillDynamicDelegate, class UTestSkill*, Skill);
// Delegateを持つ構造体
USTRUCT(BlueprintType)
struct FTestMVVMSkillCommands
{
GENERATED_BODY()
public:
FOnTestMVVMSkillDynamicMulticastDelegate Delegate;
};
// 上の構造体にBlueprint上でBindなどをするためのUtilityClass
UCLASS(BlueprintType)
class UTestMVVMSkillCommandsUtility : public UObject
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable)
static void BindCommand(UPARAM(ref) FTestMVVMSkillCommands& Target, FOnTestMVVMSkillDynamicDelegate InDelegate)
{
Target.Delegate.Add(InDelegate);
}
UFUNCTION(BlueprintCallable)
static void ExecuteCommand(const FTestMVVMSkillCommands& Target, class UTestSkill* Skill)
{
Target.Delegate.Broadcast(Skill);
}
};
↓ ViewModel内でBind
Cmd Focused SkillがFTestMVVMSkillCommands型変数で、SkillSlotViewModelのメンバ変数になっています。
↓ WidgetBlueprint内でExecute
WidgetBlueprintに結局ノードを書いているので、たぶんもっといい方法(ViewBindingを使う方法)をやろうと思えば作れると思います。
とりあえずViewModelを経由してクリックイベントを通知したかったのでこんなものを用意しました。
SoftReferenceなTexture2DをBind
ちゃんとSoft参照を気を付けてゲームを作っていくと、スキルアイコンなどの画像を以下のようにSoftObjectReferenceの形で持ちたくなると思います。
そこで、CommonUIにある「Common Lazy Image」を使うととても便利に画像表示ができるようになります。
「Common Lazy Image」のWidgetをImageの代わりに使うと、SoftObject状態のTexture2DをそのままBrushにいれて勝手にLoadしてくれるようになります(めっちゃ便利!)
また、Loading中に表示する画像も指定可能です!
ただし、この画像をセットする関数が引数2個になっているのでViewBindingで使えないんですよね、、、
そこでCommon Lazy Imageを継承する自前のBlueprintClassを作ることでViewBindingでSoftなTexture2DをBindすることを可能にします。
↓ Common Lazy Imageを継承したClassでSoftTexture2Dを引数にしてSetBrushする関数を用意
↓ ViewBindingでSoftTexture2DをBind可能に!