资源注册表

Asset Registry(资源注册表) 是一个编辑器子系统,它在编辑器加载资源过程中,异步地收集卸载的资源的信息。 该信息存储在内存中,以便编辑器不必加载这些资源就可以创建资源列表。 该信息是权威信息,且随着资源在内存中发生改变或者文件在磁盘中发生改变,该信息可以自动更新。 内容浏览器 是该系统的主要用户,但是这个信息可以在编辑器代码中的任何地方进行应用。

获得资源列表

要想按类别形成一个资源列表,仅需加载 Asset Registry模块,然后调用 Module.Get().GetAssetsByClass() 即可。

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

这将会返回一系列 FAssetData 对象,它们描述了可以加载或卸载的资源。 FAssetData 对象存放了关于资源的信息,该信息在加载该资源之前就可以确定。

这里是其成员的列表及介绍:

成员 描述
FName ObjectPath 资源的对象路径,以 包.组名称.资源名称 形式呈现。
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 pairs(标签/值 对)

一个构成部分可以有多个元素。 一个资源如果满足 所有 构成部分的要求,则通过过滤器。 要想满足一个构成部分的要求,那么该资源必须满足该构成部分中的 每个 元素的要求。

比如,如果存在一个静态网格物体,路径是 /Game/Meshes/BeachBall:

  • 如果过滤器仅包路径/Game/Meshes,则该资源通过过滤器。 该过滤器仅有一个构成部分,且该构成部分仅有一个元素。

  • 如果过滤器包路径 /Game/Meshes 和 类 UParticleSystem UStaticMesh,那么该资源将通过过滤器。 这个过滤器有两个构成部分,第一部分有一个元素,第二部分有两个元素。

  • 如果过滤器包路径 /Game/Meshes唯一的UParticleSystem ,那么该资源将不能通过过滤器。 该过滤器有两个构成部分,每个部分有一个元素。

  • 如果过滤器包含路径 /Game/NotMeshes 和类UStaticMesh,那么该资源将不能通过过滤器。 该过滤器也具有两个构成部分,但每个部分有一个元素。

这里是一个应用过滤器的示例,该过滤器由Class(类)和PackagePath(路径)两个构成部分构成:

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 的属性。

比如(来自 UTexture ):

/** 当对该贴图进行采样时所使用的贴图过滤方式。 */
UPROPERTY(Category=Texture, AssetRegistrySearchable)
TEnumAsByte<enum TextureFilter> Filter;

一旦该标记添加到了 UTexture 的 'Filter' 属性上,那么后续保存的所有 UTextures 在其 FAssetDataTagsAndValues 映射表中都有一项键值对: 键名是 "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)
{
    // 由资源注册表发现的资源。
    // 这意味着其刚刚被创建或最近在硬盘上被发现。
    // 请确认此函数中的代码速度够快,否则它会让收集过程变慢。
}

资源注册表可以用于命令行开关中,但是这时会同步地收集信息。 直到收集信息完成之前, LoadModule() 调用将会被阻止。

如果您的代码需要等待异步发现的资源,并具有 Slate用户界面 前端,那么它应该包含一个 SAssetDiscoveryIndicator 控件来向用户呈现进度。