[Tokyo DevDays 26'] UE5のUIフレームワークとUIアニメーションの解説

-- Views

August 31, 26

スライド概要

2026年8月26日に開催されました「Unreal Engine Tokyo Dev Days 26'」イベントの「UE5のUIフレームワークとUIアニメーションの解説」講演のスライドです。

profile-image

Unreal Engineを開発・提供しているエピック ゲームズ ジャパンによる公式アカウントです。 勉強会や配信などで行った講演資料を公開しています。 公式サイトはこちら https://www.unrealengine.com/ja/

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

UE5のUIフレームワークと UIアニメーションの解説 Epic Games Japan Lead Software Engineer, Developer Relations 鍬農 健⼆郎 Last update: Aug 26, 2026

2.

セッションのゴール 話すこと ● UE5 の UI がどのように進化してきたか ● プロジェクトで UI を組む際のポイント ● UMG アニメーションに関する変更 ポイント ● Widget単体ではなく、画⾯管理‧⼊⼒‧データまで含めてUIを設計 ● ⼤規模UIで破綻しにくい構成 ● 実装時の判断基準と実例

3.

⼤規模プロジェクトで表⾯化する問題 UE5 では⼤規模UI向けの仕組みが段階的に追加されてきた # ⼤規模プロジェクトで起きること 問題 実現⽅法 1 対応デバイスや⼊⼒経路の追加 ⼊⼒処理の競合 Action Router 2 画⾯数が膨⼤かつ複雑 画⾯遷移‧フォーカス管理が複雑化 CommonUI 3 表⽰データが増え、更新も頻繁 更新コストと、改修の波及範囲 MVVM 4 演出が増え、同時に⼤量再⽣ フレーム時間 Sequencer ECS 5 関わる⼈が増える 実装ルールの統⼀が難しくなる MCP / Toolset

4.

1. UIのこれまで Agenda 2. UIフレームワーク 3. UIアニメーション 4. UIのこれから

5.

第1章 UIのこれまで

6.

UE5でのUI機能の偏移 5.0 5.1 5.2 5.3 5.4 5.5 5.6 5.7 CommonUI Beta CommonUI Experimental MVVM Experimental 5.8 CommonUI ProductionReady MVVM Beta UMG Preview Experimental UIComponent Experimental Widget Animation SequencePlayer Widget Animation Sequencer ECS MCP/Toolset Experimental

7.

UE4→UE5で何が変わったか? Widget中⼼の構成 (UE4) ● Widget からゲーム側のデータを直接参照 Frameworkを利⽤した構成 (UE5) ● GetOwningPlayer() → コンポーネント → 値 ● Widget は表⽰処理を中⼼にする 表⽰データは View Model 経由で受け取る ● OnKeyDown / SetKeyboardFocus ● Widget が⾃分で⾒た⽬を持つ 各 WBP に⾊とフォントが直書き Widget は渡されたデータを描くだけ ⼊⼒はフレームワークがルーティング Activatable Tree にバインドを登録 ● ⾒た⽬はアセット化されたStyle/Material 統⼀されたデザインとスタイル Widget から⼊⼒‧画⾯管理‧データ処理を分離

8.

画⾯‧フォーカス ⼊⼒ データ 第2章 UIフレームワーク

9.

画⾯‧フォーカス Common UI ⼊⼒ データ 第2章 UIフレームワーク

10.

CommonUI は画⾯の状態管理を引き受ける CommonUI なし ● パッドのボタン管理が複雑 CommonUI が提供 ● 画⾯ごとに OnKeyDown を書く ● 異なるキー処理を複数画⾯に実装する ボタンイベントをテーブルで定義、キー画像も同梱 ● キーの変更や交換に弱い ● 管理が複雑な Widget のフォーカス ポーズメニューを開いてもキャラが動く Activatable Widget フォーカス管理と活性/⾮活性の概念を Widget に導⼊ ● 画⾯を開いた瞬間フォーカスが迷⼦ ● Input Action の抽象化 Action Router 活性な Widget Tree 構造を持ち⼊⼒を上から順に配送 ● SetInputMode を画⾯ごとに呼ぶ Input Config レイヤ単位で Game / Menu / All とカーソル状態宣⾔ ● Container Stack / Queue 重ね合わせと排他をフレームワーク側で保証

11.

状態管理:「表⽰中」と「アクティブ」を分離する CommonActivatableWidget は「表⽰されている」と「今アクティブである」を分離 // Engine/Plugins/Runtime/CommonUI/Source/CommonUI/Public/CommonActivatableWidget.h void ActivateWidget (); // 活性化 → 入力バインドが有効になり、フォーカスが入る void DeactivateWidget (); // 非活性化 → 入力バインド無効表示は残せる virtual UWidget* NativeGetDesiredFocusTarget () const; // 活性化時にどこへフォーカスするか 主要フラグ プロパティ 意味 使いどころ bAutoActivate Construct 時に⾃動で活性化 HUD など常時アクティブなもの bIsBackHandler 「戻る」⼊⼒を⾃分が処理する 画⾯ルート, NativeOnHandleBackAction() を実装 bSupportsActivationFocu s 活性化時にフォーカスを奪うか HUD レイヤは false にして裏でフォーカスを取らせない bAutoRestoreFocus 復帰時に前回位置へ戻す リスト画⾯戻ったら同じ⾏に居てほしい 注意:GetDesiredFocusTarget()未実装のActivatable Widgetは、初期フォーカス未保証で操作不能になりやすい

12.

画⾯遷移:Stack で表す CommonActivatableWidgetContainerBase とその派⽣が遷移のルールを持つ クラス 挙動 UCommonActivatableWidgetStack 後⼊れ先出し、上を閉じると下が復帰 UCommonActivatableWidgetQueue 先⼊れ先出し、1 つずつ順番に表⽰ UCommonActivatableWidgetSwitcher 排他切り替え(タブなど) Stack が保証すること ● ● ● push した Widget が活性化、下の Widget は⾃動的に⾮活性化、⼊⼒を受けない pop すると下が復帰し、フォーカスも戻る 遷移アニメーション (SCommonAnimatedSwitcher ) を挟める

13.

Lyra :「1 Layout + 複数Layer」で組む UGameUIManagerSubsystem GameInstance サブシステムUI 全体の⼊⼝ 所有 「レイアウトをどう出すか」の⽅針 UGameUIPolicy LocalPlayer毎に⽣成 UPrimaryGameLayout プレイヤー1⼈分の UI ルート Layout上に複数のLayer Stackを置く UI.Layer.Game HUD(常時表⽰‧フォーカスを取らない) UI.Layer.GameMenu インベントリ等、ゲーム内メニュー UI.Layer.Menu フロントエンド、ポーズメニュー UI.Layer.Modal 確認ダイアログ、エラー

14.

Lyra :画⾯を⾮同期にPushする ⼤規模 UI では WBP は 重いので、ハード参照を持たず⾮同期にロードして push // Samples/Games/Lyra/Plugins/CommonGame/Source/Public/PrimaryGameLayout.h template <typename ActivatableWidgetT = UCommonActivatableWidget> TSharedPtr<FStreamableHandle> PushWidgetToLayerStackAsync ( FGameplayTag LayerName, bool bSuspendInputUntilComplete, // ← ロード中の入力を止める TSoftClassPtr<UCommonActivatableWidget> Class, // ← ソフト参照 TFunction< void(EAsyncWidgetLayerState, ActivatableWidgetT*)> StateFunc); ポイント ● ● ● TSoftClassPtr … UI ルートが全画⾯を参照しない bSuspendInputUntilComplete … ロード中の連打で⼆重 push させない EAsyncWidgetLayerState(Initialize / AfterPush / Canceled )… 初期化時、押し込み後、を分離

15.

⼤規模化で必要な機能はある程度⽤意されている 機能 クラス 何のために存在するか Widget プーリング ICommonPoolableWidgetInterface Stack / ListView がインスタンスを使い回す 遅延画像ロード UCommonLazyImage TSoftObjectPtr<UTexture> を⾮同期ロード (遅延中はスピナー表⽰) 遅延 Widget UCommonLazyWidget / UCommonLoadGuard 「ロード中」表現をフレームワーク化 リスト UCommonListView / TileView / TreeView エントリ Widget を 仮想化 + プール、インスタンスは画⾯分だけ プラットフォーム分岐 UCommonUIVisibilitySubsystem + UCommonHardwareVisibilityBorder 「パッド時のみ表⽰」「モバイルのみ表⽰」を Widget Tree上で宣⾔ 数値表⽰ UCommonNumericTextBlock ⽤途に応じた数値表現 リッチテキスト UCommonRichTextBlock + UCommonUIRichTextData インラインアイコン‧スタイルをデータテーブルで⼀元管理 ポイント:これらは「便利機能」ではなく、⼤規模 UI で⾃作するものを先に⽤意してあるもの ⾃作すると CommonUI の⼊⼒‧フォーカス‧プール機構と噛み合わなくなる可能性がある

16.

画⾯‧フォーカス管理 の事例をさらに学ぶ How to Handle Input and Navigation in Unreal Motion Graphics (UMG) | Unreal Fest Gold Coast 2024

17.

画⾯‧フォーカス ⼊⼒ Action Router データ 第2章 UIフレームワーク

18.

⼊⼒はどの順で流れて、消費されて、届くのか OS IInputDevice FSlateApplication Slate Widget Tree UCommonViewportClient UCommonUIActionRouterBase FUIActionBinding コントローラ‧⼊⼒デバイス毎の実装 InputPreProcessor チェーン、ここで⼊⼒判定可能 フォーカスされたSWidgetに配送、UMGが⼊⼒握り潰す場所 Viewport が⼊⼒を補⾜ UI ⼊⼒の中枢、最前⾯レイヤから順にアクションを照合 登録済みアクション実⾏、⼀致したら OnExecuteAction 実⾏

19.

Action Router が Active Widget への⼊⼒を管理する FCommon InputActionDataBase DataTable·Key/Icon/ Hold UCommon ActivatableWidget Activate + RegisterUIActionBinding FUIActionBinding 実⾏時マッピング UCommon UIActionRouterBase UCommon GameViewportClient ハブ ⼊⼒の⼊⼝ FUIInputConfig Menu / Game / Al ● Action Router は Local Player 単位で UI Action Binding を管理 ○ 押されているキーを今開いている画⾯に届ける ○ 画⾯の開き⽅に併せて届き先を⾃動で切り替える ● 内部では活性な Activatable Widget をTreeとして持つ ○ ZOrderとレイヤを⾒て 最前⾯のルートから⼊⼒を配る ○ Active な Activatable Widget Tree を基に⼊⼒先を決定する

20.

Input Config で Game/UI の⼊⼒モードを設定する FCommon InputActionDataBase FUIActionBinding DataTable·Key/Icon/ Hold UCommon ActivatableWidget 実⾏時マッピング UCommon UIActionRouterBase UCommon GameViewportClient ハブ ⼊⼒の⼊⼝ FUIInputConfig Activate + RegisterUIActionBinding Menu / Game / Al ECommonInputMode 意味 ユースケース Game ⼊⼒はゲームだけが受け取る HUD 表⽰中の通常プレイ Menu ⼊⼒は UI だけが受け取る フルスクリーンメニュー、モーダル All 両⽅が受け取る ミニマップ拡⼤、インゲームのマップ画⾯ 推奨:Input Config は 画⾯ごとに書かない、Layer / Activatable Widget 単位で Input Config を定義

21.

Common UIとEnhanced Input はどう分担するか? これまで(〜5.7) ● ● Common UI の UI アクションは UInputAction を直接バインドできる ⼀⽅で従来の DataTable ⽅式も現役 ただし「ゲーム⼊⼒」と「UI ⼊⼒」はビューポート層で排他的に切り分けられていた 現状(5.8〜) ● ● ● 未消費の UI ⼊⼒を Enhanced Input へ渡せるようになる ゲーム側の抑制は IMC の GameplayTag でフィルタ 5.8 では ⻑押し⼊⼒(ホールド)系の修正、1 アクションへの追加キーバインド等も追加 CommonUI.AlwaysWaitForHoldActionToComplete , CommonUI.bEnhancedInputDynamicallyResolveHoldKeysForInputAction

22.

Player Input Debugger で⼊⼒イベントを確認

23.

画⾯‧フォーカス ⼊⼒ データ MVVM (Model View ViewModel) 第2章 UIフレームワーク

24.

更新は「毎フレーム」から「変更時のみ」へ Property Binding ● ● ● コストが UI の複雑さに正⽐例 Blueprint 関数呼び出しのオーバーヘッドが乗る 「いつ更新されるか」がコード上に現れない Event Tick で更新 ● ● ● Widget が ゲームロジックを知ってしまう 値が変わらなくても更新を呼び Invalidate Widget を消すとロジックも消える MVVM: 変わったときだけ、変わったところだけ 更新 ● ● ViewModel のプロパティが変化 → その値に依存するバインドだけが実⾏される Widget 数が増えても、コストは「実際に変化した値の数」にしか⽐例しない

25.
[beta]
FieldNotifyが「変わった」を伝える
MVVM の下には、UMG から独⽴した FieldNotification モジュールがある

UCLASS()
class UPlayerStatusViewModel : public UMVVMViewModelBase
{
GENERATED_BODY ()
public:
// 値が変わったときだけ BroadcastFieldValueChanged を呼ぶマクロ
void SetHealth (float In) { UE_MVVM_SET_PROPERTY_VALUE (Health, In); }
// 派生値も「通知される関数」にできる(キャッシュ不要)
UFUNCTION (BlueprintPure, FieldNotify)
float GetHealthPercent () const { return MaxHealth > 0.f ? Health / MaxHealth : 0.f; }
private:
UPROPERTY (BlueprintReadWrite, FieldNotify, Setter, Getter, meta=(AllowPrivateAccess))
float Health = 0.f;
};

26.

変更通知から描画まで、何が起きるのか ゲーム側 FieldNotify UMVVMView Execution Mode Conversion View Slate 次のPaint ViewModel->SetHealth(80.f) FieldId でフィルタされた delegate のみ発⽕ このフィールド依存の「コンパイル済みバインディング」を引く 即時実⾏/フレーム末にまとめて実⾏ を判定 float → FText などの変換関数を適⽤ UTextBlock::SetText(...)など、Widget の通常の Setter Invalidate → 次の Paint で反映 ここまでが1回の更新

27.

更新タイミングは「回数」を減らすために選ぶ EExecutionMode 挙動 使いどころ Immediate ソース値が変わった 瞬間 に実⾏ 単発‧低頻度⼊⼒への即応が要る箇所 Delayed 変わったら フレーム末(描画前)に 1 回 実⾏ 1 フレームに複数回変わる値、連続ダメージ等 Tick 変化に関わらず 毎フレーム 実⾏ 原則使わない(Property Binding と同コスト) Auto 複数Fieldから起動時 Delayed/単⼀なら Immediate 既定値、基本これ ● ● ● ● 既定(Auto)から動かさない Tick を選ぶ理由はほぼ無い フレーム中に何度も変わる値は 明⽰的に Delayed 順序依存で値が壊れるときも Delayed で直ることがある

28.

Widget は View Model の取得⽅法を知らなくてよい BlueprintViewModelContextCreationType 概要 向いている場⾯ Manual コードから SetViewModel() で差し込む 親 Widget が⼦に配るリストのエントリ Create Instance Widget が⾃分⽤に new する その Widget 専⽤の状態(開閉状態など) Global View Model Collection 名前付きコレクションから取得 ⼿軽だが ⽂字列キー依存乱⽤注意 Property Path 既に持っている VM の⼦ VM を辿る VM の⼊れ⼦("VM-ception") Resolver UMVVMViewModelContextResolver が取得/⽣成 グローバルに 1 つだけ存在する VM 主に使う 2 つ ● ● Manual … ⽂脈から取れるとき(親→⼦) Resolver … グローバル VM を取るとき Resolver を勧める理由 ● 取得ロジックが C++ の 1 クラスに閉じる Widget 側は「この VM が欲しい」と宣⾔するだけ

29.

MVVMを使う場所‧使わない場所を決める 向いているもの ● 状態を持つ画⾯ 気をつけること ● まだ Beta(5.8 時点) ● 誰が所有するか、VMの寿命を決めて作成 メニュー、インベントリ、ストア、設定 ● 同じデータを複数箇所で表⽰する HUD ● テーマ / スタイルガイドの供給 ⾊‧マテリアル‧⽂⾔を VM から配ると、季節ごとの着せ 替えが VM 差し替えだけで済む ● ListView のエントリ UMVVMViewListViewBaseClassExtension が対応

30.

MVVM の事例をさらに学ぶ Powering Dynamic UI with Viewmodels | Unreal Fest Orlando 2025

31.

2 章まとめ:Widgetから「⼊⼒/画⾯状態/データ」を外した 問題 UE5の実装 Widget からの移譲 画⾯が多いと遷移とフォーカスが管理しきれない CommonUI どの画⾯が今アクティブかという状態 デバイスが増えると、⼊⼒が競合する Action Router 今どこが⼊⼒を受けるかという所有権 データが増え、更新が頻繁になると、改修が波及する MVVM 何を表⽰するかという判断 「便利な機能が増えた」のではなく「Widget 側で実装する必要があった仕組みに移した」 Widget が単純になるほど、画⾯を増やすコストは枚数に⽐例しなくなる

32.

第3章 UIアニメーション

33.

まずUIアニメーションの⼿法を選ぶ UI を動かす⽅法は 4 つ、まずどれを使うかの判断が性能の殆どを左右する ⼿法 利⽤ケース コスト 使いどころ Widget Animation タイムライン、複数プロパティの同期 中 画⾯のIn/Out、ボタン反応、演出全般、等 Material 連続的な視覚効果、複数Widgetの⾒た⽬同期 ⼩ 常時ループする装飾、ホバー光沢、等 Niagara 瞬間的に豪華な演出、視線誘導 ⾼ 視線誘導、重要ボタン、等 Tick / Timer 何でもできる ⾼ 原則使わない 推奨:⾒た⽬の "連続的な変化" は Material、"状態の遷移" は Widget Animation

34.

Material を主体としてアニメーションを作成 メリット ● ボタン選択状態等をマテリアル内で表現 デメリット ● Widget 階層が浅くなる ● ⾊‧⾓丸‧グラデーションをパラメータ化 命令数はプラットフォームによっては重い 特にモバイル、全画⾯を覆う UI マテリアルは要注意 ● ベースマテリアルの変更の影響範囲が巨⼤ ● アーティストにマテリアルグラフの理解を要求 テーマ変更が パラメータの差し替えだけ ● レイヤ数と Widget 数が減る ドローコールが減る ● 実⾏時にパラメータを動かせる アニメーションもマテリアルで完結 Epic では bitmap を極⼒使わず、UIの⾒た⽬をマテリアルで作成

35.

Widget Animation は Sequencer Widget Animation は、 Widget Blueprint の中に埋め込まれた MovieSceneSequence WBP_MainMenu (WidgetBlueprintGeneratedClass) └─ UWidgetAnimation "Anim_Intro" ├─ UMovieScene └─ FWidgetAnimationBinding ← UMovieSceneSequenceを継承 ← Track/Section/Key ← どのWidgetを動かすか 重要な性質 ● ● Track / Section / Key は MovieScene として保持 Widget Animation の評価基盤は Sequencer と共通

36.

UE5.6で「再⽣状態」の持ち⽅が変わった ● ● ● ⼀⾔でいうと「再⽣するアニメーション1本ごとに UObject を作る」ことを⽌めた Widget Animation の評価処理が Entity System ベースへ移⾏ 5.6 以降にアップデートしたら何もせずにこれを使⽤している

37.

UObjectPlayer から軽量な Animation State へ 〜5.5:UUMGSequencePlayer 5.6〜:FWidgetAnimationState UPROPERTY(Transient) TArray<TObjectPtr<UUMGSequencePlayer>> ActiveSequencePlayers; TArray<TObjectPtr<UUMGSequencePlayer>> StoppedSequencePlayers; TArray<TSharedRef<FWidgetAnimationState>> ActiveAnimations; ● ● ● ● 再⽣のたびに UObject を NewObject IMovieScenePlayer を持つ重いインタフェース GC の対象停⽌後も次の GC まで残る 再⽣/停⽌が多い UI で UObject 数が跳ね上がる ● ● ● ただの struct(TSharedFromThis) Objectなので GC対象外‧アロケーションが軽い 旧 API 互換がある

38.

それでも Legacy Player が残るケースがある // Engine/Source/Runtime/UMG/Public/Blueprint/UserWidget.h enum class ERequiresLegacyPlayer : uint8 { AutoDetect, Yes, No }; // AutoDetect (既定)… ネイティブ C++ の子がいる場合はレガシー Player を作る virtual ERequiresLegacyPlayer GetLegacyPlayerRequirement () const; ● Legacy Player が作られることがある ⼀部トラック(旧 Event Trackなど)はまだ ECS 化されておらず、 Legacy Player を要求する LogMovieScene: Initializing a legacy player for sequence ● 対処 Event Track → Event Repeater Track に置き換える、⼿動アップグレードが必要 5.6 以降に移⾏したら、まず「レガシー Player が作られている WBP」を洗い出す

39.

これで Widget Animation は軽くなった? 効果的なケース ● 同じ Widget を⼤量に並べて⼀⻫に再⽣ あまり効果がないケース ● ListView エントリ、インベントリグリッド、スコアボード ● 短いアニメーションを⾼頻度で再⽣‧停⽌ Player の⽣成コストがそもそも問題になっていない ● ダメージ表⽰、通知、ヒットマーカー ● 複数レイヤの Intro / Outro が重なる 常時 1〜2 本しか再⽣していない ボトルネックが Invalidation 側にある Padding や Size を動かしているケースでは全く効果なし ● ボトルネックが描画側にある メニューを開きながら HUD が引っ込む、など オーバードロー、テクスチャ、マテリアル命令数 再⽣数に⽐例してObject⽣成とGC負荷が追加 1フレーム分の評価コストには影響なし 軽くなったのは「再⽣を始めるコスト」で、速くならない場合は負荷が別のところにある

40.

Widget Animation のコストは「何を動かしたか」 真のコストは 「変更するものとそれに伴う更新」 変更する内容 発⽣する Invalidation 相対コスト Render Opacity / Color and Opacity Paint のみ 最⼩、再 Paint だけ Material Parameter ほとんどなし 最⼩、GPU 側 Render Transform Layout | RenderTransform ⼩、desired size が変わらず Slot Position / Size / Padding / Margin Layout ⼤、desired size が変わる Visibility Visibility (Layout を含む) ⼤ ⼦の追加‧削除 ChildOrder (Prepass + Layout) 最⼤ 推奨:動かすなら Render Transform と Opacity 例:スライドイン演出は Position ではなく Render Transform を動かして作成 Position の変更 は desired size を変えるので、兄弟 Widget (同じPanelの別 Widget ) まで再配置される

41.

Render Transform の変更も90%削減 https://github.com/EpicGames/UnrealEngine/commit/00f57f935256356bffc5ffc20af4b77e6bcf6eaf

42.

キャッシュはアニメーションと相性が良くない Global Invalidation Retainer Box ● 配下のWidgetをRender Target にキャッシュ ● 5.8 時点でも 既定は false ● 更新頻度を落とす(Nフレーム/回だけ再描画) ● ● 更新頻度低下によりアニメーションもカクつく 有効にすると Slate 全体が Fast Path で動き、 変化した Widget だけを再描画する ● ただし Volatile な Widget が多いと効果が薄い 推奨:①プロパティ⾒直し → ②Widget 数を減らす → ③Retainer → ④Invalidation ①②で解決しない問題は、③④でも解決しないことが多い

43.

最適化する前に評価と計測 パフォーマンスの評価で使⽤ ● ● ● ● stat Slate … Slate 全体の負荷 stat MovieSceneEval … Sequencer 評価全体のコスト stat MovieSceneECS … Sequencer Entity System 側の評価 Unreal Insights の Slate Insights Widget Animation の調整で使⽤ CVar 既定 効果 UMG.AnimationBudgetMs 0.0 1フレーム中の全Anim評価時間予算(ms) 超過分は次フレームへ繰り越し UMG.FlushAnimationsAtEndOfFrame 1 フレーム末に未処理の評価をフラッシュ/次フレームまで遅延 Widget.MaxAnimationLatentActions 100 1 フレームに実⾏する Latent アクションの上限

44.

UI Performance / Invalidation の事例をさらに学ぶ UI Performance & Invalidation Debugging | Unreal Fest Chicago 2026

45.

3 章まとめ:UIアニメーション の負荷は分けて考える ● UIアニメーションは Material で作成、状態遷移や時間ベースは Widget Animation ● Widget Animation は UE5.6 以降で軽量になった ○ しかし本当のコストは変更するプロパティと数に⼤きく依存 ○ プロパティが影響する Invalidation を理解して使⽤する

46.

第4章 UIのこれから

47.

UE5.8 で 追加された MCP プラグイン LLMをUnreal Editorに接続して、LLMからEditorを制御することができる

48.

Widget や View Model を操作が可能 「ViewModel を作って、Widget にバインドを張って、コンパイルする」をプロンプト⼊ ⼒から実⾏する、といったこともできる UMGToolSet(Experimental / Editor 専⽤) CreateWidgetBlueprint / AddWidget / RemoveWidget MoveWidget / RenameWidget / WrapWidgets SetNamedSlotContent / GetNamedSlots GetWidgets / GetWidgetDescription / GetWidgetTreeDepth ListWidgetClasses / GetWidgetClassInfo AddUIComponent / RemoveUIComponent / MoveUIComponent ReplaceWidgetWithTemplate / ReplaceWidgetWithChild BindToEventProperty / ToggleWidgetAsVariable CompileWidgetBlueprint MVVMToolset CreateViewModel AddViewModelProperty ListViewModels / ListWidgetViewModels AddViewModelToWidget CreateViewBinding / RemoveWidgetViewBinding ListWidgetViewBindings ListConversionFunctions

49.

Widget Blueprint (UEFN) ● ● MVVM経由でデータをやり取りすることが前提 コンポーネント化などによるWidgetとの分離

50.

まとめ Widget は「表⽰」だけに役割を分離し、機能と役割を適切に分離 表⽰データ View Model ⼊⼒の所有権 画⾯の状態 Action Router Common UI Widget Layout / Visual 時間/アニメーション Sequencer

51.

Thank

52.

依存する値はすべて Binding に⾒せる やりがちな実装 正しい実装:Conversion Function bIsPassCompleted にだけバインドした関数の中で、 必要な値を全部 ⼊⼒引数にする 残り 2 つの値を Blueprint の Get ノードで取る Binding: [VM.bIsPassCompleted] → Fn_UpdateStatus() Fn_UpdateStatus(bIsPassCompleted): Get VM.bIsPageCompleted ← バインドされていない Get VM.bIsLocked ← バインドされていない → bIsPassCompleted が変わったときしか呼ばれない → ページのロックが解除されても表⽰が更新されない Fortnite で実際にバグの原因になった、と講演で紹介されている例 Binding: [VM.bIsPassCompleted] [VM.bIsPageCompleted] → CF_GetActiveWidget() → SetActiveWidget [VM.bIsLocked] ● ● ● 3 つの どれが変わっても 呼ばれる 引数として渡るので 必ず最新値 関数は Pure + Const にする = 「Widget の状態を変えない」という契約 ● 値を返す(関数内で SetActiveWidget しない)

53.

ViewModelは「画⾯」ではなく「データの責務」で分ける 分割の指針 ● 1 画⾯ 1 VM にしない 関連するデータのまとまりで割る 例:SeasonPassCategoryVM/ ItemDetailsVM / SeasonPassSelectionVM ▲ ▲ │ │ [タイル一覧 ] [詳細パネル ] │ │ 選択を書く 選択を読む SeasonPassSelectionVM ● VM は Widget に紐づけない 複数の Widget が同じ VM を⾒る ● 機能固有の情報を汎⽤ VM に混ぜない ItemDetailsVM にシーズンパス固有データを⼊れない → ショップでも使える ● 選択状態も VM に置く → Widget 同⼠が直接通信しなくなる(下図) Widget 間の直接参照 = ゼロ 到達点 「バトルパス画⾯」「OG パス画⾯」「⾳楽パス画⾯」 「LEGO パス画⾯」はすべて同じ 1 つの Widget 違いは 流し込む VM のデータだけ → バグ修正‧演出改善が 1 箇所で全画⾯に効く

54.

MVVM へ処理をすべて移⾏するべきか? 共有される状態は View Model へ、⾒た⽬だけの状態は Widget に残す 状況 Direct / Event MVVM ⼀時的なWidget内部状態 〇 △ ゲームデータ表⽰ △ 〇 複数Widgetで共有 ✕ 〇 頻繁に更新 △ 〇 単発の簡単なUI 〇 △

55.

Common UI を使うなら Common UI で管理を統⼀ よくある認識 ● UUserWidget/UCommonActivatableWidget 混在 正攻法 ● Widget Rootは必ず UCommonActivatableWidget ● 出し⼊れは 必ず Layer への push / pop ActionRouter の持つ状態と⾷い違う ● フォーカスは GetDesiredFocusTarget() で表現 AddToViewport()で Activatable Widget を直接出す ● HUDレイヤは bSupportsActivationFocus=false ● UCommonGameViewportClient を設定で必ず指定 混ざった瞬間フォーカス管理が複雑化 ● ● SetKeyboardFocus() を⾃分で呼ぶ Stack を経由しないので⾮活性化されない ● HUD で bSupportsActivationFocus=true のまま メニューを開いてもフォーカスが HUD に吸われる これを忘れると⼊⼒が CommonUI に届かない