UObject と FTickableGameObject を多重継承したクラスにおいて、
GC実行のタイミングでクラッシュが発生しました。
FTickableGameObject のドキュメントのNoteを参考に、
以下の対応を行うことでクラッシュは解消されています。
UTickableObject::UTickableObject()
: FTickableGameObject(ETickableTickType::Never) // ① 初期化リストで Tick 登録を Never に設定
{
}
void UTickableObject::Initialize()
{
// ② ゲームスレッド側から呼び出し、Tick を有効化
SetTickableTickType(ETickableTickType::Conditional);
}
void UTickableObject::BeginDestroy()
{
// ③ 破棄前に Tick を無効化
SetTickableTickType(ETickableTickType::Never);
Super::BeginDestroy();
}
この対応方針で問題ないか確認したいのですが、現状 Initialize() を都度呼び出す手間が発生しているため、
より簡潔な改善案があればご教示ください。
また、同様のパターンを複数のクラスで使用しており対応要否を精査したいため、
問題が発生する条件や対応が不要なケースについても合わせて教えていただけますか。
[Attachment Removed]
お世話になっております。
ご対応いただいた方針で問題ございません。Tickable.hのFTickableGameObjectクラスコメントに記載された推奨手順そのもので、エンジン内部でもUTickableWorldSubsystemが同じ構成を採用しております。
Initialize()の簡略化についてですが、全生成経路で「初期化完了後」かつ「ゲームスレッド上」を保証する自動フックは存在しないため、明示的なコードが前提となります。Worldに紐づくオブジェクトであればUTickableWorldSubsystemの継承が最も簡潔です。そうでない場合は共通の基底クラスを用意し、PostInitProperties()とPostLoad()から有効化する形にまとめてください。
発生条件の主因は、BeginDestroy()と実際のデストラクタの間のウィンドウです。登録解除はデストラクタで行われますが、インクリメンタルGCにより数フレーム後になることがあり、その間も破棄処理中のオブジェクトがTickされ続けます。ほかに、ゲームスレッド外での構築、GCワーカースレッドでの破棄、CDOの登録が要因となります。
精査の基準としては、UObject派生かつFTickableGameObjectを継承しているクラスはすべて対応が必要とお考えください。再現していないものもGCタイミング依存の潜在不具合となります。対応不要なのは、UObject派生でない場合、UTickableWorldSubsystemなど対応済みのエンジンクラスを継承している場合、Tickを有効化していない場合です。
引き続きよろしくお願いいたします。
[Attachment Removed]
お世話になっております。
ご回答いただきありがとうございます。
方針のご確認と詳細な発生条件・精査基準をご共有いただき、大変参考になりました。
いただいた内容をもとに、対象クラスの洗い出しと対応を進めてまいります。
ご対応いただきありがとうございました。
[Attachment Removed]