资源分类
这里我将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(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(this, *strBPFileName); if (pStaticMesh) { UE_LOG(LogTemp, Error, TEXT("Static Object name is %s"), *pStaticMesh->GetName()); }
打印结果:
FStreamableManager(异步加载和同步加载)
FStreamableManager提供了RequestSyncLoad (同步)和 RequestAsyncLoad(异步)的两个接口。
RequestSyncLoad和RequestAsyncLoad有点长,就不贴全了。
TSharedPtr FStreamableManager::RequestSyncLoad(const TArray& 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 Request = RequestAsyncLoad(TargetsToStream, FStreamableDelegate(), AsyncLoadHighPriority, bManageActiveHandle, false, DebugName);
TSharedPtr FStreamableManager::RequestAsyncLoad(const TArray& 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 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* RequestHandlePointer = nullptr) { return Cast(LoadSynchronous(Target, bManageActiveHandle, RequestHandlePointer) ); }
UObject* FStreamableManager::LoadSynchronous(const FSoftObjectPath& Target, bool bManageActiveHandle, TSharedPtr* RequestHandlePointer){ TSharedPtr 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(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(FSoftObjectPath(strBPFileName)); if (pClass) { UE_LOG(LogTemp, Error, TEXT("UClass name is %s"), *pClass->GetName()); }
当然同步加载也可以直接用UE4写好的接口,唯一和上面不同的是用了TSubclassOf和TSoftClassPtr,后面我会分析TSubclassOf和TSoftClassPtr的用法
template< typename T > TSubclassOf LoadSynchronous(const TSoftClassPtr& Target, bool bManageActiveHandle = false, TSharedPtr* RequestHandlePointer = nullptr) { TSubclassOf ReturnClass; ReturnClass = Cast(LoadSynchronous(Target.ToSoftObjectPath(), bManageActiveHandle, RequestHandlePointer)); return ReturnClass; }
RequestAsyncLoad(异步加载)
先看看简单版本的源码
TSharedPtr FStreamableManager::RequestAsyncLoad(const FSoftObjectPath& TargetToStream, FStreamableDelegate DelegateToCall, TAsyncLoadPriority Priority, bool bManageActiveHandle, bool bStartStalled, const FString& DebugName){ return RequestAsyncLoad(TArray{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(pObject); if (pStaticMesh) { UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pStaticMesh->GetName()); } }
更简约写法:
void ATestLoadObjectCharacter::LoadFinish(FSoftObjectPath meshFilePath){ TSoftObjectPtr MeshObjectPtr = TSoftObjectPtr(meshFilePath); UStaticMesh* pStaticMesh = MeshObjectPtr.Get(); if (pStaticMesh) { UE_LOG(LogTemp, Error, TEXT("UStaicMesh name is %s"), *pStaticMesh->GetName()); } }
值得一提的是,这里异步加载并非像之前的 LoadSynchronous 返回 UObject,而是得在加载的时候绑定一个委托FSteamableDelegate,在委托回调的时候进行对象的获取,这里对象获取是通过 TSoftObjectPtr 对 FSoftObjectPath 的构造。
TSoftObjectPtr MeshObjectPtr = TSoftObjectPtr(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 ActorClassPtr = TSoftClassPtr(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](https://docs.unrealengine.com/en-us/Programming/Assets/ReferencingAssets)