UE 资源加载

UE 资源加载

资源引用一般分为资源硬引用和软引用

硬引用:对象 A 引用对象 B,A 加载到内存时B也加载,会导致内存使用量迅速增加。构造时引用:是硬应用的1种,在构造时加载资产并赋给变量。

软引用:仅储存资源对象的资源路径(本质是关联到硬盘上的网络链接),加载A时B不会主动加载到内存中,按需加载。

1
2
3
4
5
6
7
//硬引用(直接引用)
UStaticMeshComponent* StaticMeshComp;
//软引用
FSoftObjectPath softObectPath; //软对象路径
FSoftClassPath SoftClassPath; //软类路径
TSoftObjectPtr<AActor> SoftObjectPtr; //软对象引用
TSoftClassPtr<AActor> SoftCalssPtr; //软类引用

LoadObject

一般用来加载资源对象

1
2
3
4
5
6
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());
}

LoadClass

  • 一般用来加载蓝图类, UClass
  • 蓝图类的路径末尾加上_C
1
2
3
4
5
6
7
8
9
10
11
12
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转换一下
1
2
3
4
5
6
7
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
1
2
3
4
5
6
7
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使用
1
2
3
4
5
6
7
8
9
// 配合 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());
}


也可加载蓝图类为 UClass

  • 配合FStreamableManager、TSoftObjectPtr使用
  • 配合FStreamableManager、TSoftClassPtr使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
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() 获取多个资源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//加载蓝图并创建
void AG_LoadHelper::OnLoadSync()
{
FSoftObjectPath actorPath = FSoftObjectPath("Blueprint'/Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C'");
TSharedPtr<FStreamableHandle> handle = UAssetManager::Get().GetStreamableManager().RequestSyncLoad(actorPath);
if (handle&&handle->GetLoadedAsset())
{
UClass *acotrClass = Cast<UClass>(handle->GetLoadedAsset());
GetWorld()->SpawnActor<AActor>(acotrClass,FVector(0,0,300),FRotator(0,0,0));
UE_LOG(LogTemp,Warning,TEXT("OnLoadSync:%s"),*handle->GetLoadedAsset()->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需要配置成对应字段
UStaticMesh* StaticMesh3 = Cast<UStaticMesh>(item);
UE_LOG(LogTemp, Warning, TEXT("GetLoadedAssets(), item name:%s"), *StaticMesh3->GetName());
}
}

异步加载

  • 为了避免阻塞主线程,可以使用异步加载的方式来加载资源
  • 异步加载完成后,可以设置回调函数
  • 创建 FStreamableManager,建议将它放在某类全局游戏单件对象中,如使用GameSingletonClassNameDefaultEngine.ini 中指定的对象

FStreamableManager::RequestAsyncLoad

  • 返回一个 FStreamableHandle 类型的指针

异步加载非蓝图类资源 FSoftObjectPath

  • 单文件加载
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//异步加载蓝图并创建
void AG_LoadHelper::OnLoadASync()
{
FSoftObjectPath onloadPath = FSoftObjectPath("Blueprint'/Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C'");
FStreamableDelegate hanlde = FStreamableDelegate::CreateLambda([onloadPath]
{
FSoftObjectPtr SoftObjectPtr = FSoftObjectPtr(onloadPath);
UObject *Obj = SoftObjectPtr.Get();
if (IsValid(Obj))
{
UE_LOG(LogTemp,Warning,TEXT("OnLoadASync:%s"),*Obj->GetName())
}
});

UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(onloadPath,hanlde);
}

多文件加载

  • 方法一 配合 FSoftObjectPtr

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

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

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      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());
      }
      }
      }
  • 方法二 配合TSoftObjectPtr<T>

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    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> 通过编译
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    //加载单个
    void AG_LoadHelper::OnLoadASync(const FString loadPath)
    {
    FSoftObjectPath onloadPath {"Blueprint'/Game/GameBuleprint/TestCube_BP.TestCube_BP_C'"};
    FStreamableDelegate hanlde = FStreamableDelegate::CreateLambda([onloadPath]
    {
    FSoftObjectPtr SoftObjectPtr = FSoftObjectPtr(onloadPath);
    UObject *Obj = SoftObjectPtr.Get();
    if (IsValid(Obj))
    {
    UE_LOG(LogTemp,Warning,TEXT("OnLoadASync:%s"),*Obj->GetName())
    }
    });

    UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(loadPath,hanlde);
    }


    //加载多个蓝图对象
    //.h
    UPROPERTY(EditAnywhere)
    TArray<FSoftObjectPath> ObjectList;
    //.cpp
    void AG_LoadHelper::OnLoadASync()
    {
    ObjectList.Add(FSoftObjectPath("Blueprint'/Game/GameBuleprint/TestCube_BP.TestCube_BP_C'"));
    ObjectList.Add(FSoftObjectPath("Blueprint'/Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C'"));
    FStreamableDelegate streamableDelegate = FStreamableDelegate::CreateUObject(this, &AG_LoadHelper::OnSingleAssetLoadFinshed);
    UAssetManager::Get().GetStreamableManager().RequestAsyncLoad(ObjectList,streamableDelegate);
    }

    void AG_LoadHelper::OnSingleAssetLoadFinshed()
    {
    for (auto path : ObjectList)
    {
    TSoftClassPtr<AActor> ItemPtr = TSoftClassPtr<AActor>(path);
    UClass* ItemClass = ItemPtr.Get();
    float height = UKismetMathLibrary::RandomFloatInRange(200,500);
    GetWorld()->SpawnActor<AActor>(ItemClass,FVector(0,0,height),FRotator(0,0,0));
    UE_LOG(LogTemp, Warning, TEXT("Actor name:%s"), *ItemClass->GetName());
    }
    }

卸载资源

自动回收

  • 当对象失去饮用后会被自动释放。
  • 在异步回调结束后,对象会被标记可回收,此时使用 ForceGC 可销毁对象

手动回收

在执行加载时,将bManageActiveHandle标记为true,默认为false:表示是否手动管理FStreamableHandle。如果设置为true,则对象会一直常驻内存直到手动释放。

1
2
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
UParticleSystem* AimObj = AssetLoader.LoadSynchronous<UParticleSystem>(FSoftObjectPath(AssetPath), true);

当对象不再需要时,再手动执行执行Unload。之后对象就会被自动回收:

1
2
FStreamableManager& AssetLoader = UAssetManager::GetStreamableManager();
AssetLoader.Unload(FSoftObjectPath(AssetPath));

Unload之后如果需要立即回收,可以执行ForceGC:

1
GEngine->ForceGarbageCollection(true);

通过字符串生成actor

1
2
3
4
5
6
7
FStringAssetReference asset = "Blueprint'/Game/ThirdPersonBP/Blueprints/ThirdPersonCharacter.ThirdPersonCharacter_C'";  
UObject* itemObj = asset.ResolveObject();
UBlueprint* gen = Cast<UBlueprint>(itemObj);
if (gen != NULL)
{
AActor* spawnActor = GetWorld()->SpawnActor<AActor>(gen->GeneratedClass,FVector(0,0,300),FRotator(0,0,0));
}

UE4 C++入门到进阶] 8.同步or异步资源加载 - 哔哩哔哩

UE4 C++ 基础知识<11>资源的同步加载与异步加载 - 砥才人 - 博客园

UE4:四种加载资源的方式 - Bill Yuan - 博客园