目錄
- 資源的硬參考
- 硬指標
- FObjectFinder
/ FClassFinder
- 資源的軟參考
- FSoftObjectPaths、FStringAssetReference
- TSoftObjectPtr
- 同步加載資源
- LoadObject/LoadClass
- TryLoad/LoadSynchronous
- 異步加載資源
- FStreamableManager.RequestAsyncLoad()
- 卸載資源
- 創建物件
- 創建一般物件
- 創建Actor派生類物件
- 創建Component派生類物件
- 創建藍圖物件
- 參考
在UE4中,專案中的所有資源都是存盤在硬碟中,當需要用到資源時,則需要將其加載進入記憶體中使用,為了更好的表示(參考)資源,UE4提供了兩種參考資源的方式——硬參考、軟參考,
資源的硬參考
硬性參考,即物件 A 參考物件 B,并導致物件 B 在物件 A 加載時加載,通俗點說,硬參考所表示的資源在參考初始化時就加載進記憶體,因此硬參考的資源幾乎不需要加載方法,
硬指標
參考資源的最簡單方法是創建指標UProperty并為它指定一個類別,這種稱為硬指標,
在UE4中,如果有一個硬UObject指標屬性參考了一個資源(往往在藍圖上設定參考),則加載包含這個屬性的物件(放在貼圖中,或者從gameinfo等參考)時,就會加載這個資源,
UPROPERTY(EditDefaultsOnly, Category=Building)
USoundCue* ConstructionStartStinger;
FObjectFinder<T> / FClassFinder<T>
若需要用C++代碼而非藍圖來設定參考,則往往需要FObjectFinder、FClassFinder,
#include "UObject/ConstructorHelpers.h" \\需要include的頭檔案
在UE4原始碼里面,FObjectFinder建構式里通過呼叫LoadObject()來加載資源,而FClassFinder建構式里呼叫的也是LoadObject(),
注意在使用它們的時候還得遵守如下規則:
- 只能在類的建構式中使用,如果在普通的邏輯代碼中嵌套這份代碼,會引起整個編譯器的crash,(實際上里面代碼就有檢查是否在建構式里,否則crash)
- 其次,FObjectFinder/FClassFinder變數必須是static的,從而保證只有一份資源實體,
- FObjectFinder<T>:一般用來加載非藍圖資源,比如StaticMesh、Material、SoundWave、ParticlesSystem、AnimSequence、SkeletalMesh等資源:
static ConstructorHelpers::FObjectFinder<UTexture2D> ObjectFinder(TEXT("Texture2D'/Game/Textures/tex1.tex1'"));
UTexture2D* Texture2D = ObjectFinder.Object;
- FClassFinder<T>:一般用來加載藍圖資源并獲取藍圖Class,這是因為如果C++要用藍圖創建物件,必須先獲取藍圖的Class,然后再通過Class生成藍圖物件:
static ConstructorHelpers::FClassFinder<AActor> BPClassFinder(TEXT("/Game/Blueprints/MyBP"));
TSubclassOf<AActor> BPClass = BPClassFinder.Class;
...//利用Class生成藍圖物件
- FClassFinder的模版名不能直接寫UBlueprint,例如:FClassFinder<UBlueprint>是錯誤的,創建藍圖時選擇的是什么父類,則寫對應的父類名,假如是Actor,那么要寫成:FClassFinder<AActor>,否則無法加載成功,
- FClassFinder的模版名必須和TSubclassOf變數的模版名一致,當然也可使用UClass*代替TSubclassOf<T>,實際上TSubclassOf<T>也是UClass*,只是更加強調這個Class是從T派生出來的,
- 在啟動游戲時若報錯提示找不到檔案而崩潰(例如:Default property warnings and errors:Error: COD Constructor (MyGameMode): Failed to find /Game/MyProject/MyBP.MyBP)
這是因為UE4資源路徑的一個規范問題,解決辦法有兩種:- 在copy reference出來的檔案路徑后面加_C,例如:"Blueprint'/Game/Blueprints/MyBP.MyBP_C'"(_C可以理解為獲取Class的意思),
- 去掉路徑前綴,例如:"/Game/Blueprints/MyBP"
資源的軟參考
軟性參考,即物件 A 通過間接機制(例如字串形式的物件路徑)來參考物件 B,
硬參考的問題是在容易一開始就加載全部硬參考表示的資源,這可能導致資源載入時間過長,而軟參考則是可隨時靈活加載資源的一種參考,而不用硬性地一開始就加載,
FSoftObjectPaths、FStringAssetReference
- FSoftObjectPath:是一個簡單的結構體,其中包含了資源的完整名稱(一個字串),它實質就是用一個字串來表示對應的資源,從而可以隨時通過字串找到硬碟上的目標資源,將其載入進記憶體,
FSoftObjectPath.SolveObject() 可以檢查其參考的資源是否已經載入在記憶體中,若載入則返還資源物件指標,否則返還空,
FSoftObjectPath.IsPending() 可檢查資源是否已準備好可供訪問,而如何利用FSoftObjectPath加載資源進記憶體,后面還會說到,
- FStringAssetReference:其實只是一個聽起來更容易理解的別名,它實際在UE4原始碼里是這樣的:
typedef FSoftObjectPath FStringAssetReference;
TSoftObjectPtr<T>
TSoftObjectPtr是包含了FSoftObjectPath的TWeakObjectPtr,可通過模板引數來設定特定資源型別,這樣就可以限制編輯器UI僅允許選擇特定的資源種類,
TSoftObjectPtr.Get() 可以檢查其參考的資源是否已經載入在記憶體中,若已載入則返還資源物件指標,否則返還空,想要資源加載進記憶體,則可以呼叫ToSoftObjectPath()來得到FSoftObjectPaths用于加載,
同步加載資源
LoadObject/LoadClass
- LoadObject<T>():加載UObject,一般用來加載非藍圖資源,
UTexture2D* Texture2D = LoadObject<UTexture2D>(nullptr,TEXT("Texture2D'/Game/Textures/tex1.tex1'"));
- LoadClass<T>():加載UClass,一般用來加載藍圖資源并獲取藍圖Class,實際上原始碼里LoadClass的實作是呼叫LoadObject并獲取型別,
- LoadClass的模版名稱,和上面FClassFinder一樣,不能直接寫UBlueprint,
- LoadClass路徑規范也和上面的FClassFinder一樣,帶_C后綴或去掉前綴,
另外有兩個函式叫:StaticLoadObject()和StaticLoadClass(),是LoadObject()和LoadClass()的早期版本,前兩者需要手動強轉和填寫冗雜引數,后兩者則是前兩者的封裝,使用更方便,推薦使用后者,
TSubclassOf<AActor> BPClass = LoadClass<AActor>(nullptr, TEXT("/Game/Blueprints/MyBP"));
此外一提,還有一個可能常用的全域函式FindObject(),用來查詢資源是否載入進記憶體,若存在則返還資源物件指標,否則返還空,但是我們不用先查詢再使用LoadXXX,因為LoadXXX里本身就有用到FindObject來檢查存在性,
TryLoad/LoadSynchronous
- TryLoad():FSoftObjectPaths的方法,直接根據路徑加載資源,
- LoadSynchronous():TSoftObjectPtr<T>的方法,也是直接根據路徑加載資源,
由于軟參考里包含資源完整路徑名,因此無需再寫一次路徑名,而是呼叫如上成員方法來加載資源進記憶體,而軟參考的作用不僅如此,它還可以用于下面要介紹的資源異步加載方式,
異步加載資源
即使可以控制加載資源的時機,但如果加載的資源物件很大(或者同一時刻加載多個資源),還是會造成卡頓,為了避免阻塞主執行緒,異步加載的方式必不可少,
FStreamableManager.RequestAsyncLoad()
首先,需要創建FStreamableManager,官方建議將它放在某類全域游戲單例物件中,例如使用GameSingletonClassName在DefaultEngine.ini中指定的物件,
- FStreamableManager.RequestAsyncLoad():將異步加載一組資源并在完成后呼叫委托,
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>();
創建Actor派生類物件
生成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,
創建Component派生類物件
為Actor創建組件,可使用UObject::CreateDefaultSubobject()模板函式
UCameraComponent* Camera = CreateDefaultSubobject<UCameraComponent>(TEXT("Camera0"));
- CreateDefaultSubobject必須寫在Actor的無參建構式中,否則crash,
- CreateDefaultSubobject中的TEXT或者FName引數在同一個Actor中不能重復,否則crash,
- 一定要添加RegisterComponent(),否則編輯器不會顯示,
創建藍圖物件
藍圖由于本質是一種腳本,不是直接的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);
}
參考
虛幻引擎4 官方檔案 | 參考資源
虛幻引擎4 官方檔案 | 異步資源加載
UE4:四種加載資源的方式
[UE4]C++實作動態加載的問題:LoadClass
Unreal Cook Book:創建物件的的幾種姿勢(C++)
ue4 c++加載藍圖類,資源方式
[UE4]C++實作動態加載的問題
系列其他文章:Aery的UE4 C++開發之旅系列文章
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/6754.html
標籤:其他
