Engine_Updating\Engine\Source\Runtime\CoreUObject\Public\Misc\StringAssetReference.h
/**
* A struct that contains a string reference to an asset, can be used to make soft references to assets
*/
struct COREUOBJECT_API FStringAssetReference
{
.................
};
通过这个结构体。
代码类似于下面
TAssetPtr<UObject> StringToAssetID(FString Parms)
{
FStringAssetReference AssetRef = FStringAssetReference(Parms);
TAssetPtr<UObject> AssetID;
AssetID = AssetRef;
return AssetID;
}
FPackageName::SearchForPackageOnDisk(FilePath)
判断是否存在文件。
这里返回的之歌assetptr<uobject>就可以在蓝图里面使用
不过上面的这个必须要保证资源存在,不然会导致游戏空指针崩溃。
下面是官方文档对引用和异步加载的文档
引用资产
-
虚幻引擎 4 提供了许多种机制来控制引用资产的方式并通过扩展将其装入内存。这些引用分为两种方式:硬性引用,即对象 A 引用对象 B,并导致对象 B 在对象 A 加载时加载;软性引用,即对象 A 通过间接机制(例如字符串形式的对象路径)来引用对象 B。下面的前两个小节阐述硬性引用,而其余小节探讨软性引用。
直接属性引用
这是最常见的资产引用情况,并通过 UPROPERTY 宏公开。您的游戏类会公开一个 UPROPERTY,后者允许设计人员通过蓝图继承对原型指定特定资产,或通过放在环境中的实例来指定该资产。例如,以下代码来自 StrategyGame 样本中包含的 AStrategyBuilding,它允许设计人员选择建造某种类型的建筑时播放的声音。
/** construction start sound stinger */ UPROPERTY(EditDefaultsOnly, Category=Building) USoundCue* ConstructionStartStinger;
此属性只能作为对象默认属性的一部分进行设置(由 EditDefaultsOnly 关键字控制)。设计人员创建扩展 AStrategyBuilding 的新蓝图类。然后,可以为该蓝图保存设计人员所需要的声音。每当设计人员所创建的该蓝图加载时,还将自动加载该 UPROPERTY 中引用的声音。
构造时引用
您将会遇到的第二类硬性引用是程序员知道需要为给定属性加载的确切资产,并在对象的构造中设置该属性。这项任务是使用特殊类 ConstructorHelpers 完成的,这个类在构造阶段查找某个对象的对象和类。以下 HUD 片段同样来自 StrategyGame 样本,它在其渲染过程中指派要使用的资产。
/** gray health bar texture */ UPROPERTY() class UTexture2D* BarFillTexture; AStrategyHUD::AStrategyHUD(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { static ConstructorHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill")); ... BarFillTexture = BarFillObj.Object; ... }
在以上构造函数中,ConstructorHelpers 类将尝试在内存中查找该资产,如果找不到,则进行加载。请注意,使用资产的完整路径来指定要加载的内容。如果该资产不存在或者由于出错而无法加载,那么该属性将设置为 nullptr。发生这种情况时,尝试访问纹理的代码将崩溃。最好进行声明,指出资产已正确加载(如果后续代码假设引用有效)。
UPROPERTY 的声明与前面的硬性引用示例相同。它们的工作方式相同,只不过是最初的设置方式有所差别。有关硬性引用的一个注意事项是,当对象加载并实例化时,还将加载以硬性方式引用的资产。您必须仔细地进行考虑,否则内存使用量会因为同时加载许多资产而迅速增加。如果您希望推迟该加载或确定要在运行时加载的内容,那么下列各节可以帮助您完成这些任务。
间接属性引用
控制何时加载资产的一种简单方法是使用 TAssetPtr。对于设计人员,间接属性引用的工作方式就像直接属性引用一样。但是,属性以字符串形式与模版代码存储在一起以便安全地检查资产是否已加载,而不是进行直接指针引用。使用 IsPending() 方法可检查资产是否已准备好可供访问。请注意,使用 TAssetPtr 要求在您想要使用资产时手动加载该资产。您可使用模板化 LoadObject<>() 方法、StaticLoadObject() 或 FStreamingManager 来加载对象(有关更多信息,请参阅异步资源加载 )。前两个方法以同步方式加载资产,这可能会导致帧速率突增,因此,仅当您知道不会影响游戏时,才应使用这些方法。
UPROPERTY(EditDefaultsOnly, BlueprintReadWrite, Category=Building) TAssetPtr<UStaticMesh> BaseMesh; UStaticMesh* GetLazyLoadedMesh() { if (BaseMesh.IsPending()) { const FStringAssetReference& AssetRef = BaseMesh.ToStringReference(); BaseMesh = Cast< UStaticMesh>(Streamable.SynchronousLoad(AssetRef)); } return BaseMesh.Get(); }
以上代码使用 UStaticMesh 的 TAssetPtr 将网格的加载推迟到运行时进行。将检查资产,以确定对象是否已加载。如果尚未加载,那么将使用 FStreamingManager 执行同步加载。否则,将返回 TAssetPtr 内的 UStaticMesh 指针给调用者。
如果您希望推迟加载 Uclass,请使用与 TAssetPtr 相同的方法,并替换类特定版本 TAssetSubclassOf 模版类型。其工作方式与引用特定的资产相同,但改为引用资产的 Uclass,而不是引用接口。
查找/加载对象
到目前为止,这些示例全都基于 UPROPERTY。但是,如果您希望在运行时构建字符串并使用该字符串来引用对象,情况将会如何?您可使用两个选项。如果您仅在 UObject 已加载或已创建时才使用它,那么正确的选择是使用 FindObject<>()。如果您希望对象未加载时将其加载,那么正确的选择是使用 LoadObject<>()。请注意,LoadObject<>() 在内部执行的操作与 FindObject 相同,因此您不必先尝试查找对象再进行加载。以下是各个函数的一些用法示例。
AFunctionalTest* TestToRun = FindObject<AFunctionalTest>(TestsOuter, *TestName); GridTexture = LoadObject<UTexture2D>(NULL, TEXT("/Engine/EngineMaterials/DefaultWhiteGrid.DefaultWhiteGrid"), NULL, LOAD_None, NULL);
加载 UClass 时,可以使用 LoadObject 的一种特殊形式。这无非是加载类的一种较简单方法,它会自动验证类型。以下代码片段对此作了说明。
DefaultPreviewPawnClass = LoadClass<APawn>(NULL, *PreviewPawnName, NULL, LOAD_None, NULL);
等同于:
DefaultPreviewPawnClass = LoadObject<UClass>(NULL, *PreviewPawnName, NULL, LOAD_None, NULL); if (!DefaultPreviewPawnClass->IsA(APawn::StaticClass())) { DefaultPreviewPawnClass = nullptr; }
异步资源加载
-
概述
虚幻引擎4中有几个新系统,使得可以更加轻松地异步加载资源数据,这些系统代替了虚幻引擎3中的免搜索内容包中存在的很多功能。这些新方法既可以在开发数据应用也可以在设备上的烘焙数据上进行应用,所以您不必保留两个根据需要加载数据的代码路径。有两种通用的方法可以供您根据需要来引用及加载数据。
FStringAssetReferences 和TAssetPtr
美术师或设计师引用资源的最简单的方法是,创建一个UProperty 强指针,并赋予其一个类目。在虚幻引擎4中,如果您使用强指针UObject 属性引用一个资源,那么当加载包含该属性的对象时将会加载那个资源(通过把对象放置在地图中,或者通过从类似于游戏信息这样的东西中进行引用该对象)。如果您不关心是否在游戏启动时能够100%地加载您的资源。如果您想让 美术师/设计师 使用和强指针一样的用户界面来引用特定的资源,而不是总是加载引用的资源,请使用
FStringAssetReference
或TAssetPtr
。FStringAssetReference
是一个简单的结构体,包含了一个具有资源完整名称的字符串。如果您在类中创建一个那种类型的属性,那么它将会显示在编辑器中,就像是个UObject *
属性一样。它还可以正确地处理烘焙和重定向,所以如果您有一个StringAssetReference,那么它一定可以在设备上正常工作。TAssetPtr基本上就是一个封装了FStringAssetReference
的TWeakObjectPtr
,它使用一个特定的类作为模板,以便您能限制编辑器用户界面,使其仅允许选择特定的类。如果所引用的资源存在于内存中,那么TAssetPtr.Get()
将返回该资源。如果该资源不在内存中,那么您那可以调用ToStringReference()
来查找它引用的资源,并使用下面介绍的方法加载该资源,然后再次调用TAssetPtr.Get()
解除对该资源的引用。如果美术师或设计师正在手动地设置引用,那么使用 TAssetPtrs 和 StringAssetReferences是非常适合的,但如果您想进行类似于查询这样的处理,查找满足特定要求的资源,而不是加载所有资源,那么您应该使用资源注册表及对象库。
资源注册表和对象库
资源注册表是一个存储资源的元数据的系统,允许您搜索及查询这些资源。编辑器使用此资源注册表来显示内容浏览器中的信息,但是您也可以从游戏性代码使用该注册表,来查找当前没有加载的游戏资源的相关元数据。要想使得一个资源的数据是可搜索的,您需要给该属性添加"AssetRegistrySearchable"标签。查询资源注册表会返回FAssetData类型的对象,它包含了关于该对象的信息和一个 键-》值 对映射表,该映射表包含了标记为可搜索的属性。
处理成组的未加载的资源的最简单方法是使用
ObjectLibrary
。ObjectLibrary
是一个对象,包含了一系列继承共享基类的未加载对象或者未加载对象的FAssetData 。您可以通过提供一个搜索路径来加载一个对象库,它将加载那个路径中的所有资源。这是非常有用的,因为您可以为不同类型指定您的部分内容文件夹,且 美术师/设计师 不需要手动编辑清单表就可以添加新的资源。这里是一个示例,展示了如何使用一个对象库来从磁盘加载AssetData:if (!ObjectLibrary) { ObjectLibrary = ConstructObject<UObjectLibrary>(UObjectLibrary::StaticClass()); ObjectLibrary->ObjectBaseClass = BaseClass; ObjectLibrary->UseWeakReferences(GIsEditor); ObjectLibrary->AddToRoot(); } ObjectLibrary->LoadAssetDataFromPath(TEXT("/Game/PathWithAllObjectsOfSameType"); if (bFullyLoad) { ObjectLibrary->LoadAssetsFromAssetData(); }
在这个示例中,它创建了一个新的对象库,关联了一个基类,然后加载了给定路径中的所有资源数据。接着选择性地加载真正的资源。如果资源很小,或者如果您正在进行烘焙且需要确保烘焙所有内容,那么您则需要完全地加载这些资源。只要您在烘焙过程中执行一次资源注册表查询,并加载返回的资源,您的对象库就可以正常地在烘焙数据中发挥作用,就像在开发过程中的处理一样。一旦把资源数据放入到了一个
ObjectLibrary
中,您可以进行查询并选择性地加载特定的资源。这里是示例,展示了如何进行查询:TArray<FAssetData> AssetDatas; ObjectLibrary->GetAssetDataList(AssetDatas); for (int32 i = 0; i < AssetDatas.Num(); ++i) { FAssetData& AssetData = AssetDatas[i]; const FString* FoundTypeNameString = AssetData.TagsAndValues.Find(GET_MEMBER_NAME_CHECKED(UAssetObject,TypeName)); if (FoundTypeNameString && FoundTypeNameString->Contains(TEXT("FooType"))) { return AssetData; } }
在这个示例中,它搜索对象库并查找任何具有
TypeName
文本域且该文本域中包含 "FooType" 的对象,然后返回第一次查找到的对象。一旦有了此AssetData
,您可以调用ToStringReference()
来把它转换成一个FStringAssetReference
,然后您可以使用下一个系统进行异步加载。StreamableManager(动态加载管理器)和异步加载
现在,您具有一个引用了磁盘上一个资源的
FStringAssetReference
,那么怎样真正地异步加载它哪? FStreamableManager是完成这个处理的最简单方法。首先,您需要创建一个FStreamableManager
,我建议你把它放到某个全局游戏单例对象中,比如在DefaultEngine.ini
中使用GameSingletonClassName
指定的对象。然后,您可以把StringAssetReference
传给该对象,并启动进行加载。SynchronousLoad
将会进行简单的、阻碍加载,并返回该对象。这种方法对于较小的对象可能很好,但是它可能会使您的主线程停顿时间过长。出现那种情况,您将需要使用RequestAsyncLoad
,它将会异步地加载一组资源,并在完成后调用一个代理。这里是一个示例:void UGameCheatManager::GrantItems() { TArray<FStringAssetReference> ItemsToStream; FStreamableManager& Streamable = UGameGlobals::Get().StreamableManager; for(int32 i = 0; i < ItemList.Num(); ++i) { ItemsToStream.AddUnique(ItemList[i].ToStringReference()); } Streamable.RequestAsyncLoad(ItemsToStream, FStreamableDelegate::CreateUObject(this, &UGameCheatManager::GrantItemsDeferred)); } void UGameCheatManager::GrantItemsDeferred() { for(int32 i = 0; i < ItemList.Num(); ++i) { UGameItemData* ItemData = ItemList[i].Get(); if(ItemData) { MyPC->GrantItem(ItemData); } } }
在这个示例中,ItemList 是一个
TArray< TAssetPtr<UGameItem> >
,由设计人员在编辑器中进行修改。这段代码迭代那个列表,将列表项转换为StringReferences
,并将它们进行排队。当所有列表项都加载后(或者由于缺失列表项而不能加载),它将在代理中调用已通过的相关处理。该代理然后迭代同样的列表项,区分它们,并把它们提供给一个玩家。StreamableManager
在调用该代理之前,将保持到任何它所加载的对象的强引用,所以在调用该代理之前,您可以确保您想异步加载的任何对象都没有被垃圾回收。当调用该代理后,StreamableManager
释放这些引用,所以您想确保它们一直存在,您需要在其他地方对它们进行强引用。您可以使用同样的方法来异步加载
FAssetDatas
,仅需在它们上面调用ToStringReference
,把它们添加到一个数组中,然后使用一个代理调用RequestAsyncLoad 。该代理可以是您想操作的任何内容,所以如果需要,你可以传入负载信息。通过把结合使用上述的方法,您应该可以设置一个高效加载游戏中的任何资源的系统。转换直接访问内存的游戏代码来处理异步加载将会花费一些时间,但是之后您的游戏停顿将变得非常少,并且内存使用量会更低。
-