【UE4的资源加载与释放】

说起资源加载,首先要说的是UE对资源的引用。

FSoftObjectPath&FsoftClassPath

UE游戏运行时通常需要加载内存浏览器的资源,FSoftObjectPath&FsoftClassPath代表对应资源的路径

FSoftObjectPath

    其中FSoftObjectPath可以指所有资源的路径,包括蓝图资源或者非蓝图资源。在C++中声明变量时,可使用Uproperty对资源类型进行限制,比如添加对应的meta方法。

UPROPERTY(EditAnywhere, meta = (AllowedClasses ="Material,StaticMesh"))
		FSoftObjectPath  softObjectPath;

FsoftClassPath只能指蓝图资源的路径。

使用方法就是调用Path的tostring方法来获取资源路径。

FSoftObjectPtr&TSoftObjectPtr&TSoftClassPtr

软对象指针,用来在异步加载资源完成触发回调函数的时候获取资源指针用的,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的资源加载

UE的资源加载分为同步加载和异步加载

同步加载

同步加载有两种API

  • FStreamableManager::LoadSynchronous
  • FStreamableManager::RequestSyncLoad

加载返回一个FStreamableHandle类型的指针=》Handle。同步加载内部其实是异步加载,调用FStreamableHandle::WaitUntilComplete()阻塞等待。

RequestSyncLoad函数内部要么会进行异步载入并且调用WaitUntilComplete函数,要么直接调用LoadObject函数 —— 哪个更快就调哪个。

异步加载

API

  • FStreamableManager::RequestAsyncLoad

异步加载结束可以设置回调函数,上文中也提到了用加载的回调函数来获得加载的资源对象指针。

目前异步加载无法用来加载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,除非重启编辑器,(等待验证)

注意

  • FStreamableManager::Unload()会Release掉和当前资源相关的所有FStreamableHandle。比如在三处位置加载了同一个资源,即使bManageActiveHandle设置为true,那么只要调用Unload一次,就可以将这三个FStreamableHandle对象全部Release掉,即从内存中释放该对象;如果对这3个FStreamableHandle对象分别执行Release,那么只有当最后一个Handle被Release之后,该资源才会从内存中释放。
  • 异步加载时,谁先请求则谁的回调函数先执行,不会存在回调函数执行顺序乱序的问题(除非修改TAsyncLoadPriority),因为引擎内部接收回调请求的容器使用的是TArray,且每次执行索引为0的回调,然后RemoveAt(0)。
  • 异步加载时,如果资源还没加载完成就执行ReleaseHandle()(假设加载时bManageActiveHandle为true),比如在执行回调函数之前执行ReleaseHandle,那么当资源加载完成后(回调函数执行之后),会自动从内存中回收。不过该对象在回调函数中仍然有效,除非在回调函数内ForceGC。
  • UPROPERTY()修饰的成员变量,可以让其保持的资源对象常驻内存,如果不再需要驻留内存,将该成员变量值为NULL,等到下次GC时就会被自动回收
  • GEngine->ForceGarbageCollection();执行后,内存回收至少要等到下一帧才会执行。在当前帧内,即使一个对象执行ConditionalBeginDestroy()且执行了ForceGarbageCollection,当前帧内该对象仍然有效。
  • ConditionalBeginDestroy()是所有UObject都有的API,其对象销毁是异步执行且对象在当前帧内持续有效;AActor::Destroy()是AActor特有的API,其对象回收发生在当前帧结束时。

参考:

https://zhuanlan.zhihu.com/p/33303645

https://blog.csdn.net/qq_29523119/article/details/84929384

项目相关的一些代码

你可能感兴趣的:(UE)