资产注册表

资产注册表 是一个编辑器子系统,它在编辑器加载时异步收集有关未加载的资产的信息。该信息存储在内存中,这样,编辑器无需加载它们就可以创建资产列表。该信息是权威信息,当资产在内存中被更改或文件在磁盘上被更改时,它会自动更新。此系统主要由 内容浏览器 使用,但是也可能会在编辑器代码的任意位置被使用。

获取资产列表

要按类建立资产列表,只需加载资产注册表(Asset Registry)模块然后调用 Module.Get().GetAssetsByClass() 即可。

FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> AssetData;
const UClass* Class = UStaticMesh::StaticClass();
AssetRegistryModule.Get().GetAssetsByClass(Class->GetFName(), AssetData);

以上代码将返回一系列 FAssetData 对象,它们描述已加载或未加载的资产。FAssetData 对象容纳有关资产的信息,这些信息在资产被加载前就可以确定。

下面是其成员列表和说明:

成员 说明
FName ObjectPath 资产的对象路径,形式是 Package.GroupNames.AssetName
FName PackageName 包含该资产的包的名称。
FName PackagePath 包含该资产的包的路径。
FName GroupNames 包含该资产的组名称的列表(使用 . 分隔)。如果无组,则为 NAME_None
FName AssetName 无包或组的资产的名称。
FName AssetClass 资产的类的名称。
TMap<FName, FString> TagsAndValues 标记为 AssetRegistrySearchable 的属性的值映射。请参阅 标记和值 以了解更多信息。

你也可以通过调用下列任意一个函数,使用其他条件建立列表:

函数 说明
GetAssetsByPackageName() 返回来自特定包的资产的列表。
GetAssetsByPath() 返回特定路径中的资产的列表。
GetAssetByObjectPath() 返回具有特定对象路径的资产的列表。
GetAssetsByTagValues() 返回具有特定标记和值组合的资产的列表。
GetAllAssets() 返回所有资产的列表。此过程可能较慢。

如果需要使用多个条件建立资产列表,可以使用 GetAssets() 并提供 FARFilter 结构体,如 创建过滤器 部分中所述。

将 FAssetData 转换为 UObject*

FAssetData 对象具有一个名称为 GetAsset() 的函数,它将返回 FAssetData 表示的 UObject*。如果需要,它将加载资产然后返回它。

如果仅希望检查资产是否已加载,可使用 IsAssetLoaded()

创建过滤器

调用 GetAssets() 时可提供 FARFilter,以创建采用多个条件过滤后的资产列表。过滤器由多个组件组成:

  • 包名称(PackageName)
  • 包路径(PackagePath)
  • 集合(Collection)
  • 类(Class)
  • 标记/值(Tags/Value)对

一个组件可能具有多个元素。要通过过滤器,资产必须满足 所有 组件。要满足组件,资产必须与其中的 任意 元素匹配。

例如,存在一个路径为 /Game/Meshes/BeachBall 的静态网格体资产:

  • 如果过滤器仅包含包路径 /Game/Meshes,资产将通过过滤。仅存在一个具有一个元素的组件。
  • 如果过滤器包含包路径 /Game/MeshesUParticleSystem UStaticMesh 类,资产将通过过滤。存在两个组件,第一个组件具有一个元素,第二个组件具有两个元素。
  • 如果过滤器包含包路径 /Game/Meshes 并且 包含 UParticleSystem 类,资产将无法通过过滤。存在两个组件,每个组件各具有一个元素。
  • 如果过滤器包含包路径 /Game/NotMeshesUStaticMesh 类,资产将无法通过过滤。该过滤器也使用两个组件,每个组件各具有一个元素。

使用具有两个组件(类和包路径)的过滤器的一个示例:

FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
TArray<FAssetData> AssetData;
FARFilter Filter;
Filter.Classes.Add(UStaticMesh::StaticClass());
Filter.PackagePaths.Add("/Game/Meshes");
AssetRegistryModule.Get().GetAssets(Filter, AssetData);

标记和值

从资产注册表返回的 FAssetData 对象包含名称和称作 TagsAndValues 的值映射。它们是 FAssetData 表示的资产的属性名称和关联值。此信息是在资产被保存时收集的,存储在包含资产的 UAsset 文件的标头中。资产注册表读取此标头并相应地填充 TagsAndValues 映射。资产注册表仅收集使用 AssetRegistrySearchable UPROPERTY() 标记标记的属性。

例如(来自 UTexture):

/** 对此纹理采样时使用的纹理过滤模式。*/
UPROPERTY(Category=Texture, AssetRegistrySearchable)
TEnumAsByte<enum TextureFilter> Filter;

将此标记添加到 UTexture 的 “过滤器(Filter)” 属性后,之后保存的所有 UTextureFAssetDataTagsAndValues 映射中此时都将具有一个键是 Filter、值是枚举值的字符串表示(例如,"TF_Linear")的条目。

要使资产注册表能够发现资产的属性,必须重新保存资产。

如果你希望资产注册表能够搜索本身不是 UProperty 的信息,资产的类可以实现虚函数 GetAssetRegistryTags(),以手动将键/值对添加到 TagsAndValues 映射。GetAssetRegistryTags 继承自 UObject。

异步数据收集

资产注册表异步读取 UAsset 文件,因此在你请求资产列表时,列表可能并非包含所有资产的完整列表。如果编辑器代码需要完整列表,资产注册表可提供委托回调,涵盖资产被发现/创建、重命名或删除等情况。也存在适用于资产注册表已完成初始搜索的情况的委托,它对于很多系统都非常有用。

注册这些委托的方法是,加载 “资产注册表(Asset Registry)” 模块,然后使用 IAssetRegistry 中提供的函数:

/** 注册/取消注册适用于资产被添加到注册表中的情况的回调 */
virtual FAssetAddedEvent& OnAssetAdded() = 0;

/** 注册/取消注册适用于资产被从注册表中删除的情况的回调 */
virtual FAssetRemovedEvent& OnAssetRemoved() = 0;

/** 注册/取消注册适用于资产在注册表中被重命名的情况的回调 */
virtual FAssetRenamedEvent& OnAssetRenamed() = 0;

/** 注册/取消注册适用于资产注册表加载完文件的情况的回调 */
virtual FFilesLoadedEvent& OnFilesLoaded() = 0;

/** 注册/取消注册更新后台文件加载进度的回调 */
virtual FFileLoadProgressUpdatedEvent& OnFileLoadProgressUpdated() = 0;

/** 如果资产注册表当前正在加载文件,尚无法返回有关所有资产的信息时返回 True */
virtual bool IsLoadingAssets() = 0;

例如:

void FMyClass::FMyClass()
{
    // 加载资产注册表模块,以侦听更新
    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    AssetRegistryModule.Get().OnAssetAdded().AddRaw(this, &FMyClass::OnAssetAdded);
}

FMyClass::~FMyClass()
{
    // 加载资产注册表模块,以将委托取消注册
    FAssetRegistryModule& AssetRegistryModule = FModuleManager::LoadModuleChecked<FAssetRegistryModule>("AssetRegistry");
    AssetRegistryModule.Get().OnAssetAdded().RemoveAll(this);
}

void FMyClass::OnAssetAdded(const FAssetData& AssetData)
{
    // 资产注册表发现某个资产。
    // 这意味着该资产刚被创建或刚在磁盘上被发现。
    // 确保此函数中的代码速度较快,否则它将拖慢收集过程。
}

可在 commandlet 中使用资产注册表,但是这样会同步收集信息。LoadModule() 调用将被阻止,直至收集完成。

如果代码期望资产被异步发现,并且具有 Slate UI 前端,它应包含 SAssetDiscoveryIndicator 控件,以将进度传达给用户。