说起资源加载,首先要说的是UE对资源的引用。
UE游戏运行时通常需要加载内存浏览器的资源,FSoftObjectPath&FsoftClassPath代表对应资源的路径
其中FSoftObjectPath可以指所有资源的路径,包括蓝图资源或者非蓝图资源。在C++中声明变量时,可使用Uproperty对资源类型进行限制,比如添加对应的meta方法。
UPROPERTY(EditAnywhere, meta = (AllowedClasses ="Material,StaticMesh"))
FSoftObjectPath softObjectPath;
FsoftClassPath只能指蓝图资源的路径。
使用方法就是调用Path的tostring方法来获取资源路径。
软对象指针,用来在异步加载资源完成触发回调函数的时候获取资源指针用的,UE的异步加载无法直接获得对象指针。异步加载的函数后面再说。
void ATestLoadObjectCharacter::BeginPlay()
{
Super::BeginPlay();
FStreamableManager streamableManager;
FString strMeshFileName = "/Game/Geometry/Meshes/1M_Cube.1M_Cube";
FStreamableDelegate streamableDelegate;
FSoftObjectPath strMeshObjectFileName = FSoftObjectPath(strMeshFileName);
streamableDelegate.BindUObject(this, &ThisClass::LoadFinish, strMeshObjectFileName);
streamableManager.RequestAsyncLoad(strMeshObjectFileName, streamableDelegate);
}
void ATestLoadObjectCharacter::LoadFinish(FSoftObjectPath meshFilePath)
{
FSoftObjectPtr meshObjectPtr = FSoftObjectPtr(meshFilePath);
UObject* pObject = meshObjectPtr.Get();
if (nullptr == pObject)
return;
UStaticMesh* pStaticMesh = Cast(pObject);
if (pStaticMesh)
{
UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pStaticMesh->GetName());
}
}
TSoftObjectPtr是封装了FSoftObjectPtr的模板,用于检测文件路径下的资源是否已经加载进了内存,获取资源指针。不同之处就是不需要指针再进行一次Cast类型转换操作。
TSoftClassPtr和Path变量命名方式类似,用于检测蓝图资源加载的对象。
void AMyProject7Character::BeginPlay()
{
Super::BeginPlay();
FStreamableManager streamableManager;
FString strBPClassPath = "/Game/testActor.testActor_C";
FStreamableDelegate streamableDelegate;
FSoftClassPath SoftBPClassPathName = FSoftClassPath(strBPClassPath);
streamableDelegate.BindUObject(this, &ThisClass::LoadFinish, SoftBPClassPathName);
streamableManager.RequestAsyncLoad(SoftBPClassPathName, streamableDelegate);
}
void AMyProject7Character::LoadFinish(FSoftClassPath SoftBPClassPathName)
{
TSoftClassPtr ActorClassPtr = TSoftClassPtr(SoftBPClassPathName);
UClass* pClass = ActorClassPtr.Get();
if (pClass)
{
UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pClass->GetName());
}
}
UE的资源加载分为同步加载和异步加载
同步加载有两种API
加载返回一个FStreamableHandle类型的指针=》Handle。同步加载内部其实是异步加载,调用FStreamableHandle::WaitUntilComplete()阻塞等待。
RequestSyncLoad函数内部要么会进行异步载入并且调用WaitUntilComplete函数,要么直接调用LoadObject函数 —— 哪个更快就调哪个。
API
异步加载结束可以设置回调函数,上文中也提到了用加载的回调函数来获得加载的资源对象指针。
目前异步加载无法用来加载UserWidget资源。
StreamManager加载蓝图时,不能使用默认路径(即使后缀加_C),否则加载出来的Class无法使用,虽然不为空。
正确方式:前缀统一用Class,然后后缀再加_C。
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
UClass* WidgetClass = AssetLoader.LoadSynchronous(FSoftObjectPath("Class'/Game/TopDownCPP/Blueprints/NewWidgetBlueprint.NewWidgetBlueprint_C'"));
if (WidgetClass)
{
UUserWidget* Widget = CreateWidget(this, WidgetClass);
if (Widget)
{
Widget->AddToViewport();
}
}
用上面的方式加载资源以后,如果对象失去引用,就会被自动释放,如果是异步加载,对象只在回调函数中有效,回调函数执行完毕就会被标记为可回收。那么如何保存异步加载的资源呢?
在项目里看到并没有回调中保存加载的对象,而是保存了异步资源加载的Handle,通过Handle来获得的GetLoadedAssets来获得加载的对象。在保存到对象池之后再释放Handle。
在加载资源的时候如果讲bManageActiveHandle设置为true,对象会一直常驻内存直到手动释放。
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
UParticleSystem* AimObj = AssetLoader.LoadSynchronous(FSoftObjectPath(AssetPath), true);
如果对象不需要,手动执行unload
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
AssetLoader.Unload(FSoftObjectPath(AssetPath));
在编辑器模式里,上述两种方式都无法生效,只有打包版本才可以。如果在编辑器模式下强制Destroy或者MarkPendingKill(),可以把对象从内存销毁,但是也无法再次Load,除非重启编辑器,(等待验证)
参考:
https://zhuanlan.zhihu.com/p/33303645
https://blog.csdn.net/qq_29523119/article/details/84929384
项目相关的一些代码