【虚幻引擎】UE4 同步和异步资源加载(软引用)

一、引用介绍

虚幻引擎给我们提供了两种引用,一个是硬引用。软引用通常是仅储存资源对象的资源路径没有与资源产生耦合关系的引用(软引用加载到内存中,引用对象不会被加载到内存中,只有在需要的时候才会被加载进内存中)。硬引用则是拥有资源对象实际成员变量,直接与资源对象产生耦合(硬引用被加载到内存中,则被引用的对象资源也会被加载到内存中)。

在UE4开发中经常性需要获取一些资源(StaticMesh,Material,Particle,Datatable, Actor蓝图,各种继承UObject的蓝图等等)的路径,然后利用这些路径进行资源的加载。

蓝图类资源,也就是BlueprintClass,继承于UObject并且蓝图化的资源。

 非蓝图类资源:UTexture,UStaticMesh,UParticleSystem,UMaterialInterface这些资源:如纹理,粒子,静态网格,材质等等。

 二、软引用

在UE4中,我们常用的软引用有以下四种 FSoftObjectPath、FSoftClassPath、FSoftObjectPtr、TSubclassOf 这四个。

FSoftObjectPath:翻译成“软对象路径”,也就是在一个(UObject,UStruct,Actor,ActorCompoennt)对象中配置一个具体资源(非蓝图资源和蓝图资源都可以)的路径,当这个对象被加载的时候,FSoftObjectPath指向的资源未被加载,仅仅是提供了一个路径。

蓝图中的应用:


	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ftr")
		FSoftObjectPath AssetObjectPath;

如果我们需要某些特定的资源路径,可以使用元数据,meta = (AllowedClasses ="Material,StaticMesh"),他就只会选择你设置的类型 ,

注意1:FSoftObjectPath用AllowedClasses 只能筛选"Material,StaticMesh,Particle等资源

注意2:Material,StaticMesh类型之间不能有空格

 FSoftClassPath:对蓝图资源的一种弱引用,类似FSoftObjectPath,继承自FSoftObjectPath,可以说是FSoftObjectPath的进一步封装,不过这里是蓝图资源,指向了蓝图资源的路径,通过路径我们可以手动加载。

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ftr")
		FSoftClassPath AssetClassPath;

本质是FSoftClassPath指向的资源的路径为FSoftObjectPath的指向资源路径的子集 

TSoftObjectPtr:软对象指针用于在异步加载并且资源加载完成触发回调函数的时候获取资源对应的对象指针用的,毕竟异步加载是无法直接获取对象指针的。

//软引用获得资源对象的指针,仅储存资源对象的资源路径没有与资源产生耦合关系的引用
   //(软引用加载到内存中,引用对象不会被加载到内存中,只有在需要的时候才会被加载进内存中)
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ptr")
		TSoftObjectPtr<AActor> softActorobj;

 TSoftClassPtr:检测蓝图资源加载,获取蓝图资源对应的指针

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Ptr")
		TSoftClassPtr<AActor> softActorClass;

同样的可以直接用TSoftObjectPtr来获取UClass的指针,因为UClass本身就是UObject的子类。

三、同步加载

在UE4中使用同步加载RequestSyncLoad和异步加载RequestASyncLoad,就要提到一个类FStreamableManager,用于管理流资产并将其保存在内存中的本机类。

第一种同步加载的方式RequestSyncLoad

源码分析:

TSharedPtr<FStreamableHandle> FStreamableManager::RequestSyncLoad(const FSoftObjectPath& TargetToStream, bool bManageActiveHandle, FString DebugName)
{
	return RequestSyncLoad(TArray<FSoftObjectPath>{TargetToStream}, bManageActiveHandle, MoveTemp(DebugName));
}

参数解释:

  • TargetsToStream要加载的资产磁盘。
  • bManageActiveHandle如果为true,管理器将保持流句柄活动,直到显式释放。
  • DebugName此句柄的名称,将在调试工具中报告。

测试案例介绍:

FSoftObjectPath Path2 = FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_M.T_Brick_Clay_Beveled_M'"));
	//注意:在资源未完成加载之前代码会在这一行暂停运行以等待资源加载完成。
	TSharedPtr<FStreamableHandle> SyncStreamableHandle2 = UAssetManager::GetStreamableManager().RequestSyncLoad(Path2);
	if (SyncStreamableHandle2)
	{
		//使用RequestSyncLoad(方法加载单个资源的时候要用GetLoadedAsset来获得FStreamableHandle中返回的资源。
		UTexture2D* Image2 = Cast<UTexture2D>(SyncStreamableHandle2->GetLoadedAsset());
		if (Image2)
		{
			UE_LOG(LogTemp, Warning, TEXT("Image2 is %s"), *Image2->GetName());
		}
	}

注意:资源的引用在Content Browser中右键该资产,再选择Copye Reference即可复制路径

结果:

同步加载资源组:

源码分析:

/** 
	 * Synchronously load a set of assets, and return a handle. This can be very slow and may stall the game thread for several seconds.
	 * 
	 * @param TargetsToStream		Assets to load off disk
	 * @param bManageActiveHandle	If true, the manager will keep the streamable handle active until explicitly released
	 * @param DebugName				Name of this handle, will be reported in debug tools
	 */
TSharedPtr<FStreamableHandle> RequestSyncLoad(TArray<FSoftObjectPath> TargetsToStream, bool bManageActiveHandle = false, FString DebugName = TEXT("RequestSyncLoad Array"));

参数解释:

  • TargetsToStream要加载的资产磁盘。
  • bManageActiveHandle如果为true,管理器将保持流句柄活动,直到显式释放。
  • DebugName此句柄的名称,将在调试工具中报告。

案例测试: 

//同步加载资源组
	TArray<FSoftObjectPath> Paths;
	Paths.AddUnique(FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_N.T_Brick_Clay_Beveled_N'")));
	Paths.AddUnique(FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_New_D.T_Brick_Clay_New_D'")));
	//注意:在资源未完成加载之前代码会在这一行暂停运行以等待资源加载完成。
	TSharedPtr<FStreamableHandle> SyncStreamableHandle3 = UAssetManager::GetStreamableManager().RequestSyncLoad(Paths);
	if (SyncStreamableHandle3)
	{
		TArray<UObject*>LoadedAssets;
		SyncStreamableHandle3->GetLoadedAssets(LoadedAssets);
		if (LoadedAssets.Num() > 0)
		{
			for (int32 i = 0; i < LoadedAssets.Num(); i++)
			{
				UTexture2D* Image3 = Cast<UTexture2D>(LoadedAssets[i]);
				if (Image3)
				{
					UE_LOG(LogTemp, Warning, TEXT("Image3 is %s"), *Image3->GetName());
				}
			}
		}
	}

注意,使用这个方法加载单个资源的时候要用GetLoadedAsset来获得FStreamableHandle中返回的资源。 

结果:

同步加载单个资源LoadSynchronous ,实际上是对RequestSyncLoad的一层封装

源码分析:

UObject* LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr);

	/** Typed wrappers */
	template< typename T >
	T* LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr)
	{
		return Cast<T>(LoadSynchronous(Target, bManageActiveHandle, RequestHandlePointer) );
	}

	template< typename T >
	T* LoadSynchronous(const TSoftObjectPtr<T>& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr)
	{
		return Cast<T>(LoadSynchronous(Target.ToSoftObjectPath(), bManageActiveHandle, RequestHandlePointer));
	}

案例测试:

//同步加载单个资源LoadSynchronous
	FSoftObjectPath Path =FString(TEXT("Texture2D'/Game/StarterContent/Textures/T_Brick_Clay_Beveled_D.T_Brick_Clay_Beveled_D'"));
	UTexture2D* Image = UAssetManager::GetStreamableManager().LoadSynchronous<UTexture2D>(Path,false,nullptr);
	if (Image)
	{
		UE_LOG(LogTemp,Warning,TEXT("Image is %s"),*Image->GetName());
	}

结果:

注意:该方法或许适用于较小对象,但可能会导致主线程长时间停滞。在这种情况下,您将需要使用RequestAsyncLoad,它将异步加载一组资源并在完成后调用委托。 

四、异步加载 

UE4异步加载RequestAsyncLoad

源码分析:

/** 
	 * This is the primary streamable operation. Requests streaming of one or more target objects. When complete, a delegate function is called. Returns a Streamable Handle.
	 *
	 * @param TargetsToStream		Assets to load off disk
	 * @param DelegateToCall		Delegate to call when load finishes. Will be called on the next tick if asset is already loaded, or many seconds later
	 * @param Priority				Priority to pass to the streaming system, higher priority will be loaded first
	 * @param bManageActiveHandle	If true, the manager will keep the streamable handle active until explicitly released
	 * @param bStartStalled			If true, the handle will start in a stalled state and will not attempt to actually async load until StartStalledHandle is called on it
	 * @param DebugName				Name of this handle, will be reported in debug tools
	 */
	TSharedPtr<FStreamableHandle> RequestAsyncLoad(TArray<FSoftObjectPath> TargetsToStream, FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, FString DebugName = TEXT("RequestAsyncLoad ArrayDelegate"));
	TSharedPtr<FStreamableHandle> RequestAsyncLoad(const FSoftObjectPath& TargetToStream, FStreamableDelegate DelegateToCall = FStreamableDelegate(), TAsyncLoadPriority Priority = DefaultAsyncLoadPriority, bool bManageActiveHandle = false, bool bStartStalled = false, FString DebugName = TEXT("RequestAsyncLoad SingleDelegate"));

参数解释:

  • TargetsToStream要加载的资产磁盘。

DelegateToCall委托在加载完成时调用。将被调用在下一个Tick,如果资产已加载,或许多秒后。

  • Priority优先级传递给流系统,优先级高的将首先加载。
  • bManageActiveHandle如果为true,管理器将保持流句柄活动,直到显式释放。
  • bStartStalled如果为true,句柄将以停滞状态启动,并且在调用StartStalledHandle之前不会尝试实际异步加载。
  • DebugName此句柄的名称,将在调试工具中报告。

案例测试

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Test")
		TArray<TSoftObjectPtr<UTexture2D>> ObjectPtrs;

	TSharedPtr<FStreamableHandle> ASyncStreamableHandle2;
	void AsyncLoadCompleted();

蓝图资源配置

//异步加载单个资源
	FSoftObjectPath Path4 = TEXT("Texture2D'/Game/StarterContent/Textures/T_Ceramic_Tile_M.T_Ceramic_Tile_M'");
	TSharedPtr<FStreamableHandle> ASyncStreamableHandle = UAssetManager::GetStreamableManager().RequestAsyncLoad(Path4);
	if (ASyncStreamableHandle)
	{
		UTexture2D* Image4 = Cast<UTexture2D>(ASyncStreamableHandle->GetLoadedAsset());
		if (Image4)
		{
			UE_LOG(LogTemp, Warning, TEXT("Image4 is %s"), *Image4->GetName());
		}
	}
	//异步加载资源组
	TArray<FSoftObjectPath>Paths2;
	for (auto item:ObjectPtrs)
	{
		Paths2.AddUnique(item.ToSoftObjectPath());//ToSoftObjectPath()返回的是一个智能指针
	}
	ASyncStreamableHandle2 = UAssetManager::GetStreamableManager().RequestAsyncLoad(Paths2, FStreamableDelegate::CreateUObject(this, &AMyActor::AsyncLoadCompleted));
}



void AMyActor::AsyncLoadCompleted()
{
	if (ASyncStreamableHandle2)
	{
		TArray<UObject*>ObjectArray;
		ASyncStreamableHandle2->GetLoadedAssets(ObjectArray);
		if (ObjectArray.Num()>0)
		{
			for (int i = 0; i < ObjectArray.Num(); i++)
			{
				UTexture2D* Image5 = Cast<UTexture2D>(ObjectArray[i]);
				UE_LOG(LogTemp, Warning, TEXT("Image5 is %s"), *Image5->GetName());
			}
		}
	}
}

结果:

异步加载单个资源

异步加载资源组

猜你喜欢

转载自blog.csdn.net/qq_43021038/article/details/129661360