パッケージでの最初のTickがスキップされる

お世話になります、不具合について伺います

パッケージでの最初のTickがスキップされます

パッケージについては触れられておりませんが、こちらの問題になります

(スタンドアロンについては触れているのでそれが同じ意図かもしれません)

[Content removed]

問題の実例が望まれていたようなので、

改めて問題の指摘とこちらでの修正案とともにあげておきます

アドバイスや修正プランがあればお聞かせいただけると助かります

TickTaskManager.cppでのFInternalDataのTickVisitedGFrameCounterが0で初期化されています

これと比較されるゲームのフレームカウンタGFrameCounterも0から開始されます

5.5では、

void FTickFunction::QueueTickFunction(FTickTaskSequencer& TTS, const struct FTickContext& TickContext)

の冒頭でこの二つが比較されて異なる場合はTickに進むようになっています

エディタからの起動時は、レベル起動前からGFrameCounterが動いているため、

これが32ビット一周して0になる瞬間でもなければ0で比較されませんが(実際問題なかろう)、

パッケージでは最初に起動したレベルの最初のTickで必ず0になっており、

FTickFunctionを使う全てのアクター・コンポーネントのTickが初回にスキップされています

FTickFunctionのコンストラクタを次のように必ず作成時より過去のカウントにすることで、

例えカウンタが一周するタイミングでも、パッケージの初回でも確実にTickが実行されます

​FTickFunction::FInternalData::FInternalData()

: bRegistered(false)

, bWasInterval(false)

, TaskState(ETickTaskState::NotQueued)

, ActualStartTickGroup(TG_PrePhysics)

, ActualEndTickGroup(TG_PrePhysics)

#if 1 // Fixed

, TickVisitedGFrameCounter((uint32)(GFrameCounter-1))

, TickQueuedGFrameCounter((uint32)(GFrameCounter-1))

#else

, TickVisitedGFrameCounter(0)

, TickQueuedGFrameCounter(0)

#endif

, TaskPointer(nullptr)

, Next(nullptr)

, RelativeTickCooldown(0.f)

, LastTickGameTimeSeconds(-1.f)

, TickTaskLevel(nullptr)

{

}

弊社では5.6に移行するまでまだしばらくあり、5.6での再現は確認できていません

もしかしたら初期化から最初のTickに至る経路の改善で発生しないのかもしれません

5.6では、このFTickFunction::QueueTickFunction冒頭が少し変更されており、

カウンタの比較がifによる起動条件からcheckによるアサートになっていました

もし実行経路が従来と同じ場合、checkが無効なパッケージでは問題が発生しないでしょうが、

根本的な問題解決はされてないことになります

弊社では5.5にて上記のような暫定の修正でしのぎ、

5.6移行後にcheckが問題になる場合も引き続き同様の対策を入れようと思います

TickQueuedGFrameCounterもこのようにすべきかははっきりわかっていませんが、

同様のカウンタなので念のため同じ修正を入れています

これらの改変による悪影響が考えられるかアドバイスあればお願いします

この問題による問題点としては次のようなケースが確認されています

GameplayCameraを使ってワールドパーティションのストリーミングソースを更新しているとき、

カメラリグの指定(ActivateCameraRigs)をするのにGameplayCamera(をもつPlayerCharacter)と、

PlayerControllerが必要ですが、これらの初期化順序はエディタとパッケージで異なるので、

簡易な安全策としてGameplayCameraコンポーネントを持つPlayerCharacterのPossessedで、

DelayUntilNextTickによってActivateCameraForPlayreController,ActivatePersistentGlobalCameraRig,ActivatePersistentBaseCameraRigなど行います

エディタでは

ワールド初期化直後LevelTick.cppのUWorld::Tickに入ると、

DelayされたActivateCameraRigsが、TG_PrePhysicsで実行されて、

カメラのコンテキストが作成され(void UControllerGameplayCameraEvaluationComponent::EnsureEvaluationContext())、

次いでUGameplayCameraComponent::TickComponentで、そのコンテキストにコンポーネント(PC)の位置が渡されます

主だったTickの後に実行されるストリーミングの更新(InternalUpdateStreamingState())において、

そのカメラコンテキストの位置(カメラの設定でオフセットが乗ったPC付近)がストリーミングソースとして参照されるので、

PCがいるセルがそのまま使われます

パッケージでは

ワールド初期化直後、UWorld::Tickにて各Tickが上記の問題でスキップされてしまうため、

DelayされたActivateCameraRigsまわりの処理もTG_PrePhysicsでは実行されません

コンテキストが未作成なので呼ばれても意味はないですが、

UGameplayCameraComponent::TickComponentもスキップされています

そのあと同フレームの(UWorld::Tickの)CurrentLatentActionManager.ProcessLatentActions(nullptr, DeltaSeconds);

でDelayされたActivateCameraRigsが実行されてカメラコンテキストが作られます

そして同フレームのストリーミングの更新が行われるため、

カメラコンテキストが原点付近で処理されます

(TickComponentでコンテキストにコンポーネントの位置が与えられてないので、原点にキャラがいるかのようなカメラ配置になる)

これによってPCの開始位置がワールドパーティションのレベルの原点付近で無い場合、

開始と同時にPC(カメラ)が本来いる位置のセルがストリームアウトしてしまいます

そのためすぐにPC周辺のアクターのEndPlayが呼ばれます

しかし次のフレームには正常にTickが動作するためカメラコンテキストが指す位置は本来のPC付近になるため、

同じセルがストリームインします

このように本来のBeginPlayのあと、一度すぐにEndPlayされていたアクターが、またBeginPlayされています

これはGFrameCounterが0であることからくる問題のため、

同じレベルであってもOpenLevelで入り直しても発生しません

(逆に言えば同じパッケージ内であってもそのレベルの初回と2回目で挙動が異なります)

また実際の製品では起動直後には、警告やクレジットを行うシーケンス、タイトル画面などが来るため、

この報告例は実害を及ぼすことはないとは思っています

しかし開発時や検証時に原因不明な症状として問題になることは考えられ、

実際弊社でも原因調査に時間を取られたように、

コミュニティ全体としての損失は大きいように思われます

とくにもし5.6でも起きうる問題でしたら、根本的な修正を検討いただければと思います

以上、よろしくお願いします

お世話になっております。

このたびは詳細な問題報告をありがとうございます。また、原因不明の症状で調査にかなりお時間を使わせてしまい、大変申し訳ありませんでした。

まず、お手元で適用された修正方法についてですが、TickVisitedGFrameCounterとGFrameCounterのズレからTickを実行すべきかどうかを判断する基本ロジックには変更の予定がないことから、この修正は当面のあいだ意図通り機能すると思われます。リスクの低い、順当な変更と思われますが、念のため社内の他の開発メンバーにも確認を取っておりますので、返信が得られましたら後ほど回答を追記させていただきます。

また、ご懸念の通り、UE5.6でも残念ながらこの問題は明示的に解決されているとはいえません。実害の実例も頂戴しましたので、これらの情報を開発チームにフィードバックし、将来的な対応について協議を始めております。こちらも対応方針が決まり次第、報告させていただきます。

このたびは、ご不便をお詫びすると同時に、今回の詳細な技術調査とフィードバックに深く感謝申し上げます。

引き続き、よろしくお願いいたします。

お世話になっております。

エンジン開発チーム側でも、ご提示いただいたローカルパッチについて確認を行ったところ、特に大きな問題は見られないとの見解でした。また、将来の修正に向けて、いただいた情報をもとに社内でバグ報告を行いました。

Public Issueとしての承認された後は、下記のURLより対応状況をご確認いただけます。

なお、開発側としても本件は基本的に修正すべき不具合であるとの認識を共有しておりますが、担当チームが現在多くの優先対応事項を抱えている関係上、本件の対応にはある程度お時間をいただく可能性が高そうです。もし可能であれば、今回のパッチをもとにGitHubにてプルリクエストを作成していただけますと、社内の対応がよりスムーズになる場合がある、とのことでした。

(もちろんプルリクエストの作成は必須ではございません。あくまで社内でこのようなやり取りがあったことを参考までにお伝えさせていただきます)

以上、よろしくお願いいたします。​

お世話になります。バグ報告ありがとうございます。

こちらでは報告した手法で回避しておきます。

GitHubへのプルリクエストにつきましては、弊社で利用したことがなく、検討させていただきます。

本件はクローズいただいて大丈夫です。

ご確認ありがとうございます。

それでは本件は回答済みとしてCloseさせていただきます。

以上、よろしくお願いいたします。