目录html
在UE4中,项目中的全部资源都是存储在硬盘中,当须要用到资源时,则须要将其加载进入内存中使用。为了更好的表示(引用)资源,UE4提供了两种引用资源的方式——硬引用、软引用。c++
硬性引用,即对象 A 引用对象 B,并致使对象 B 在对象 A 加载时加载。通俗点说,硬引用所表示的资源在引用初始化时就加载进内存,所以硬引用的资源几乎不须要加载方法。异步
引用资源的最简单方法是建立指针UProperty并为它指定一个类别,这种称为硬指针。
在UE4中,若是有一个硬UObject指针属性引用了一个资源(每每在蓝图上设置引用),则加载包含这个属性的对象(放在贴图中,或者从gameinfo等引用)时,就会加载这个资源。async
UPROPERTY(EditDefaultsOnly, Category=Building) USoundCue* ConstructionStartStinger;
若须要用C++代码而非蓝图来设置引用,则每每须要FObjectFinder、FClassFinder。编辑器
#include "UObject/ConstructorHelpers.h" \\须要include的头文件
在UE4源码里面,FObjectFinder构造函数里经过调用LoadObject()来加载资源,而FClassFinder构造函数里调用的也是LoadObject()。函数
注意在使用它们的时候还得遵照以下规则:ui
static ConstructorHelpers::FObjectFinder<UTexture2D> ObjectFinder(TEXT("Texture2D'/Game/Textures/tex1.tex1'")); UTexture2D* Texture2D = ObjectFinder.Object;
static ConstructorHelpers::FClassFinder<AActor> BPClassFinder(TEXT("/Game/Blueprints/MyBP")); TSubclassOf<AActor> BPClass = BPClassFinder.Class; ...//利用Class生成蓝图对象
软性引用,即对象 A 经过间接机制(例如字符串形式的对象路径)来引用对象 B。this
硬引用的问题是在容易一开始就加载所有硬引用表示的资源,这可能致使资源载入时间过长。而软引用则是可随时灵活加载资源的一种引用,而不用硬性地一开始就加载。spa
FSoftObjectPath:是一个简单的结构体,其中包含了资源的完整名称(一个字符串)。它实质就是用一个字符串来表示对应的资源,从而能够随时经过字符串找到硬盘上的目标资源,将其载入进内存。.net
FSoftObjectPath.SolveObject() 能够检查其引用的资源是否已经载入在内存中,若载入则返还资源对象指针,不然返还空。
FSoftObjectPath.IsPending() 可检查资源是否已准备好可供访问。而如何利用FSoftObjectPath加载资源进内存,后面还会说到。
typedef FSoftObjectPath FStringAssetReference;
TSoftObjectPtr是包含了FSoftObjectPath的TWeakObjectPtr,可经过模板参数来设置特定资源类型,这样就能够限制编辑器UI仅容许选择特定的资源种类。
TSoftObjectPtr.Get() 能够检查其引用的资源是否已经载入在内存中,若已载入则返还资源对象指针,不然返还空。想要资源加载进内存,则能够调用ToSoftObjectPath()来获得FSoftObjectPaths用于加载。
UTexture2D* Texture2D = LoadObject<UTexture2D>(nullptr,TEXT("Texture2D'/Game/Textures/tex1.tex1'"));
另外有两个函数叫:StaticLoadObject()和StaticLoadClass(),是LoadObject()和LoadClass()的早期版本,前二者须要手动强转和填写冗杂参数,后二者则是前二者的封装,使用更方便,推荐使用后者。
TSubclassOf<AActor> BPClass = LoadClass<AActor>(nullptr, TEXT("/Game/Blueprints/MyBP"));
此外一提,还有一个可能经常使用的全局函数FindObject(),用来查询资源是否载入进内存,若存在则返还资源对象指针,不然返还空。可是咱们不用先查询再使用LoadXXX,由于LoadXXX里自己就有用到FindObject来检查存在性。
因为软引用里包含资源完整路径名,所以无需再写一次路径名,而是调用如上成员方法来加载资源进内存。而软引用的做用不只如此,它还能够用于下面要介绍的资源异步加载方式。
即便能够控制加载资源的时机,但若是加载的资源对象很大(或者同一时刻加载多个资源),仍是会形成卡顿,为了不阻塞主线程,异步加载的方式必不可少。
首先,须要建立FStreamableManager,官方建议将它放在某类全局游戏单例对象中,例如使用GameSingletonClassName在DefaultEngine.ini中指定的对象。
void UGameCheatManager::GrantItems() { //获取 FStreamableManager的单例对象引用 FStreamableManager& Streamable = ...; //获得一组软引用 TArray<FSoftObjectPath> ItemsToStream; for(int32 i = 0; i < ItemList.Num(); ++i) ItemsToStream.AddUnique(ItemList[i].ToStringReference()); //根据一组软引用来异步加载一组资源,加载完后调用委托 Streamable.RequestAsyncLoad(ItemsToStream, FStreamableDelegate::CreateUObject(this, &UGameCheatManager::GrantItemsDeferred)); } void UGameCheatManager::GrantItemsDeferred() { //do something.... }
FStreamableManager其实也有同步加载的方法:SynchronousLoad()方法将进行一次简单的块加载并返回对象。
若是资源永再也不使用,想将资源对象从内存上卸载,代码以下:
Texture2D* mytex; //这里假设mytex合法有效 mytex->ConditionalBeginDestroy(); mytex = NULL; GetWorld()->ForceGarbageCollection(true);
UE4的对象(即从UObject派生出来的类对象)最好不要用C++的new/delete,而应使用UE4提供的对象生成方法,要否则继承UObject的垃圾回收能力就无从用处。
若是有UObject的派生类(非Actor、非Component),那么可以使用NewObject()模板函数来建立其实例对象:
UMyObject* MyObject = NewObject<UMyObject>();
生成AActor派生类对象不要用NewObject或new,而要用UWorld::SpawnActor()
UWorld* World = GetWorld(); FVector pos(150, 0, 20); AMyActor* MyActor = World->SpawnActor<AMyActor>(pos,FRotator::ZeroRotator);
注意SpawnActor不能放在构造函数,可是能够放在其余时期的函数里,例如BeginPlay()、Tick()...不然可能会编译后就crash。
为Actor建立组件,可以使用UObject::CreateDefaultSubobject()模板函数
UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera0"));
蓝图因为本质是一种脚本,不是直接的C++类,所以每每须要借助动态类型来生成蓝图对象。全部的加载资源并建立到场景中的方式都离不开SpawnActor这一句代码。
1.经过已肯定的父类来生成蓝图对象
AMyActor* spawnActor = GetWorld()->SpawnActor<AMyActor>(AMyActor::StaticClass());
若是你的蓝图派生于某个C++类,那么能够直接访问该类的StaticClass()并用于SpawnActor来建立蓝图对象。
2.经过UClass生成蓝图对象
UClass* BPClass = LoadClass<AActor>(nullptr, TEXT("/Game/Blueprints/MyBP")); //TSubclassOf<AActor>同理 AActor* spawnActor = GetWorld()->SpawnActor<AActor>(BPClass);
3.经过UObject生成蓝图对象
若获得UObject则须要先转换成UBlueprint,再经过GeneratedClass获取UClass来生成蓝图对象
FStringAssetReference asset = "Blueprint'/Game/BluePrint/TestObj.TestObj'"; UObject* itemObj = asset.ResolveObject(); UBlueprint* gen = Cast<UBlueprint>(itemObj); if (gen != NULL) { AActor* spawnActor = GetWorld()->SpawnActor<AActor>(gen->GeneratedClass); }
[UE4]C++实现动态加载的问题:LoadClass
Unreal Cook Book:建立对象的的几种姿式(C++)
系列其余文章:Aery的UE4 C++开发之旅系列文章