#1. 導入
UE4のオブジェクトサイクルは、必要な時にオブジェクトを作成し、必要が無くなったタイミングでガーベージコレクションシステムによって削除されます。ガーベージコレクションシステムは自動的にオブジェクト管理をする上で便利ですが、時にはオブジェクトを意図的に削除したくないようなケースもあります。そこで、本記事ではオブジェクトを常駐するための方法について示します。
オブジェクトを常駐させることは、ロード時間の短縮やロードによるヒッチの回避に役立ちます。必要なリソースを先にロードしたり、再利用することによって、都度必要なロードの機会を削減します。オブジェクトを常駐するには”DisregardForGC”を使用します。
#2. 詳細
“DisregardForGC”は、指定したobjectをgarbage collectの対象から除外することによってオブジェクトを永続的に生かす仕組みです。以下のリンク先にあるスライドで詳細に説明しています。Resources のページの [UE4におけるLoadingとGCのProfilingと最適化手法]からスライドをダウンロードすることもできます。
https://www.unrealengine.com/ja/resources?lang=ja
以下のスライドと同じです。
https://www.slideshare.net/EpicGamesJapan/ue4loadinggcprofiling
UE4のオブジェクトは、全オブジェクトを格納する配列(GUObjectArray)で一元管理されています。DisregardForGCは、この配列に格納するオブジェクトの一部を非GC領域に格納することによってGCの対象から除外します。
DisregardForGCの対象となるオブジェクトは、エンジンの起動開始直後からFUObjectArray::CloseDisregardForGC()
が呼ばれるまでの期間にロードされたオブジェクトです。このタイミングは、エンジンの初期化フェーズであるFEngineLoop::PreInit
で、エンジンが必要な全てのオブジェクトのロードが行われますが、プロジェクトで使用するオブジェクトもこのタイミングでロードすることによって、常駐オブジェクトの対象とすることが可能です。
#3. 前提
DisregardForGCを有効にします。 (gc.MaxObjectsNotConsideredByGCを>0の値に設定).
#4. 使用例
実際にDisregardForGCの対象とすることが可能なロードの定義方法についていくつか紹介します。
###例1
以下はシューターゲームにおける例です。DefaultGame.ini
で指定したファイルをPostInitProperties()
のタイミングでロードすることも有効です。
以下の場合、T_ImageAのTexture2Dは永続オブジェクトとして破棄されることはありません。
virtual void PostInitProperties() override;
UPROPERTY(config)
TArray<FString> StartupPackages;
void UShooterGameInstance::PostInitProperties()
{
Super::PostInitProperties();
for (FString PackageName : StartupPackages)
{
LoadPackage(nullptr, *PackageName, 0);
}
}
[/Script/ShooterGame.ShooterGameInstance]
+StartupPackages=/Game/Textures/T_ImageA
###例2
別の例として、GameInstanceのコンストラクタでテクスチャをロードしていますが、これが呼ばれるタイミングはDisregardForGCがまだクローズしていないため、ここでロードされたTexture2D オブジェクトは最終的に永続オブジェクトとして破棄されません。
UShooterGameInstance::UShooterGameInstance(const FObjectInitializer& ObjectInitializer)
: Super(ObjectInitializer)
, OnlineMode(EOnlineMode::Online) // Default to online
, bIsLicensed(true) // Default to licensed (should have been checked by OS on boot)
{
UTexture2D* DisregardGCTexture = LoadObject<UTexture2D>(NULL, TEXT("/Game/Textures/T_ImageB.T_ImageB"), nullptr, LOAD_None, nullptr);
}
###例3
さらに別の例として、UObjectから派生したクラスでPostCDOContruct
でアセットをロードする例です。
void UMyObject::PostCDOContruct()
{
UTexture2D* DisregardGCTexture = LoadObject<UTexture2D>(NULL, TEXT("/Game/Image/TextureC.TextureC"), nullptr, LOAD_None, nullptr);
}
#5. QA
Q:どのオブジェクトでロードしたものが対象となる?
A:UObjectを継承したクラスが対象。非UObjectのクラスは対象外。
Q:どのモジュール(タイミング)でロードしたものが対象となる?
A:エンジンの初期化時に全UObjectのCDO(CreateDefaultObject)処理が走るため、そこに関連するConstructor
、PostInitProperties
, PostCDOContruct
でロードしておけばDisregardForGCの対象となります。使用例を参照。