目錄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++開發之旅系列文章