スレートは、「即時モード」の UI フレームワークです。つまり、フレーム毎に UI 全体を再描画します。これは、グラフィックスやアニメーションが豊富な動的インターフェースでは素晴らしいことです。しかし、UI で何も変更する必要がない場合は、プロセッサを無駄に使うことになり、これは多くの場合ランチャーやエディタで起こりがちです。
この機能では新しい「アクティブ タイマー」システムをスレートに導入することで、UI を更新する必要がない場合に「スリープ」状態にすることができます。
この変更によって何か影響があるでしょうか?
エディタ や ランチャー の UI の作業をしている場合は影響があります。 リアルタイム ビューポートを備えたゲームの UI の作業をしている場合は影響はありません。
スレートはどんなときにスリープ状態になるのでしょうか?
あるフレームに対して以下の 2 つが両方とも該当する場合にスリープ状態になります。
- ユーザー アクションが何もない。
- 実行する必要があるアクティブ タイマーがない。
どういったものが「ユーザー アクション」になるのでしょう?
マウスの移動、クリック、キーの押下が該当します。
以下の図はスレート アプリケーションがフレーム毎にどのようにティックするかを示しています。
スリープ状態にすることは効果があるのでしょうか?
はい実際に効果があります。以下はその結果を示すグラフです。
「アクティブ タイマー」とはどういったものでしょう?
アクティブ タイマーは、ウィジェットによって明示的に登録されるデリゲート関数で、ユーザー アクションがなくても、実行時にスレートのティック / ペイント パスを生じさせます (従って、タイマーは「アクティブ」な性質を持っています)。アクティブ タイマーは、登録削除されるまで、実行周期で定められた頻度で無制限に実行を続けます。
これは、これまでの Tick() にどのような影響を及ぼすでしょうか?
今でも Tick() は存在しますが、「受動的」ティックと考えてください。Tick() は、これまでどおりスレートによって呼び出されますが、これはスレートが起動状態の場合に限ります。現時点で、PassiveTick() と名前変更し、非推奨 Tick() にするかどうか、またその方法をまだ検討中です。
アクティブ タイマーはどのように実装できますか?
登録には以下の 3 つのステップがあります。
- 関数を以下のシグネチャで定義します。"EActiveTimerReturnType Foo(double InCurrentTime, float InDeltaTime)"
- それを FWidgetActiveTimerDelegate にバインドします。
- デリゲートとタイマー実行の間の時間周期を (0 からフレーム毎に呼び出し) SWidget::RegisterActiveTimer() に渡します。
登録解除には以下の 3 つの方法があります。
- デリゲートから、 Return EActiveTimerReturnType::Stop を戻します。
- SWidget::RegisterActiveTimer() によって戻された FActiveTimerHandle を SWidget::UnRegisterActiveTick() に渡します。
- アクティブ タイマーが登録されているウィジェットを破棄します。
登録の重複に注意!
現在、アクティブ タイマーはスレートに関しては、オール オア ナッシングの実装になっています。 つまり、ひとつのアクティブ タイマーを実行する必要があれば、スレートのすべてがティックされることになります。さらに、ひとつのウィジェットに同時に登録できるアクティブ タイマーの数に制限はありません。これは非常に便利ですが、重複登録の可能性もでてきます。重複を防ぐには、以下のいずれかの方法で登録ステータスを追跡します。
-
ウィジェットにフラグを付けてアクティブ タイマーが登録されているかを追跡します。
- 例えば、“bIsActiveTimerRegistered“ を検索します。
-
RegisterActiveTimer() によって戻される FActiveTimerHandle にウイーク ポインターを格納し、無効な場合にのみ登録するようにします。
- “TWeakPtr<FActiveTimerHandle> ActiveTimerHandle“ を検索します。
- メモリを節約するために、アクティブ タイマーを明示的に登録解除する必要がある場合に限り、上記に配慮してください。
一般的なユースケース
-
何らかのアクションをトリガーする
- 以前は、フラグを格納し、各ティック中にそれが true であるかをチェックしていました。
- 現在は、周期 0 で常に EActiveTimerReturnType::Stop を戻すアクティブ タイマーを登録します。
-
FCurveSequence によって制御されない何らかのアニメーションや補間を行います (制御されるものは、以下を参照)。
-
例:慣性スクロール - SScrollBox を参照。
- 慣性スクロールが開始すると、周期 0 のアクティブ タイマーを登録し、フレーム毎にスクロールを更新します。
- 目的位置に到達するまで、EActiveTimerReturnType::Continue を戻します。
- 目的位置に到達すると、EActiveTimerReturnType::Stop を戻して登録解除します。
-
例:慣性スクロール - SScrollBox を参照。
-
遅延後にアクションを行います。
-
例:別のサブメニューを開き、タブをドッキングします。
- 正の非ゼロの遅延で登録します。
-
アクティブ タイマーの周期は一度登録すると変更できません。
- 実行前に遅延をリセットするには、アクティブ タイマーを明示的に登録解除し、再登録しなければなりません。
-
例:別のサブメニューを開き、タブをドッキングします。
-
周期的かつ無制限にアクションを行います。
- 正の非ゼロの周期で登録し、EActiveTimerReturnType::Continue を戻し続けます。
FCurveSequence API を更新しました。
FCurveSequence の API は、ウィジェットがアニメートされているときにアクティブなティックを登録するプロセスを単純化するように更新されました。以下は、いくつかの変更点です。
-
シーケンスの再生では、シーケンスがアニメートするウィジェットへの参照が必要になりました。
- シーケンスの再生中、渡されたウィジェットの代わりに、空のアクティブ ティックが自動的に登録されます。
-
シーケンスをループすべきかどうかはシーケンス再生時に指定するようになりました。
- こうすることでアクティブ ティックを無制限に登録するため、注意してください!
- ループするかどうかに関わらず、Pause()、JumpToStart()、または JumpToEnd() を呼び出す場合に、アクティブなティックは登録解除されます。
すべて機能しているでしょうか?
はい! そう思います。このアクティブ ティック システムは、メインとすべてのエディタのウィジェット(自分が見つけたのは、最高 90 ) に存在し、それを必要としていたものは適宜更新されています。なんらかの予期しない影響が生じるかもしれませんが、問題に対処するための 2 つの cvar (コンソール変数) がエディタにあります。
- Slate.AllowSlateToSleep は、スレートがスリープ状態に入ることができるかどうかを制御します。これはデフォルトで有効になっています。
- Slate.SleepBufferPostInput は、最後のユーザー アクション後にスレートが起動状態を続ける時間の長さを指定します。これはデフォルトでゼロになっていて、現在、デバッグ目的でのみ使用され、システムの機能としては残らないでしょう。
スレートがスリープ状態であることはどのようにわかるのでしょうか?
目標は、スリープ状態にできるスレートをスリープ状態に決してならないスレートと見分けがつかないようにすることであるため、識別は難しいでしょう。
現在、これをモニターする一番良い方法は、エディタのフレーム レートを表示することです (以下の順序で選択します。Editor Settings (エディタ設定) ->Miscellaneous (その他) ->Show Frame Rate and Memory (フレームレートとメモリを表示) )フリーズすると、スレートはスリープ状態になろうとします。
ではどうしたら良いのでしょうか?
これまでに相当複雑なウィジェットを作成したか、現在新しいウィジェットを作成中の場合、スリープを有効にした状態で、永続的なリアルタイム ビューポートをひとつも開いていない状態でテストすることが非常に重要になります (入力後のバッファを 0 に保ちます) 。
うまく動かないウィジェットを見つけたり、ウィジェットの更新に関するご質問がある場合は、フォーラム までお問い合わせください。