FScopedMovementUpdate についてのメモ
移動コンポーネントを自作しているときに FScopedMovementUpdate について調べたので、その時のメモ。
概要
FScopedMovementUpdate は USceneComponent の Transform 変更に伴う、バウンディングボックスや子コンポーネントの Transform 更新処理、Overlap 情報更新や Overlap 関連イベント発火、Hit イベントの発火などを遅延させるために使用される構造体。
主に移動処理におけるパフォーマンス最適化や、移動位置のロールバックに利用される(はず)。
Definition/Implementation
Source/Runtime/Engine/Public/Engine/ScopedMovementUpdate.h Source/Runtime/Engine/Private/Engine/ScopedMovementUpdate.cpp
使用方法
FScopedMovementUpdate 適用対象の USceneComponent と、遅延を行うかどうかのフラグ( EScopedUpdate )、Overlap イベントを行うかのフラグ( bool )をコンストラクタに指定して使用する。
| 引数 | 型 | デフォルト値 | 説明 |
|---|---|---|---|
| Component | USceneComponent* | - | FScopedMovementUpdate を適用する USceneComponent |
| ScopeBehavior | EScopedUpdate::Type | EScopedUpdate::DeferredUpdates | 遅延を行うかどうかのフラグ値。デフォルト設定では遅延を行う。 遅延ではなく即時実行したい場合は EScoedUpdate::ImmediateUpdates を設定する |
| bRequireOverlapsEventFlagToQueueOverlaps | bool | true | Overlap イベントを行うかのフラグ。デフォルトでは Overlap イベントを行う |
例えば、遅延を行う場合は以下のように定義する。
この場合、処理が ScopedMovementUpdate を定義したスコープからでたとき UpdatedComponent に対して遅延していた処理が行われる。
// USceneComponent* UpdatedComponent; { FScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, EScopedUpdate::DeferredUpdates); // UpdatedComponent の Transform を更新する処理 } // ScopedMovementUpdate のスコープから出たとき、遅延していた処理が行われる
同じ USceneComponent に対し、複数の FScopedMovementUpdate を適用することもできる。この場合、一番最初に適用した FScopedMovementUpdate のスコープを抜けるときに遅延していた処理が実行される。
// USceneComponent* UpdatedComponent; { FScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, EScopedUpdate::DeferredUpdates); // UpdatedComponent の Transform を更新する処理 { FScopedMovementUpdate InnerScopedMovementUpdate(UpdatedComponent, EScopedUpdate::DeferredUpdates); // UpdatedComponent の Transform を更新する処理 } // InnerScopedMovementUpdate のスコープから出ても遅延処理は実行されない } // ScopedMovementUpdate のスコープから出たとき、遅延していた処理が行われる
同じ USceneComponent に対し、複数の FScopedMovementUpdate を適用するときの注意
定義した FScopedMovementUpdate の外側の FScopedMovementUpdate が遅延設定の場合、即時実行( EScopedUpdate::ImmediateUpdates )を指定しても遅延設定となる。
// USceneComponent* UpdatedComponent; { FScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, EScopedUpdate::DeferredUpdates); // UpdatedComponent の Transform を更新する処理 { // 即時実行で設定しているが、外側の FScopedMovementUpdate が遅延実行設定なので // この FScopedMovementUpdate も遅延実行となる FScopedMovementUpdate InnerScopedMovementUpdate(UpdatedComponent, EScopedUpdate::ImmediateUpdates); // UpdatedComponent の Transform を更新する処理 (Overlap イベントなど即時実行されない) } } // ScopedMovementUpdate のスコープから出たとき、遅延していた処理が行われる
移動処理のロールバック
FScopedMovementUpdate::RevertMove() を使用することで、それまでに更新していた USceneComponent の Transform を FScopedMovementUpdate 定義時までロールバックできる。
// USceneComponent* UpdatedComponent; { FScopedMovementUpdate ScopedMovementUpdate(UpdatedComponent, EScopedUpdate::DeferredUpdates); // UpdatedComponent の Transform を更新する処理 if (ShouldRevertMove()) { // 移動をロールバックしたい条件 ScopedMovementUpdate.RevertMove(); // UpdatedComponent の Transform を ScopedMovementUpdate 定義時の値に戻す } }
FScopedMovementUpdate は、コンストラクタの引数で渡した USceneComonent の Transform や Relative Location/Rotation/Scale を内部に保存しているので、このメソッドを呼ぶことでロールバックできる。
動作の仕組みメモ
FScopedMovementUpdate のコンストラクタで EScopedUpdate::DeferredUpdates を設定したときの動作についてのメモ。
FScopedMovementUpdate 定義時
引数に設定した EScopedUpdate::Type が EScopedUpdate::DeferredUpdates のとき USceneComponent::BeginScopedMovementUpdate(FScopedMovementUpdate&) を呼び出す。
これにより USceneComponent は自身に FSopedMovementUpdate が適用されたことを知る。内部的には USceneComponent のメンバ変数 ScopedMovementStack に引数の FScopedMovementUpdate を push する。
USceneComponent の Transform 更新時
USceneComponent の Transform や Relative Location/Rotation/Scale 更新処理が呼ばれるとき、コンポーネント側の責任でそれら更新を行う。
自身に FScopedMovementUpdate が適用されているとき、更新処理により発生するバウンディングボックスや子コンポーネントの Transform 更新処理、Overlap 更新処理、Hit イベント発火処理などが遅延されるように処理が実装されている。
基本的には FScopedMovementUpdate 適用時は遅延時に処理する FOverlapInfo や FHitResult を FScopedMovementUpdate に保存するような処理を行っている。
例えば以下参照。
UsceneComponent::PropagateTransformUpdate(bool, EUpdateTransformFlags, ETeleportType)USceneComponent::UpdateOverlaps(const TOverlapArrayView*, bool, const TOverlapArrayView*)USceneComponent::SetRelativeScale3D(FVector)UPrimitiveComponent::MoveComponentImpl(const FVector&, const FQuat&, bool, FHitResult*, EMoveComponentFlags, ETeleportType)
スコープを抜けたとき
FScopedMovementUpdate を定義したスコープを抜けることにより FScopedMovementUpdate のデストラクタにて USceneComponent::EndScopedMovementUpdate(FScopedMovementUpdate&) が呼び出される。
USceneComponent::EndScopedMovementUpdate(FScopedMovementUpdate&) では USceneComponent に適用された FScopedMovementUpdate が他にいなければ、遅延処理を実行する。
他にいた場合は ScopedMovementStack の Top の要素に引数で渡された FScopedMovementUpdate の遅延した Overlap や Hit を渡す。内部的には USceneComponent は Top の要素の FScopedMovementUpdate::OnInnerScopeComplete(FScopedMovementUpdate*) を呼ぶ。
遅延処理
遅延処理では以下のような処理を行う。
FScopedMovementUpdate定義時のUSceneComponentの初期 Transform と、現在の Transform が変わっている場合 Transform 更新時の処理を行う。 内部的にはUSceneComponent::PropagateTransformUpdate(bool, EUpdateTransformFlags, ETeleportType)を呼び出し、バウンディングボックス更新や Transform 変更に伴う子コンポーネントの Transform 更新を行う。FScopedMovementUpdateに登録していた Overlap を利用し Overlap 情報更新処理を行う。 内部的にはUSceneComponent::UpdateOverlaps(const TOverlapArrayView*, bool, const TOverlapArrayView*)を呼び出し Overlap 情報の更新や Overlap 関連イベントを発火するFScopedMovementUpdateに登録していた Hit イベントをすべて発火する。