草庐IT

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

飞起的猪 2023-12-23 原文

一、引用介绍

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

在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());
			}
		}
	}
}

结果:

异步加载单个资源

异步加载资源组

有关【虚幻引擎】UE4 同步和异步资源加载(软引用)的更多相关文章

  1. ruby-on-rails - 如何在 ruby​​ 中使用两个参数异步运行 exe? - 2

    exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby​​中使用两个参数异步运行exe吗?我已经尝试过ruby​​命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何ruby​​gems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除

  2. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  3. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  4. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  5. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

  6. ruby - 一个 YAML 对象可以引用另一个吗? - 2

    我想让一个yaml对象引用另一个,如下所示:intro:"Hello,dearuser."registration:$introThanksforregistering!new_message:$introYouhaveanewmessage!上面的语法只是它如何工作的一个例子(这也是它在thiscpanmodule中的工作方式。)我正在使用标准的ruby​​yaml解析器。这可能吗? 最佳答案 一些yaml对象确实引用了其他对象:irb>require'yaml'#=>trueirb>str="hello"#=>"hello"ir

  7. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  8. ruby-on-rails - Rails 3,嵌套资源,没有路由匹配 [PUT] - 2

    我真的为这个而疯狂。我一直在搜索答案并尝试我找到的所有内容,包括相关问题和stackoverflow上的答案,但仍然无法正常工作。我正在使用嵌套资源,但无法使表单正常工作。我总是遇到错误,例如没有路线匹配[PUT]"/galleries/1/photos"表格在这里:/galleries/1/photos/1/edit路线.rbresources:galleriesdoresources:photosendresources:galleriesresources:photos照片Controller.rbdefnew@gallery=Gallery.find(params[:galle

  9. ruby-on-rails - Rails 中的推荐引擎 - 2

    我想为我的Rails网络应用程序提供推荐功能。特别是,我想向新注册的用户推荐他可能想要关注的其他用户。Rails中是否有用于此目的的引擎/gem?如果没有,我应该从哪里开始构建它?谢谢。 最佳答案 有Coletivogemhttps://github.com/diogenes/coletivo我试了一下。在MySQL上运行。Neo4jhttp://neo4j.org真的很容易实现一个“跟随谁”。事实上,大多数展示其能力的样本都涉及“跟随谁”。快速提示-只有在JRuby上运行时,Neo4j.rb才会很酷。如果不是-使用Neograph

  10. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

随机推荐