【UE】UMG Viewmodel の Binding Type について

Unreal Engine

はじめに

Unreal Engine (UE) Advent Calendar 2025 の9日目の記事です!
この記事では UMG Viewmodel の Binding Type について解説します。

Binding Type とは View Bindings で追加した行ごと(バインディングごと)に設定できる、通知の方向のことです。
この画像の矢印マークの ComboBox で設定できるやつが Binding Type です。

この「Binding Type」は、公式ドキュメントでは「バインド方向(Bind Direction)」、UMG Viewmodel のソースコードでは「Binding Mode(EMVVMBindingMode)」と書かれていますが、下の画像のようにエディタでそのプロパティの名前を確認すると「Binding Type」と表示されていたので、この記事では「Binding Type」と呼称します。

(ちなみにこのプロパティは MVVMBlueprintViewBinding.h の FMVVMBlueprintViewBinding という構造体で定義されています↓)

UPROPERTY(EditAnywhere, Category = "Viewmodel")
EMVVMBindingMode BindingType = EMVVMBindingMode::OneWayToDestination;

検証環境

この記事での環境は以下の通りです。

  • Windows 11
  • UE 5.7.0(ランチャー版)
  • UMG Viewmodel 1.0(Beta)

検証用のプロジェクト

今回作成したブループリントは以下の3つです。
「BP_Model」が Model で、「BP_ViewModel」が ViewModel、「WBP_View」が View です。
BP_Model の親クラスは Actor、BP_ViewModel の親クラスは MVVMViewModelBase、WBP_View の親クラスは UserWidget です。

ViewModel

BP_ViewModel には「ViewModelVariable」という Float 型の Field Notify 変数を1つ追加しました。

BP_ViewModel

Model

BP_Model には自身のスポーン時に外部から注入可能な、BP_ViewModel 型の「ViewModel」という変数を1つ追加しました。

また、左矢印キーが押されたら BP_ViewModel::ViewModelVariable の値を0.1減らして、右矢印キーが押されたら BP_ViewModel::ViewModelVariable の値を0.1増やすという処理も追加しました。

BP_Model

以下のようにして BP_ViewModel::ViewModelVariable の変更を検知し、「BP_Model: {現在の BP_ViewModel::ViewModelVariable の値}」と表示する処理も追加しました。

BP_Model

View

WBP_View の Hierarchy には Canvas Panel と Slider を追加しました。

WBP_View
WBP_View

Viewmodels には「ViewModel」という名前の BP_ViewModel 型の ViewModel を追加しました。
Creation Type は Manual です。

WBP_View

View Bindings では行を1つ追加して Slider の Value と ViewModel の ViewModelVariable をバインドしました。

WBP_View

その他

PIE するレベルのレベルブループリントには以下の処理を追加しました。

  1. ViewModel をインスタンス化して変数に保持する。
  2. Model をインスタンス化して ViewModel を渡す。
  3. View をインスタンス化して ViewModel を渡し、ビューポートに追加する。

Binding Type

ここからは各 Binding Type ごとの挙動の違いを確認していきます。

以降は

  • 「Slider のツマミをドラッグしていないにも関わらず、矢印キーを押したら Slider のツマミが移動した」なら「Model(BP_Model)から View(WBP_View)へ通知を送れている」
  • 「矢印キーを押していないにも関わらず、Slider のツマミをドラッグしたらログが表示された」なら「View(WBP_View)から Model(BP_Model)へ通知を送れている」

ということです。

One Way To Widget

まずは「One Way To Widget」についてです。
これは名前の通り、ViewModel から View への一方通行です。

この GIF のように Slider のツマミをドラッグしてもログは表示されませんが、矢印キーを押すと Slider のツマミが移動します。

これはつまり、View(WBP_View)から Model(BP_Model)へは通知を送れていませんが、Model(BP_Model)から View(WBP_View)へは通知を送れているということです。

One Way To View Model

次は「One Way To View Model」についてです。
これは名前の通り、View から ViewModel への一方通行です。

この GIF のように Slider のツマミをドラッグするとログが表示されますが、矢印キーを押しても Slider のツマミが移動しません。

これはつまり、View(WBP_View)から Model(BP_Model)へは通知を送れていますが、Model(BP_Model)から View(WBP_View)へは通知を送れていないということです。

Two Way

次は「Two Way」についてです。
これは名前の通り、View と ViewModel の双方向です。

この GIF のように Slider のツマミをドラッグするとログが表示され、矢印キーを押すと Slider のツマミが移動します。

これはつまり、View(WBP_View)から Model(BP_Model)へも、Model(BP_Model)から View(WBP_View)へも通知を送れているということです。

今回の例のようにして「Two Way」を使用すると MVVMView.cpp の746行目辺りで「ensureAlwaysMsgf(false, TEXT(“Recursive binding detected”));」というアサーションに引っかかってしまうので、今回はこれらの行をコメントアウトして検証しました。

//ensureAlwaysMsgf(false, TEXT("Recursive binding detected"));
//Todo add more infos. Callstack maybe? Log the chain?
//UE::MVVM::FMessageLog Log(Self->GetUserWidget());
//Log.Warning(LOCTEXT("RecursionDetected", "A recursive binding was detected (ie. A->B->C->A->B->C) at runtime."));

恐らく Model から ViewModel の値を変更し、それが View に反映され、その View への反映が検知されて ViewModel 経由で Model に通知されるという、バインディングが循環するような状態になってしまったのが原因かと思います。

One Time To Widget

次は「One Time To Widget」についてです。
これは名前の通り、ViewModel から View に一度のみ通知を送ります。

ですが、この GIF のように Slider のツマミをドラッグしてもログが表示されず、矢印キーを押しても Slider のツマミが移動しません。

Slider のツマミをドラッグしてもログが表示されないのは正常な挙動ですが、矢印キーを押しても Slider の見た目が一度も変化しないのはおかしいので検証内容を少し修正してみます。

OnViewModelVariableChanged() という、Float 型の引数が1つあるカスタムイベントを View(WBP_View)に追加してみました。
このカスタムイベントが呼び出されるとログに「WBP_View: {現在の BP_ViewModel::ViewModelVariable の値}」と表示されます。

また、ViewModel から受け取った通知を Slider の Value に反映させるのではなく、WBP_View::OnViewModelVariableChanged() を呼び出してそれに渡すように変更しました。
(カスタムイベントもバインドできるの便利!!)

ゲームを実行してみると一度のみ「WBP_View: 0.0」と表示され、以降は矢印キーを押しても「WBP_View: {現在の BP_ViewModel::ViewModelVariable の値}」というログは表示されませんでした。

これはつまり、View(WBP_View)から Model(BP_Model)へは通知を送れていませんが、Model(BP_Model)から View(WBP_View)へは一度のみ通知を送れているということです。

One Time To View Model

この記事の執筆時点でエディタから設定できる Binding Type は「One Way To Widget」と「One Way To View Model」、「Two Way」、「One Time To Widget」の4つなのですが、UMG Viewmodel のソースコードを読んでいると「One Time To View Model」という幻の選択肢を見つけたのでそれも試してみました。

「One Time To View Model」(EMVVMBindingMode::OneTimeToSource)は MVVMBindingMode.h の EMVVMBindingMode で定義されているのですが、「UMETA(Hidden)」によってエディタでは非表示になっていたので、その部分をコメントアウトしてエディタでそれを選択できるようにしました。

UENUM()
enum class EMVVMBindingMode : uint8
{
	OneTimeToDestination = 0,
	OneWayToDestination,
	TwoWay,
	OneTimeToSource /*UMETA(Hidden)*/, // ←コメントアウト
	OneWayToSource,
};

すると、矢印マークのない「One Time To View Model」という選択肢が表示されました。

それを選択した後も矢印マークは表示されません…

この状態でゲームを実行してみましたが、View から ViewModel へも、ViewModel から View へも通知が送られませんでした…
(今後に期待!)

最後に

参考記事

お問い合わせ

    タイトルとURLをコピーしました