(UE4 4.20)UE4同步加载和异步加载UObject ----------LoadObject,LoadClass,FStreamableManager

资源分类

这里我将UE4资源划分为两种:

蓝图类资源,也就是BlueprintClass,继承于UObject并且蓝图化的资源,如下所示:

 

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

LoadClass (同步加载蓝图类资源为UClass*)

LoadClass函数在UObjectGlobal.h里,源码为

// Load a class object.
template< class T > 
inline UClass* LoadClass( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
	return StaticLoadClass( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}

第二个参数 “Name” 就是相应资源的路径, 这里试验加载一个AActor的蓝图类

	FString strBPFileName = "/Game/ThirdPersonCPP/Blueprints/TestActor.TestActor_C";
	UClass* pClass = LoadClass<AActor>(this, *strBPFileName);
	if (pClass)
	{
		UE_LOG(LogTemp, Error, TEXT("UClass name is %s"), *pClass->GetName());
	}

得非常注意的是蓝图类资源名最后加了 “_C”

打印结果:

 

 

LoadObject(同步加载非蓝图类资源)

LoadObject函数在UObjectGlobal.h里,源码为

// Load an object.
template< class T > 
inline T* LoadObject( UObject* Outer, const TCHAR* Name, const TCHAR* Filename=nullptr, uint32 LoadFlags=LOAD_None, UPackageMap* Sandbox=nullptr )
{
	return (T*)StaticLoadObject( T::StaticClass(), Outer, Name, Filename, LoadFlags, Sandbox );
}

第二个参数 “Name” 就是相应资源的路径,这里试验加载一个StaticMesh资源

	FString strMeshFileName = "/Game/Geometry/Meshes/1M_Cube.1M_Cube";
	UStaticMesh* pStaticMesh = LoadObject<UStaticMesh>(this, *strBPFileName);
	if (pStaticMesh)
	{
		UE_LOG(LogTemp, Error, TEXT("Static Object name is %s"), *pStaticMesh->GetName());
	}

打印结果:

 

FStreamableManager(异步加载和同步加载)

FStreamableManager提供了RequestSyncLoad (同步)和 RequestAsyncLoad(异步)的两个接口。

RequestSyncLoad和RequestAsyncLoad有点长,就不贴全了。

TSharedPtr<FStreamableHandle> FStreamableManager::RequestSyncLoad(const TArray<FSoftObjectPath>& TargetsToStream, bool bManageActiveHandle, const FString& DebugName)
{
	// If in async loading thread or from callback always do sync as recursive tick is unsafe
	// If in EDL always do sync as EDL internally avoids flushing
	// Otherwise, only do a sync load if there are no background sync loads, this is faster but will cause a sync flush
	bForceSynchronousLoads = IsInAsyncLoadingThread() || IsEventDrivenLoaderEnabled() || !IsAsyncLoading();

	// Do an async load and wait to complete. In some cases this will do a sync load due to safety issues
	TSharedPtr<FStreamableHandle> Request = RequestAsyncLoad(TargetsToStream, FStreamableDelegate(), AsyncLoadHighPriority, bManageActiveHandle, false, DebugName);

 

TSharedPtr<FStreamableHandle> FStreamableManager::RequestAsyncLoad(const TArray<FSoftObjectPath>& TargetsToStream, FStreamableDelegate DelegateToCall, TAsyncLoadPriority Priority, bool bManageActiveHandle, bool bStartStalled, const FString& DebugName)
{
	// Schedule a new callback, this will get called when all related async loads are completed
	TSharedRef<FStreamableHandle> NewRequest = MakeShareable(new FStreamableHandle());
	NewRequest->CompleteDelegate = DelegateToCall;
	NewRequest->OwningManager = this;
	NewRequest->RequestedAssets = TargetsToStream;
	NewRequest->DebugName = DebugName;
	NewRequest->Priority = Priority;

LoadSynchronous (同步加载)

为了加载对象的方便,UE4利用RequestSyncLoad实现了LoadSynchronous接口:

/** Typed wrappers */
	template< typename T >
	T* LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle = false, TSharedPtr<FStreamableHandle>* RequestHandlePointer = nullptr)
	{
		return Cast<T>(LoadSynchronous(Target, bManageActiveHandle, RequestHandlePointer) );
	}
UObject* FStreamableManager::LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle, TSharedPtr<FStreamableHandle>* RequestHandlePointer)
{
	TSharedPtr<FStreamableHandle> Request = RequestSyncLoad(Target, bManageActiveHandle, FString::Printf(TEXT("LoadSynchronous of %s"), *Target.ToString()));

	if (RequestHandlePointer)
	{
		(*RequestHandlePointer) = Request;
	}

	if (Request.IsValid())
	{
		UObject* Result = Request->GetLoadedAsset();

		if (!Result)
		{
			UE_LOG(LogStreamableManager, Verbose, TEXT("LoadSynchronous failed for load of %s! File is missing or there is a loading system problem"), *Target.ToString());
		}

		return Result;
	}

	return nullptr;
}

同步加载UStaticMesh(非蓝图资源类)

	FStreamableManager streamableManager;

	FString strMeshFileName = "/Game/Geometry/Meshes/1M_Cube.1M_Cube";
	UStaticMesh* pStaticMesh = streamableManager.LoadSynchronous<UStaticMesh>(FSoftObjectPath(strMeshFileName));
	if (pStaticMesh)
	{
		UE_LOG(LogTemp, Error, TEXT("Static Object name is %s"), *pStaticMesh->GetName());
	}

 

同步加载蓝图类为Class

	FStreamableManager streamableManager;
	FString strBPFileName = "/Game/ThirdPersonCPP/Blueprints/TestActor.TestActor_C";
	UClass* pClass = streamableManager.LoadSynchronous<UClass>(FSoftObjectPath(strBPFileName));
	if (pClass)
	{
		UE_LOG(LogTemp, Error, TEXT("UClass name is %s"), *pClass->GetName());
	}

当然同步加载也可以直接用UE4写好的接口,唯一和上面不同的是用了TSubclassOf和TSoftClassPtr,后面我会分析TSubclassOf和TSoftClassPtr的用法

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

 

RequestAsyncLoad(异步加载)

先看看简单版本的源码

TSharedPtr<FStreamableHandle> FStreamableManager::RequestAsyncLoad(const FSoftObjectPath& TargetToStream, FStreamableDelegate DelegateToCall, TAsyncLoadPriority Priority, bool bManageActiveHandle, bool bStartStalled, const FString& DebugName)
{
	return RequestAsyncLoad(TArray<FSoftObjectPath>{TargetToStream}, DelegateToCall, Priority, bManageActiveHandle, bStartStalled, DebugName);
}

异步加载UObject

这里以加载一个UStaticMesh为案例

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<UStaticMesh>(pObject);
	if (pStaticMesh)
	{
		UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pStaticMesh->GetName());
	}

}

更简约写法:


void ATestLoadObjectCharacter::LoadFinish(FSoftObjectPath meshFilePath)
{
	TSoftObjectPtr<UStaticMesh> MeshObjectPtr = TSoftObjectPtr<UStaticMesh>(meshFilePath);

	UStaticMesh* pStaticMesh = MeshObjectPtr.Get();
	if (pStaticMesh)
	{
		UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pStaticMesh->GetName());
	}

}

值得一提的是,这里异步加载并非像之前的 LoadSynchronous 返回 UObject,而是得在加载的时候绑定一个委托FSteamableDelegate,在委托回调的时候进行对象的获取,这里对象获取是通过 TSoftObjectPtr FSoftObjectPath 的构造。

TSoftObjectPtr<UStaticMesh> MeshObjectPtr = TSoftObjectPtr<UStaticMesh>(meshFilePath);

	UStaticMesh* pStaticMesh = MeshObjectPtr.Get();

 

运行结果:

异步加载蓝图为UClass

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<AActor> ActorClassPtr = TSoftClassPtr<AActor>(SoftBPClassPathName);
		UClass* pClass = ActorClassPtr.Get();
		if (pClass)
		{
			UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pClass->GetName());
		}
}

其实UClass是UObject的子类,所以同步异步都可以用同一套。

参考资料:【1】http://api.unrealengine.com/INT/Programming/Assets/AsyncLoading/index.html

                    【2】https://docs.unrealengine.com/en-us/Programming/Assets/ReferencingAssets

 

  • 14
    点赞
  • 44
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UE4中,异步加载关卡可以使用场景流送机制。场景流送机制是一种将场景分为多个部分并按需加载的技术,可以提高游戏中的性能和效率。 在使用场景流送机制时,需要将场景分为多个Level,每个Level都是一个独立的场景,可以单独进行加载和卸载。在游戏运行时,可以使用Level Streaming Volume将场景分为多个区域,并根据玩家的位置动态地加载和卸载Level。 Level Streaming Volume是一种特殊的Volume,可以在场景中创建。它用于将场景分为多个区域,并在不同的区域之间进行场景的流送。 在使用Level Streaming Volume时,需要先创建一个Streaming Level,在Streaming Level中添加需要加载的Level,然后将Streaming Level添加到Level Streaming Volume中。当玩家进入Level Streaming Volume所在的区域时,相应的Streaming Level就会被加载,当玩家离开区域时,Streaming Level就会被卸载。 在代码中,可以使用以下函数来加载和卸载Level: ``` ULevelStreaming* UGameplayStatics::LoadStreamLevel( UObject* WorldContextObject, const FName& LevelName, bool bMakeVisibleAfterLoad, bool bShouldBlockOnLoad ); void UGameplayStatics::UnloadStreamLevel( UObject* WorldContextObject, const FName& LevelName, bool bShouldBlockOnUnload ); ``` 在加载Level时,可以选择是否在加载完成后立即显示Level,是否在加载过程中阻塞游戏等待加载完成。在卸载Level时,可以选择是否在卸载完成后立即终止游戏等待卸载完成。 使用场景流送机制可以提高游戏的性能和效率,并可以实现异步加载关卡的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值