【UE4 C++ 基礎知識】<11>資源的同步加載與異步加載


同步加載

同步加載會造成進程阻塞。

FObjectFinder / FClassFinder

在構造函數加載

LoadObject

  • 一般用來加載資源對象
UMaterial* M_Cube = LoadObject<UMaterial>(nullptr, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.CubeMaterial'"));
if (M_Cube)
{
	UE_LOG(LogTemp, Warning, TEXT("Material name:%s"), *M_Cube->GetName());
}
  • 早期版本 StaticLoadObject() ,本處只作為記錄,推薦使用 LoadObject
soundCue = Cast<USoundCue>(StaticLoadObject(USoundCue::StaticClass(), nullptr, TEXT("SoundCue'/Game/Demo_Drone/Sound/explore_Cue.explore_Cue'")));
UGameplayStatics::PlaySoundAtLocation(this, soundCue,SweepResult.Location);

LoadClass

  • 一般用來加載藍圖類, UClass*
  • 藍圖類的路徑末尾加上_C
UClass* pClass = LoadClass<AActor>(nullptr, TEXT("Blueprint'/Game/CPPFunction/Load/BP_LoadActor.BP_LoadActor_C'"));
if (pClass)
{
	UE_LOG(LogTemp, Warning, TEXT("pClass name:%s"), *pClass->GetName());
}

TSubclassOf<AActor> BPClass = LoadClass<AActor>(nullptr, TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor'"));
if (BPClass)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass name:%s"), *BPClass->GetName());
}

TryLoad

  • 配合 FSoftObjectPath 使用
  • TryLoad 中調用 LoadObject,加載時需要調用Cast轉換一下
FSoftObjectPath SoftObjectPaths_Mesh = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
UStaticMesh* Mesh1 = Cast<UStaticMesh>(SoftObjectPaths_Mesh.TryLoad());
if (Mesh1)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *Mesh1->GetName());
}

TryLoadClass

  • 搭配 FSoftClassPath 使用
  • TryLoadClass 中調用了 LoadClass
FSoftClassPath SoftClassPath_Actor = FSoftClassPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor_SoftRef.BP_MyActor_SoftRef_C'"));
UClass* pClass_Actor = SoftClassPath_Actor.TryLoadClass<AActor>();
if (pClass_Actor)
{
	UE_LOG(LogTemp, Warning, TEXT("pClass_Actor name:%s"), *pClass_Actor->GetName());
}

FStreamableManager::LoadSynchronous

  • FStreamableManager::內部調用 RequestSyncLoad
  • 參數中返回一個FStreamableHandle類型的指針

可加載非藍圖資源類

  • 配合FStreamableManager、FSoftObjectPath 使用
  • 配合FStreamableManager、TSoftObjectPtr 使用
// 配合 FSoftObjectPath 使用 方法一
FSoftObjectPath SoftObjectPaths_Mesh1 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
UStaticMesh* StaticMesh1 = UAssetManager::GetStreamableManager().LoadSynchronous<UStaticMesh>(SoftObjectPaths_Mesh1);
if (StaticMesh1)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh1->GetName());
}

// 配合 FSoftObjectPath 使用 方法二
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FSoftObjectPath SoftObjectPaths_Mesh2 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft.StaticMesh_Soft'"));
UStaticMesh* StaticMesh2 = streamableManager.LoadSynchronous<UStaticMesh>(SoftObjectPaths_Mesh2);
if (StaticMesh2)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh2 name:%s"), *StaticMesh2->GetName());
}

// 配合 TSoftObjectPtr<T> 使用
FSoftObjectPath SoftObjectPaths_Mesh3 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft2.StaticMesh_Soft2'"));
TSoftObjectPtr<UStaticMesh> SoftObjectPtr_Mesh = TSoftObjectPtr<UStaticMesh>(SoftObjectPaths_Mesh3);
UStaticMesh* StaticMesh3 = streamableManager.LoadSynchronous(SoftObjectPtr_Mesh);//保持良好習慣<UStaticMesh>
if (StaticMesh3)
{
	UE_LOG(LogTemp, Warning, TEXT("Mesh3 name:%s"), *StaticMesh3->GetName());
}

也可加載藍圖類為 UClass*

  • 配合FStreamableManager、TSoftObjectPtr 使用
  • 配合FStreamableManager、TSoftClassPtr 使用
FSoftObjectPath SoftObjectPaths_Actor1 = FSoftObjectPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor.BP_MyActor_C'"));
UClass* BPClass1 = UAssetManager::GetStreamableManager().LoadSynchronous<UClass>(SoftObjectPaths_Actor1);
if (BPClass1)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass1 name:%s"), *BPClass1->GetName());
}

// 配合 FSoftObjectPath 使用 方法二
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FSoftObjectPath SoftObjectPaths_Actor2 = FSoftObjectPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor_SoftRef.BP_MyActor_SoftRef_C'"));
UClass* BPClass2 = streamableManager.LoadSynchronous<UClass>(SoftObjectPaths_Actor2);
if (BPClass2)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass2 name:%s"), *BPClass2->GetName());
}

// 配合 TSoftObjectPtr<T> 使用
FSoftObjectPath SoftObjectPaths_Actor3 = FSoftObjectPath(TEXT("Blueprint'/Game/CPPFunction/Load/BP_MyActor_SoftRef2.BP_MyActor_SoftRef2_C'"));
TSoftObjectPtr<UClass> SoftObjectPtr_Actor = TSoftObjectPtr<UClass>(SoftObjectPaths_Actor3);
UClass* BPClass3 = streamableManager.LoadSynchronous(SoftObjectPtr_Actor); //保持良好習慣可添加<UClass>
if (BPClass3)
{
	UE_LOG(LogTemp, Warning, TEXT("BPClass3 name:%s"), *BPClass3->GetName());
}

FStreamableManager::RequestSyncLoad

  • 配合 FStreamableManager、FSoftObjectPath 使用
  • 返回一個FStreamableHandle類型的指針
  • TSharedPtr 通過 GetLoadedAsset() 獲取單個資源
  • TSharedPtr 通過 GetLoadedAssets() 獲取多個資源
// 獲取單個資源 方法一
FSoftObjectPath SoftObjectPaths_Mesh1 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
TSharedPtr<FStreamableHandle> Handle1  = UAssetManager::GetStreamableManager().RequestSyncLoad(SoftObjectPaths_Mesh1);	
if (Handle1.IsValid())
{
	UStaticMesh* StaticMesh1 = Cast<UStaticMesh>(Handle1->GetLoadedAsset());
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh1->GetName());
}

// 獲取單個資源 方法二
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
FSoftObjectPath SoftObjectPaths_Mesh2 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft.StaticMesh_Soft'"));
TSharedPtr<FStreamableHandle> Handle2 = streamableManager.RequestSyncLoad(SoftObjectPaths_Mesh2);	
if (Handle2.IsValid())
{
	UStaticMesh* StaticMesh2 = Cast<UStaticMesh>(Handle2->GetLoadedAsset());
	UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh2->GetName());
}

// 獲取多個資源
TArray<FSoftObjectPath> SoftObjectPaths;
SoftObjectPaths.Add(SoftObjectPaths_Mesh1);
SoftObjectPaths.Add(SoftObjectPaths_Mesh2);
TSharedPtr<FStreamableHandle> Handle3 = streamableManager.RequestSyncLoad(SoftObjectPaths);	
{
	TArray<UObject*> Objects;
	Handle3->GetLoadedAssets(Objects);
	for (UObject* item : Objects)
	{
		UStaticMesh* StaticMesh3 = Cast<UStaticMesh>(item);
		UE_LOG(LogTemp, Warning, TEXT("GetLoadedAssets(), item name:%s"), *StaticMesh3->GetName());
	}
}

異步加載

  • 為了避免阻塞主線程,可以使用異步加載的方式來加載資源
  • 異步加載完成后,可以設置回調函數
  • 創建 FStreamableManager,建議將它放在某類全局游戲單件對象中,如使用GameSingletonClassNameDefaultEngine.ini 中指定的對象

FStreamableManager::RequestAsyncLoad

  • 返回一個 FStreamableHandle 類型的指針

異步加載非藍圖類資源 FSoftObjectPath

  • 單文件加載

    UPROPERTY(EditAnywhere, Category = "SoftObjectPath", meta = (AllowedClasses = "StaticMesh"))
    	FSoftObjectPath SingeleObject;
    
    UFUNCTION()
    	void OnSingleAssetLoadFinshed();
    
    // 函數內部分語句
    FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
    FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &ALoadActor::OnSingleAssetLoadFinshed);
    streamableManager.RequestAsyncLoad(SingeleObject, streamableDelegate);
    
    // 要回調的函數
    void ALoadActor::OnSingleAssetLoadFinshed()
    {
    	FSoftObjectPtr SoftObjPtr = FSoftObjectPtr(SingeleObject);
    	UStaticMesh* mesh = Cast<UStaticMesh>(SoftObjPtr.Get());
    	if (mesh)
    	{
    		UE_LOG(LogTemp, Warning, TEXT("mesh name:%s"), *mesh->GetName());
    	}
    }
    

    image

  • 多文件加載

    • 方法一 配合 FSoftObjectPtr

      • FSoftObjectPtr是一個結構體,是一種指向UObject的弱指針。無法在藍圖中使用
      • TSoftObjectPtr是一個模板類,是通用FSoftObjectPtr的模塊化包裝器
      UPROPERTY(EditAnywhere, Category="SoftObjectPath", meta = (AllowedClasses = "StaticMesh"))
      	TArray<FSoftObjectPath> ObjectList1;
      
      UFUNCTION()
      	void OnMultiAssetsLoadFinshed1();
      
      // 函數內部分語句
      FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
      FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &ALoadActor::OnMultiAssetsLoadFinshed1);
      streamableManager.RequestAsyncLoad(ObjectList1, streamableDelegate);
      
      // 要回調的函數
      void ALoadActor::OnMultiAssetsLoadFinshed1()
      {
      	for (auto AssetItem : ObjectList1)
      	{
      		FSoftObjectPtr SoftObjPtr = FSoftObjectPtr(AssetItem); //此處也可用 TSoftObjectPtr<T>
      		UStaticMesh* mesh = Cast<UStaticMesh>(SoftObjPtr.Get());
      		if (mesh)
      		{
      			UE_LOG(LogTemp, Warning, TEXT("mesh name:%s"), *mesh->GetName());
      		}
      	}
      }
      

      image

    • 方法二 配合TSoftObjectPtr<T>

      UPROPERTY(EditAnywhere, Category = "SoftObjectPath")
      	TArray<TSoftObjectPtr<UTexture2D>> ObjectList2;
      
      UFUNCTION()
      	void OnMultiAssetsLoadFinshed2();
      
      // 函數內部分語句
      FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
      FStreamableDelegate streamableDelegate;
      streamableDelegate.BindUObject(this, &ALoadActor::OnMultiAssetsLoadFinshed2);
      
      TArray<FSoftObjectPath> SoftPathList;
      for (int32 i=0; i<ObjectList2.Num(); i++)
      {
      	SoftPathList.Add(ObjectList2[i].ToSoftObjectPath());
      }
      streamableManager.RequestAsyncLoad(SoftPathList, streamableDelegate);
      
      // 要回調的函數
      void ALoadActor::OnMultiAssetsLoadFinshed2()
      {
      	for (auto AssetItem : ObjectList2)
      	{
      		UTexture2D* ItemTex = AssetItem.Get();
      		if (ItemTex)
      		{
      			UE_LOG(LogTemp, Warning, TEXT("Texture2D name:%s"), *ItemTex->GetName());
      		}
      	}
      }
      
      

      image

異步加載藍圖類

  • 單文件加載 FSoftClassPath 、TSoftClassPtr

    • 測試 TArray<FSoftClassPath> 加載多個藍圖類編譯不通過
    UPROPERTY(EditAnywhere, Category = "SoftClassPath", meta = (MetaClass = "Actor"))
    	FSoftClassPath SingleClassPath;
    
    UFUNCTION()
    	void OnSingleClassLoadFinshed();
    
    // 函數內部分語句
    FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
    FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &ALoadActor::OnSingleClassLoadFinshed);
    streamableManager.RequestAsyncLoad(SingleClassPath, streamableDelegate);
    
    // 函數內部分語句
    void ALoadActor::OnSingleClassLoadFinshed()
    {
    	TSoftClassPtr<AActor> ItemPtr = TSoftClassPtr<AActor>(SingleClassPath);
    	UClass* ItemClass = ItemPtr.Get();
    	if (ItemClass)
    	{
    		UE_LOG(LogTemp, Warning, TEXT("Actor name:%s"), *ItemClass->GetName());
    	}
    }
    

    image

卸載資源

自動回收

  • 當對象失去飲用后會被自動釋放。
  • 在異步回調結束后,對象會被標記可回收,此時使用 ForceGC 可銷毀對象

手動回收

使用 FStreamableManager::Unload()

  • LoadSynchronous()、RequestSyncLoad()、RequestAsyncLoad() 默認bManageActiveHandle 參數為false,表示自動管理內存;當設為true,表示常駐內存直到手動釋放
  • FStreamableManager::Unload()會Release掉和當前資源相關的所有FStreamableHandle
  • 編輯器模式下會一直常駐內存,打包版本中才會生效
FStreamableManager& streamableManager = UAssetManager::GetStreamableManager();
streamableManager.Unload(FSoftObjectPath(AssetPath));

//需要立即回收的話
GEngine->ForceGarbageCollection(true);
//GetWorld()->ForceGarbageCollection(true);

使用 FStreamableHandle::ReleaseHandle()

  • 異步加載時,如果資源還沒加載完成就執行ReleaseHandle()(假設加載時bManageActiveHandle為true),比如在執行回調函數之前執行ReleaseHandle,那么當資源加載完成后(回調函數執行之后),會自動從內存中回收。不過該對象在回調函數中仍然有效,除非在回調函數內ForceGC。
  • 編輯器模式下會一直常駐內存,打包版本中才會生效
TSharedPtr<FStreamableHandle> Handle1  = UAssetManager::GetStreamableManager().RequestSyncLoad(SoftObjectPaths_Mesh1);
Handle1->ReleaseHandle();

使用 ConditionalBeginDestroy()

  • 編輯器模式下卸載后,對象從內存中銷毀,無法再次Load,需要重啟編輯器
UMaterial* M_Cube = LoadObject<UMaterial>(nullptr, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.CubeMaterial'"));
if (M_Cube)
{
	M_Cube->ConditionalBeginDestroy();
	M_Cube = nullptr;
	GetWorld()->ForceGarbageCollection();
}

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM