【UE4 C++ 基础知识】<11>资源的同步加载与异步加载
阅读原文时间:2023年07月09日阅读:1

同步加载

同步加载会造成进程阻塞。

在构造函数加载

  • ConstructorHelpers::FObjectFinder

  • ConstructorHelpers::FClassFinder

    参考 【UE4 C++ 基础知识】<10>资源的引用

  • 一般用来加载资源对象

    UMaterial* M_Cube = LoadObject(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(StaticLoadObject(USoundCue::StaticClass(), nullptr, TEXT("SoundCue'/Game/Demo_Drone/Sound/explore_Cue.explore_Cue'")));
    UGameplayStatics::PlaySoundAtLocation(this, soundCue,SweepResult.Location);

  • 一般用来加载蓝图类, UClass*

  • 蓝图类的路径末尾加上_C

    UClass* pClass = LoadClass(nullptr, TEXT("Blueprint'/Game/CPPFunction/Load/BP_LoadActor.BP_LoadActor_C'"));
    if (pClass)
    {
    UE_LOG(LogTemp, Warning, TEXT("pClass name:%s"), *pClass->GetName());
    }

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

  • 配合 FSoftObjectPath 使用

  • TryLoad 中调用 LoadObject,加载时需要调用Cast转换一下

    FSoftObjectPath SoftObjectPaths_Mesh = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
    UStaticMesh* Mesh1 = Cast(SoftObjectPaths_Mesh.TryLoad());
    if (Mesh1)
    {
    UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *Mesh1->GetName());
    }

  • 搭配 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();
    if (pClass_Actor)
    {
    UE_LOG(LogTemp, Warning, TEXT("pClass_Actor name:%s"), *pClass_Actor->GetName());
    }

  • FStreamableManager::内部调用 RequestSyncLoad

  • 参数中返回一个FStreamableHandle类型的指针

可加载非蓝图资源类

  • 配合FStreamableManager、FSoftObjectPath 使用

  • 配合FStreamableManager、TSoftObjectPtr使用

    // 配合 FSoftObjectPath 使用 方法一
    FSoftObjectPath SoftObjectPaths_Mesh1 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
    UStaticMesh* StaticMesh1 = UAssetManager::GetStreamableManager().LoadSynchronous(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(SoftObjectPaths_Mesh2);
    if (StaticMesh2)
    {
    UE_LOG(LogTemp, Warning, TEXT("Mesh2 name:%s"), *StaticMesh2->GetName());
    }

    // 配合 TSoftObjectPtr 使用
    FSoftObjectPath SoftObjectPaths_Mesh3 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh_Soft2.StaticMesh_Soft2'"));
    TSoftObjectPtr SoftObjectPtr_Mesh = TSoftObjectPtr(SoftObjectPaths_Mesh3);
    UStaticMesh* StaticMesh3 = streamableManager.LoadSynchronous(SoftObjectPtr_Mesh);//保持良好习惯
    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(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(SoftObjectPaths_Actor2);
    if (BPClass2)
    {
    UE_LOG(LogTemp, Warning, TEXT("BPClass2 name:%s"), *BPClass2->GetName());
    }

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

  • 配合 FStreamableManager、FSoftObjectPath 使用

  • 返回一个FStreamableHandle类型的指针

  • TSharedPtr 通过 GetLoadedAsset() 获取单个资源

  • TSharedPtr 通过 GetLoadedAssets() 获取多个资源

    // 获取单个资源 方法一
    FSoftObjectPath SoftObjectPaths_Mesh1 = FSoftObjectPath(TEXT("StaticMesh'/Game/CPPFunction/Load/StaticMesh.StaticMesh'"));
    TSharedPtr Handle1 = UAssetManager::GetStreamableManager().RequestSyncLoad(SoftObjectPaths_Mesh1);
    if (Handle1.IsValid())
    {
    UStaticMesh* StaticMesh1 = Cast(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 Handle2 = streamableManager.RequestSyncLoad(SoftObjectPaths_Mesh2);
    if (Handle2.IsValid())
    {
    UStaticMesh* StaticMesh2 = Cast(Handle2->GetLoadedAsset());
    UE_LOG(LogTemp, Warning, TEXT("Mesh1 name:%s"), *StaticMesh2->GetName());
    }

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


异步加载

  • 为了避免阻塞主线程,可以使用异步加载的方式来加载资源

  • 异步加载完成后,可以设置回调函数

  • 创建 FStreamableManager,建议将它放在某类全局游戏单件对象中,如使用GameSingletonClassNameDefaultEngine.ini 中指定的对象

  • 返回一个 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());
        }
    }

  • 多文件加载

    • 方法一 配合 FSoftObjectPtr

      • FSoftObjectPtr是一个结构体,是一种指向UObject的弱指针。无法在蓝图中使用

      • TSoftObjectPtr是一个模板类,是通用FSoftObjectPtr的模块化包装器

        UPROPERTY(EditAnywhere, Category="SoftObjectPath", meta = (AllowedClasses = "StaticMesh"))
        TArray 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
        UStaticMesh* mesh = Cast(SoftObjPtr.Get());
        if (mesh)
        {
        UE_LOG(LogTemp, Warning, TEXT("mesh name:%s"), *mesh->GetName());
        }
        }
        }

    • 方法二 配合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());
              }
          }
      }

异步加载蓝图类

  • 单文件加载 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 ItemPtr = TSoftClassPtr(SingleClassPath);
      UClass* ItemClass = ItemPtr.Get();
      if (ItemClass)
      {
      UE_LOG(LogTemp, Warning, TEXT("Actor name:%s"), *ItemClass->GetName());
      }
      }

卸载资源

  • 当对象失去饮用后会被自动释放。
  • 在异步回调结束后,对象会被标记可回收,此时使用 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 Handle1 = UAssetManager::GetStreamableManager().RequestSyncLoad(SoftObjectPaths_Mesh1);
    Handle1->ReleaseHandle();

使用 ConditionalBeginDestroy()

  • 编辑器模式下卸载后,对象从内存中销毁,无法再次Load,需要重启编辑器

    UMaterial* M_Cube = LoadObject(nullptr, TEXT("Material'/Game/Geometry/Meshes/CubeMaterial.CubeMaterial'"));
    if (M_Cube)
    {
    M_Cube->ConditionalBeginDestroy();
    M_Cube = nullptr;
    GetWorld()->ForceGarbageCollection();
    }

参考

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章