前回の記事:
Asset Managerのアセットの非同期ロード機能について その1 ( 非同期ロードの解説 & レベルの裏読み編 )
以降は、↑の記事を読んでいる前提で進みます。
はじめに
前回はレベルアセットの非同期ロード(裏読み)について説明しました。しかし、記事の最後で説明した通り、レベルアセットを丸ごと非同期ロードするとメモリ使用量などの問題が発生する場合があります。そこで、今回はもう少し細かいアセット単位で非同期ロードする方法について説明します。
なお、本記事を作成する上で検証・使用したUE4のバージョンはUE4.23.1です。
アセット単体で非同期ロードをするには
プロジェクトによって色んなケースがあると思いますが、キャラの衣装替え目的などで需要が多そうなSkeletalMeshアセット単体の非同期ロードを例にします。
前回の記事で紹介した手順でセットアップ・動作してみると…
まずは前回の記事で紹介した手順でセットアップをしていきましょう!
プロジェクト設定のAsset Manager/Primary Asset Type to Scan
に SkeletalMeshアセットを追加します。これでSkeletalMeshアセットがAssetManagerの管理対象であるPrimary Assetとして登録されます。
Async Load Primary Asset
ノードで非同期ロードを行います。今回はIsValid
ノードを使ってロードが正常に完了したか確認します。
そして、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起動時はエラーが出ずに正常に動くのはなぜ?
プロジェクト設定のAsset Manager/Should Guess Type and Name in Editor
がデフォルトで有効になっているためです。この機能が有効になっている場合、これから説明するGetPrimaryAssetId
関数が対象のクラスに実装されていなくても非同期ロードに成功します。しかし、名前の通りエディタでしか動作しない機能なのでPackageでは動作しません。また、内部ではアセットパスからの検索処理を行っているため、中・大規模プロジェクトになるとAsset Managerに関するエディタ操作が遅くなる可能性があります。
いい感じにしてくれる便利な項目ではあるのですが、OFFにしておいた方がトラブルは少なくなるかと思います。具体的な処理内容が気になる方はUAssetManager::ExtractPrimaryAssetIdFromData
をご確認くださいまし。
Primary Asset Idに関するエラーを解決するには
この問題を解決するための方法は以下の2つです。
-
USkeletalMesh
にGetPrimaryAssetId
関数を追加・実装する - プロジェクト設定のAsset Manager/Should Manager Determine Type and Nameを有効にする
USkeletalMeshにGetPrimaryAssetId関数を追加実装する
GetPrimaryAssetId
関数がないというエラーだったので、GetPrimaryAssetId
関数を実装すれば解決します(そのまま)。
virtual FPrimaryAssetId GetPrimaryAssetId() const override;
FPrimaryAssetId USkeletalMesh::GetPrimaryAssetId() const
{
return FPrimaryAssetId(GetClass()->GetFName(), GetOutermost()->GetFName());
}
上記の対応を入れることで、各SkeletalMeshアセットがPrimaryAssetIdをAssetManagerに返すようになります。
ちなみに、PrimaryAssetIdはPrimaryAssetType
, PrimaryAssetName
から構成されており、FPrimaryAssetId
の各引数ではそれらを設定しています。アセットにマウスを重ねることで、どんな文字列が設定されているか確認できます。
そして、ここで設定したPrimaryAssetType
はプロジェクト設定のAsset Manager/Primary Asset Type to Scan
におけるPrimary Asset Type
と一致させる必要があります。もし一致していない場合はAsset Managerが認識できなくなります。
これでAsset ManagerがSkeletal Meshアセットを正しく認識・管理できるようになりました。プロジェクト側のクラスであれば上記のような対応で済むのですが、今回のようにエンジン側のクラスの場合はエンジンコードを編集することになります。
余談:Asset Managerがデフォルトでレベルアセットを認識できているのはなぜ?
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
関数が実装されているからです。
ちなみに、ここで使用されている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を有効にする
上図の赤枠の項目を有効にすることでもエラーを回避することができます。この項目を有効にすることで、先程のGetPrimaryAssetId
関数が実装されていなくても、Asset Managerによる非同期ロードが動作するようになります。
しかし、アセットの検索処理が追加されるため、GetPrimaryAssetId
関数が実装されている場合に比べると処理負荷が増加します。 どのような処理が走っているか気になる方は UAssetManager::DeterminePrimaryAssetIdForObject
をご確認くださいまし。
ここまでのまとめ
- Asset ManagerによってPrimary Assetとして正しく管理されるためには、対象のクラスに
GetPrimaryAssetId
関数が実装されている必要がある -
GetPrimaryAssetId
関数を追加するにはエンジンコードを編集する必要がある場合がある -
GetPrimaryAssetId
関数を実装しなくても「いい感じに」してくれる機能はあるが、その分の負荷が加算されてしまう。
次回予告
「Asset Managerで非同期ロードするには対象のクラスにGetPrimaryAssetId
関数が必要なのは分かったけど、対応するの凄く大変そう…」と感じていただけたかと思います…
そんなあなたのために、GetPrimaryAssetId
関数を実装しなくても
各アセットを効率よく非同期ロードできる仕組み「PrimaryDataAsset, PrimaryAssetLabel」が用意されています!
(この子達の良さを感じてもらうために、活用しない場合はどれ程大変なのかが今回の記事の趣旨でした)
次回の記事ではそれらの機能の使い方について解説しようと思います。
おわり