お世話になっております。
特定条件下においてRetainerBoxの描画が崩れる問題に対しての解消方法をご相談させてください。
Viewport領域にPlayerScreen領域をマスクしたカットアウト画像を描画
といった処理の実装を行っています。
描画負荷が高いため「Viewportサイズに変更があった場合のみ再描画」としているのですが
表示崩れにより期待した結果になりません。
UGameViewportClient* gameViewportClient = GetGameViewportClient();
if (!gameViewportClient || !gameViewportClient->Viewport)
{
MyUserWidget = nullptr;
return;
}
//
gameViewportClient->Viewport->ViewportResizedEvent.AddWeakLambda(this, [this](class FViewport* InViewPort, uint32 InVal)
{
// ビューポートサイズ変更時に再描画要求
if (MyUserWidget)
{
MyUserWidget->OnViewportSizeChanged();
}
});
再描画処理をViewportのサイズ変更時ではなくTickで常時行うと期待した結果になるため
実行タイミングの問題に見受けられます。
(「/Game/Widget/WBP_UserWidget.uaaset」のイベントグラフを編集すると確認可能です)
こちら期待した挙動を得るためのワークアラウンドなどありますでしょうか。
備考:
UE5.6、UE5.7、UE5.8で再現したのですがバージョンにより崩れ方が若干異なりました。
以上、よろしくお願いいたします。
[Attachment Removed]
お世話になっております。
本事象は ViewportResizedEvent の発火タイミングだけに再描画を依存していることが要因です。ViewportResizedEvent はビューポートのリサイズ検知には有効ですが、RetainerBox 側の描画に必要な状態(UIジオメトリ確定やマスク計算条件)が同フレーム内で揃わない場合があり、表示崩れが発生します。また、マスク対象が PlayerScreen ベースの場合、再描画が必要になる条件はリサイズ以外(カメラ, FOV,ViewportScale 等)にも存在するため、リサイズ時のみの更新では不足するケースがあります。Tick で毎フレーム再描画すると崩れないのはこの不足分を毎フレーム吸収できるためです。
対応方針として、ViewportResizedEvent で直接 RequestRender() は行わずにここでは再描画予約フラグのみ設定し、その後次フレームの Tick(または SetTimerForNextTick)でフラグを消費して OnViewportSizeChanged() → RequestRender() を1回実行するようにして頂くことが推奨される対処方法となります。
[Attachment Removed]
ご返信、ご検証結果を共有頂きありがとうございます。
前回お伝えした「次フレームの Tick(または SetTimerForNextTick)で再描画」という表現はやや曖昧で混乱を招いてしまい失礼いたしました。
ご指摘のとおり、単なる「次 Tick」では不十分で、次エンジンフレームまで待ってから再描画する必要があります。SetTimerForNextTick も、設定によっては同一エンジンフレーム内で実行されることがあります。改めて、ViewportResizedEvent では RequestRender() を直接呼ばずフラグのみ立て、次エンジンフレームで OnViewportSizeChanged() → RequestRender() を1回実行する形がよろしいかと思われます。
PlayerScreen マスクについて、リサイズ以外(カメラ・FOV・ViewportScale 等)でも再描画が必要という点について、残念ながら一括で通知する専用のデリゲートというものは用意されておりません。Viewport サイズは ViewportResizedEvent(再描画は次エンジンフレームへ遅延)、DPI やディスプレイ変更は FSlateApplication の OnWindowDPIScaleChanged や OnDisplayMetricsChanged、ViewTarget 切替は FGameDelegates::ViewTargetChangedDelegate などが利用できるかと思われます。カメラの回転や FOV、PlayerScreen 上の矩形の連続変化には専用デリゲートがないため、毎フレームまたは Tick で状態を比較し、変化があったときだけ RequestRender() する判定が現実的な方法になってくるかと思われます。
[Attachment Removed]
ご確認ありがとうございます。
ご教示いただいた「ViewportResizedEvent の 次Tickで再描画を行う」を試行したのですが
期待した結果を得られなかったため「次Tickではなくさらに次Tick」で再描画を行うと期待した結果になりました。
類似した処理を比較した結果
■Delay Until Next Frame → OK
■Set Timer for Next Tick by Event
・TimerManager.GuaranteeEngineTickDelay 0 → NG
・TimerManager.GuaranteeEngineTickDelay 1 → OK
■Delay Until Next Tick
・LatentActions.GuaranteeNextTickDelay 0 → NG
・LatentActions.GuaranteeNextTickDelay 1 → OK
となったので次エンジンフレームでの再描画が必要に見受けられますが
理解としてあっておりますでしょうか。
また
- PlayerScreen ベースの場合、再描画が必要になる条件はリサイズ以外(カメラ, FOV,ViewportScale 等)にも存在するため、リサイズ時のみの更新では不足するケースがあります。
とご指摘いただいた部分についてですが
こちらの条件を検知できるデリゲートなどはありますでしょうか。
以上、よろしくお願いいたします。
[Attachment Removed]
ご回答ありがとうございます、諸々承知いたしました。
いただいた情報を元に実装方法を検討したいと思います。
以上、よろしくお願いいたします。
[Attachment Removed]