18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[UE4] Asset Managerのアセットの非同期ロード機能について その2 ( レベルアセット以外の裏読み編 )

Last updated at Posted at 2019-10-27

前回の記事:

Asset Managerのアセットの非同期ロード機能について その1 ( 非同期ロードの解説 & レベルの裏読み編 )

以降は、↑の記事を読んでいる前提で進みます。

はじめに

前回はレベルアセットの非同期ロード(裏読み)について説明しました。しかし、記事の最後で説明した通り、レベルアセットを丸ごと非同期ロードするとメモリ使用量などの問題が発生する場合があります。そこで、今回はもう少し細かいアセット単位で非同期ロードする方法について説明します。

なお、本記事を作成する上で検証・使用したUE4のバージョンはUE4.23.1です。

アセット単体で非同期ロードをするには

プロジェクトによって色んなケースがあると思いますが、キャラの衣装替え目的などで需要が多そうなSkeletalMeshアセット単体の非同期ロードを例にします。

前回の記事で紹介した手順でセットアップ・動作してみると…

まずは前回の記事で紹介した手順でセットアップをしていきましょう!
image.png
プロジェクト設定のAsset Manager/Primary Asset Type to Scan に SkeletalMeshアセットを追加します。これでSkeletalMeshアセットがAssetManagerの管理対象であるPrimary Assetとして登録されます。
image.png
Async Load Primary Assetノードで非同期ロードを行います。今回はIsValidノードを使ってロードが正常に完了したか確認します。
image.png
そして、PIEではなくStandalone起動で試すと…無事非同期ロードが完了しました!簡単ですね!
…が、、いざPackageを作ろうとすると「PrimaryAssetId SkeletalMesh:SK_Mannequinが対象のアセットとマッチしてないよ!」というエラーが発生して失敗します。

UATHelper: Packaging (Windows (64-bit)):   LogAssetManager: Error: Registered PrimaryAssetId SkeletalMesh:SK_Mannequin for asset /Game/Mannequin/Character/Mesh/SK_Mannequin.SK_Mannequin does not match object's real id of ! This will not load properly at runtime!
PackagingResults: Error: Registered PrimaryAssetId SkeletalMesh:SK_Mannequin for asset /Game/Mannequin/Character/Mesh/SK_Mannequin.SK_Mannequin does not match object's real id of ! This will not load properly at runtime!
...
UATHelper: Packaging (Windows (64-bit)):   LogInit: Display: LogAssetManager: Error: Registered PrimaryAssetId SkeletalMesh:SK_Mannequin for asset /Game/Mannequin/Character/Mesh/SK_Mannequin.SK_Mannequin does not match object's real id of ! This will not load properly at runtime!

これは対象のクラスをPrimaryAssetとして管理する際に必要なPrimaryAssetIdを返すGetPrimaryAssetId関数がUSkeletalMeshクラスに実装されていないためです。

余談:Standalone起動時はエラーが出ずに正常に動くのはなぜ?

image.png
プロジェクト設定のAsset Manager/Should Guess Type and Name in Editorがデフォルトで有効になっているためです。この機能が有効になっている場合、これから説明するGetPrimaryAssetId関数が対象のクラスに実装されていなくても非同期ロードに成功します。しかし、名前の通りエディタでしか動作しない機能なのでPackageでは動作しません。また、内部ではアセットパスからの検索処理を行っているため、中・大規模プロジェクトになるとAsset Managerに関するエディタ操作が遅くなる可能性があります。
いい感じにしてくれる便利な項目ではあるのですが、OFFにしておいた方がトラブルは少なくなるかと思います。具体的な処理内容が気になる方はUAssetManager::ExtractPrimaryAssetIdFromDataをご確認くださいまし。

Primary Asset Idに関するエラーを解決するには

この問題を解決するための方法は以下の2つです。

  • USkeletalMeshGetPrimaryAssetId関数を追加・実装する
  • プロジェクト設定のAsset Manager/Should Manager Determine Type and Nameを有効にする

USkeletalMeshにGetPrimaryAssetId関数を追加実装する

GetPrimaryAssetId関数がないというエラーだったので、GetPrimaryAssetId関数を実装すれば解決します(そのまま)。

Engine\Source\Runtime\Engine\Classes\Engine\SkeletalMesh.h
virtual FPrimaryAssetId GetPrimaryAssetId() const override;
Engine\Source\Runtime\Engine\Private\SkeletalMesh.cpp
FPrimaryAssetId USkeletalMesh::GetPrimaryAssetId() const
{
	return FPrimaryAssetId(GetClass()->GetFName(), GetOutermost()->GetFName());
}

上記の対応を入れることで、各SkeletalMeshアセットがPrimaryAssetIdをAssetManagerに返すようになります。

image.png
ちなみに、PrimaryAssetIdはPrimaryAssetType, PrimaryAssetNameから構成されており、FPrimaryAssetIdの各引数ではそれらを設定しています。アセットにマウスを重ねることで、どんな文字列が設定されているか確認できます。

image.png
そして、ここで設定したPrimaryAssetTypeはプロジェクト設定のAsset Manager/Primary Asset Type to ScanにおけるPrimary Asset Typeと一致させる必要があります。もし一致していない場合はAsset Managerが認識できなくなります。

これでAsset ManagerがSkeletal Meshアセットを正しく認識・管理できるようになりました。プロジェクト側のクラスであれば上記のような対応で済むのですが、今回のようにエンジン側のクラスの場合はエンジンコードを編集することになります。

余談:Asset Managerがデフォルトでレベルアセットを認識できているのはなぜ?

Engine\Source\Runtime\Engine\Private\World.cpp
FPrimaryAssetId UWorld::GetPrimaryAssetId() const
{
	UPackage* Package = GetOutermost();

	if (!Package->HasAnyPackageFlags(PKG_PlayInEditor))
	{
		// Return Map:/path/to/map
		return FPrimaryAssetId(UAssetManager::MapType, Package->GetFName());
	}

	return FPrimaryAssetId();
}

理由は単純で、レベルアセットを管理するUWorldクラスにてGetPrimaryAssetId関数が実装されているからです。

image.png
ちなみに、ここで使用されているUAssetManager::MapTypeの中身はconst FPrimaryAssetType UAssetManager::MapType = FName(TEXT("Map"));です。そのため、Should Manager Determine Type and Nameにおけるレベルアセットを管理する項目のPrimary Asset TypeはMapに設定されています。

プロジェクト設定のAsset Manager/Should Manager Determine Type and Nameを有効にする

image.png
上図の赤枠の項目を有効にすることでもエラーを回避することができます。この項目を有効にすることで、先程のGetPrimaryAssetId関数が実装されていなくても、Asset Managerによる非同期ロードが動作するようになります。

しかし、アセットの検索処理が追加されるため、GetPrimaryAssetId関数が実装されている場合に比べると処理負荷が増加します。 どのような処理が走っているか気になる方は UAssetManager::DeterminePrimaryAssetIdForObjectをご確認くださいまし。

ここまでのまとめ

  • Asset ManagerによってPrimary Assetとして正しく管理されるためには、対象のクラスにGetPrimaryAssetId関数が実装されている必要がある
  • GetPrimaryAssetId関数を追加するにはエンジンコードを編集する必要がある場合がある
  • GetPrimaryAssetId関数を実装しなくても「いい感じに」してくれる機能はあるが、その分の負荷が加算されてしまう。

次回予告

「Asset Managerで非同期ロードするには対象のクラスにGetPrimaryAssetId関数が必要なのは分かったけど、対応するの凄く大変そう…」と感じていただけたかと思います…

そんなあなたのために、GetPrimaryAssetId関数を実装しなくても
各アセットを効率よく非同期ロードできる仕組み「PrimaryDataAsset, PrimaryAssetLabel」が用意されています!

(この子達の良さを感じてもらうために、活用しない場合はどれ程大変なのかが今回の記事の趣旨でした)

次回の記事ではそれらの機能の使い方について解説しようと思います。

おわり

18
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
18
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?