こんにちは、情熱開発部プログラム1課の平野です!
UE4でゲーム開発をしていると【ハード参照、ソフト参照】というワードをよく耳にするかと思います。UE4プロジェクトに配属されて間もない頃の私は、正直なところあまりこの言葉の意味を理解しないまま開発を進めていました。
その結果、プロジェクト全体にも影響するような問題を、知らず知らずのうちに引き起していたのでした…。
そんな自分への戒めの意味も込めつつ、今回はハード参照とソフト参照について記事をしたためたいと思います。
※以下、UE4のver.4.26を使用しております。
ハード参照とは?
ハード参照とはアセットの直接参照のことです。例えば、アセットAがアセットBをハード参照していたとすると、アセットAがロードされた時、自動的にアセットBもロードされます。
この時もし、アセットBがアセットCをハード参照していたとすると、アセットAをロードするだけでアセットCまでロードされます。
したがって、ハード参照では依存するアセットが自動的にロードされる為、ロードの順序やタイミング等の細かい挙動を考慮せずに実装することができます。
ハード参照の実装例
class UHardReferenceTester : public UObject
{
GENERATED_BODY()
protected:
// プロパティ指定してアセットを参照させる等すると、
// 基本的にはハード参照になります。
UPROPERTY(EditDefaultsOnly)
UParticleSystem* ParticleAsset;
};
ソフト参照とは?
一方、ソフト参照とはアセットの間接参照(パス情報)のことです。ハード参照と異なり、親アセットをロードしただけで自動的にロードされることはなく、実装者が任意のタイミングでロードする必要があります。
すぐ必要になる訳ではないアセットなど、自動的にロードしてほしくないものをソフト参照化し、必要になった時にロードすることで、ハード参照によるロード時間の短縮や、メモリの節約を図ることができます。
ソフト参照の実装例(非同期ロード)
class USoftReferenceTester : public UObject
{
GENERATED_BODY()
public:
// ロード開始関数
void RequestAsyncLoad();
// ロード完了関数
void OnAssetLoaded();
protected:
// ハンドル
TSharedPtr<FStreamableHandle> StreamableHandle;
// ロードするソフト参照アセット
UPROPERTY(EditDefaultsOnly)
TSoftObjectPtr<UParticleSystem> ParticleAsset;
};
void USoftReferenceTester::RequestAsyncLoad()
{
// StreamableManagerを取得
FStreamableManager& Streamable = UGameGlobals::Get().StreamableManager;
// 非同期ロード開始(完了するとOnAssetLoaded()が呼ばれる)
StreamableHandle = Streamable->RequestAsyncLoad(ParticleAsset.ToSoftObjectPath(),
FStreamableDelegate::CreateUObject(this, &ThisClass::OnAssetLoaded));
}
void USoftReferenceTester::OnAssetLoaded()
{
// ソフト参照アセットの非同期ロードが完了!
}
地獄の参照連鎖について
ロード処理を意識することなく実装できる為、便利で使い勝手の良いハード参照ですが、何でもかんでもハード参照していると、開発が進むにつれ徐々に欠陥が露呈していきます。
前述の通り、ハード参照された全ての依存アセットは自動ロードされます。 つまりハード参照されたアセットの中で、ハード参照されているアセットも芋づる式に読み込まれます。
プロジェクトが肥大化していくと、一つのアセットをロードしただけなのに数百、数千の不要なアセットが連鎖的にロードされる…ということが発生し得ます。これが(地獄の)参照連鎖です。
参照連鎖を防止するには?
1.データテーブルにハード参照要素を追加しない
開発が進み量産体制に入っていくと、各種データテーブルの情報が肥大化していく事もよくあるかと思います。データテーブルでアセットを指定する場合には、ハード参照しないように気を付けなければいけません。
アイテムを1つロードする為にデータテーブルにアクセスした途端、データテーブルに登録されている全アイテムのロードが始まる…ということが起こり得てしまう為です。
多くのアセット情報が集まるデータテーブルではこういった問題が起こりやすく、全体への影響も大きいと言えます。
2.できるだけBPを参照しない
参照連鎖を引き起してしまう要因として、意図せずブループリントをハード参照してしまうパターンもあります。 BPクラスは「.uasset」であり、以下のような実装をした場合アセットのハード参照になります。
- BPクラスをキャストする。(Cast To BP_〇〇)
- 関数の引数や戻り値の型としてBPクラスを指定する。
- 変数としてBPクラスを保持する。
こういった実装を防ぐ為には、新しいオブジェクトを作成する際にいきなりBPへ派生せず、必ずC++の親クラスを作成して抽象化し、BPを参照しなくても良いような設計を心掛けることが大切です。(C++へのキャスト等ではアセット参照は増えません。)
さいごに
以上、UE4のハード参照とソフト参照についてでした。
参照連鎖による影響はゲーム内のみならず、UE4エディタを開いた時のロード時間にも反映されます。つまりプロジェクトチーム全体の作業効率に直結する問題であると言えます。
今回の記事をまとめながら、UE4ゲーム開発者として必ず知っておくべき挙動だなと改めて思いました。無駄なロードが発生しないよう、適切なアセット参照を心掛けていきたいですね。
最後までご清覧いただきありがとうございました!
参考にさせていただいた資料
【免責事項】
本サイトでの情報を利用することによる損害等に対し、
株式会社ロジカルビートは一切の責任を負いません。