虚幻引擎基础入门(C++) — 【资源加载 08】
前言
-
在虚幻中我们需要处理的资源分为:类资源和非类资源。
-
资源/资产加载到内存中,我们操作需要通过引用来完成,引用分两类:
直接属性引用(硬性引用):直接引用
间接属性引用(软性引用):通过间接机制(如字符串形式的对象路径)来引用 -
加载方式分为:
同步加载(资源过大会导致游戏程序卡顿)
01.在加载运行线程中,阻塞线程的流程执行,将线程停止在当前加载逻辑中,加载完成后继续线程的执行逻辑操作
02.对于加载小资源可以保证资源的不为空,但是加载大资源将导致调用线程卡顿异步加载
在加载线程中,不阻塞当前线程逻辑加载资源,加载器本身具备线程进行资源加载
比同步加载更加灵活,但维护成本较高,资源加载成功后需要进行回调通知,以完成整个加载流程
一、直接属性引用
- 编辑器直接加载
通过使用属性 UPROPERTY 宏标记,将资产对象指针暴露到编辑器面板,从而直接从编辑器面板拾取资产
- TSubClassOf
01.是提供UClass的安全模板类,可以快速构建某种类型对象数据
02.但只能选取模版类型或继承自模版类型的类 / 蓝图
//1.引用类资源
//① UClass类:暴露到蓝图中,直接拾取任意类资源(用指针)
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
UClass* CharacterClass;
//② TSubclassOf:只能用来拾取 AMyActor 类或其子类(用<>包裹)(记得加头文件)
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
TSubclassOf<AMyActor> MyActor;
MyActor.GetClass(); //获取UClass指针
//2.引用非类资源
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
USoundBase* Sound; //拾取音频资源
//拾取音频还可以用:USoundCue(加头文件#include<Sound/USoundCue.h>)
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
UStaticMesh* Mesh; //拾取静态网格体
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
UStaticMeshComponent* Mesh; //静态网格体组件(装载资产的组件)
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
USkeletalMesh* SMesh; //拾取骨骼网格体
//3.加载类资源和非类资源
//UObject类
UPROPERTY(EditAnyWhere, BlueprintReadWrite)
UObject* Object;
-
构造函数加载
- 借助构造函数资产加载类进行资源引用 - ConstructorHelpers可以进行类引用,源资源引用 - ConstructorHelpers 只能在构造函数中使用,GameInstance中是 Init 函数(需要重载)
-
常用的资源加载分类:
FClassFinder:加载创建后的蓝图对象
ConstructorHelpers::FClassFinder<APawn>(TEXT("/Game/Flappybird/Blueprints/BP_Bird")).Class;//返回数据类型是TSubClassof
//使用路径拾取蓝图对象(要加_C)
ConstructorHelpers::FClassFinder<AActor> UnitSelector(TEXT(“Blueprint‘/Game/Blueprints/MyBlueprint.MyBlueprint_C’”));//下划线C必须要加
FObjectFinder:加载各种资源(如音频,图片,材质,静态网格)
ConstructorHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
BarFillTexture = BarFillObj.Object; //将获取的数据内容指针保存
注意: 操作路径前加入 /Game/ 前缀 加载蓝图类模版对象时,需要加注“_C” 加载失败或是未找到资源,对象内的资产属性为null
- 查找加载
1>在只知道目标资源路径的基础上,进行运行时态的资源加载
2>资源加载可能会失败或是无效,所以需要对操作的结果进行判定
- LoadClass:加载类资产,可放在任何地方加装 , 返回UClass指针,可用TSubclassOf来接
TSubclassOf Player1 = LoadClass(NULL, TEXT(“Blueprint’/Game/BP/BP_Actor.BP_Actor_C’”));
- LoadObject:加载类资源 + 非类资产,返回该类的指针
AMyActor* Player2 = LoadObject(NULL, TEXT(“Blueprint’/Game/BP/BP_Actor.BP_Actor_C’”));
- 查找加载
此处的 “文件路径” 是复制绝对路径
二、间接属性引用
- FSoftObjectPath
-
是一个简单的结构体,使用一个字符串包含资源的完整名称
-
可以在编辑器中拾取资源(与直接属性引用相同),但没有把资源加载到内存(资源的加载需要通过额外的代码编写完成)
-
对于资源的拾取并没有特定的要求,所有能够被序列化的资源均能被拾取(类资源,非类资源)
- FSoftClassPath
- 仅拾取类资源链接
//(在.h文件中)
//间接属性引用
//1.FSoftClassPath,用于拾取类资源的链接
UPROPERTY(EditAnywhere)
FSoftClassPath CharacterPath;
//2.FSoftObjectPath:(类资源 + 非类资源)暴露到蓝图中拾取的是一个链接,不会进行资源加载
UPROPERTY(EditAnyWhere)
FSoftObjectPath SoundPath;
三、同步加载和异步加载
- 同步加载(加载大资源将导致调用线程卡顿)
- 异步加载(资源加载成功后需要进行回调通知)
FSreamableManager 构建异步处理逻辑,创建在全局游戏的单例对象中,结合FSoftObjectPath进行加载
//(在UECCharacter.h文件中)
#include <Engine/StreamableManager.h> //资源加载器头文件引入
UCLASS(config=Game)
class AUECCharacter : public ACharacter
{
public:
//1.同步加载
//加载播放音效资源
void LoadPlaySound();
//2.异步加载
//① 声明资源加载器(构建为栈对象,需引入头文件,不要构建为堆对象)
FStreamableManager LoadStream;
//② 回调通知函数
void LoadCallBack();
};
/(在UECCharacter.cpp文件中)
#include <Kismet/GameplayStatics.h> //播放音频头文件引入
void AUECCharacter::LoadPlaySound()
{
if (!SoundPath.IsValid()) //判断资产链接是否有效
{
//如果资产链接无效,则直接设置资产路径(可以用来替代细节面板中的引用)
SoundPath.SetPath(TEXT("SoundCue'/Game/StarterContent/Audio/Collapse_Cue.Collapse_Cue'"));
}
//尝试加载资源
UObject*Source = SoundPath.TryLoad();
//1.同步加载(会阻塞线程)
UObject* Source = LoadStream.LoadSynchronous(SoundPath);
//2.异步加载
//① 请求加载,并绑定回调函数(资源,单播)
LoadStream.RequestAsyncLoad(SoundPath,FStreamableDelegate::CreateUObject(this,&AUECCharacter::LoadCallBack));
}
//② 回调函数调用(表明异步加载完成)
void AUECCharacter::LoadCallBack()
{
//类型转换Cast
//SoundPath.ResolveObject():获取异步加载好的资源引用
USoundBase* Sound = Cast<USoundBase>(SoundPath.ResolveObject());
if (Sound)
{
//播放音效
UGameplayStatics::PlaySound2D(GetWorld(), Sound);
}
}
四、同步加载和异步加载
TSoftObjectPtr和TSoftClassPtr
- TSoftObjectPtr和TSoftClassPtr也分为同步加载与异步加载
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class UStaticMesh> SoftMesh;
UPROPERTY(EditAnywhere)
TSoftClassPtr<class ATestActor> SoftTestActor;
- TSoftObjectPtr
同步加载
//(.h 文件)
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class UStaticMesh> SoftMesh;
// 构建为栈对象,需要引入头文件,不要构建为堆对象
// #include "Engine/StreamableManager.h"
FStreamableManager m_Streamable;
//(.cpp文件)
// 可以转换为FSoftObjectPath对象
SoftMesh.ToSoftObjectPath();
// 同步加载
UObject* Source = m_Streamable.LoadSynchronous(SoftMesh);
UStaticMesh* Mesh = Cast<UStaticMesh>(Source);
if (Mesh)
{
UKismetSystemLibrary::PrintString(this, TEXT("加载成功!"));
}
异步加载
//(.h文件)
// 异步加载回调函数
void LoadSourceCallback();
//(.cpp文件)
// 异步加载
//需要在初始化函数中绑定回调函数,然后实现回调函数
m_Streamable.RequestAsyncLoad(SoftMesh.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &UTestGameInstance::LoadSourceCallback));
- TSoftClassPtr
同步加载
//(.h文件)
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class UStaticMesh> SoftMesh;
// 构建为栈对象,需要引入头文件,不要构建为堆对象
// #include "Engine/StreamableManager.h"
FStreamableManager m_Streamable;
//(.cpp文件)
// 同步加载
TSubclassOf<ATestActor> TestActorClass = m_Streamable.LoadSynchronous(SoftTestActor);
if (TestActorClass)
{
GetWorld()->SpawnActor<ATestActor>(TestActorClass);
}
//(.cpp文件)
// 异步加载
m_Streamable.RequestAsyncLoad(SoftTestActor.ToSoftObjectPath(), FStreamableDelegate::CreateUObject(this, &UTestGameInstance::LoadSourceCallback));
//回调函数实现
void UTestGameInstance::LoadSourceCallback() {
// 此函数调用,则表明异步加载完成
UClass* TestActorClass = SoftTestActor.Get();
if (TestActorClass)
{
GetWorld()->SpawnActor<ATestActor>(TestActorClass);
}
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了虚幻引擎中资源加载的使用。
如有帮助给个关注吧!