虚幻C++入门个人笔记(2)——标记宏、结构体枚举、资源加载、代理
標記宏
類UCLASS、成員屬性UPROPERTY、成員函數UFUNCTION
藍圖和C++的交互
UObject
UE4對于C++進行調整后,靠攏了其他虛擬機語言,為了方便對象內存管理和映射數據操作,UE4對UECPP進行了更加嚴謹的語法定義,所有UE中使用的對象大部分繼承自UObject,這樣的系統級的管理更加輕松,不必涉及更多不定性內容,UObject具備如下優點
1.垃圾回收(new出來的對象不用去釋放了)
2.引用更新(更新受限于GC回收機制,需要使用UPROPERTY宏標記)(蘋果消亡,對象里指向它的指針指向為空)
3.序列化(場景中的Actor被保存時發生)(保存時保存到本地)
4.默認屬性變化自動更新(對象在藍圖中暴露了屬性,在C++中改了屬性,藍圖中會自動修改,藍圖里改c++也會改)
5.源碼調整了對象屬性時,編譯后會自動更新到資源實例上,前提場景實例沒有修改過屬性值,修改過后將使用場景中的修改內容做填充參考
6.自動屬性初始化(不賦值指向默認值)
7.自動編輯器整合(可以將C++屬性暴露到藍圖上)
8.運行時類型信息可用
9.運行時使用Cast可以進行類型信息投射檢查(指針轉換關系,嘗試性地將一個指針轉換為另一個指針,轉換的過程是安全的,失敗返回空成功返回真,兩個類沒有關系的時候就會失敗)
10.網絡復制
UCLASS宏
用于標記從UObject派生的類,使得UObject處理系統識別到它們
該宏為UObject提供一個對UCLASS的引用,描述是基于虛幻引擎的類型。對于標記UCLASS的類,引擎可進行識別
繼承關系中的頭文件描述
#include "MyObject.generated.h"
必須是最后一個被引入的頭文件,默認由UE模板生成,此頭文件需要了解其他類,所以需要放到最后
UCLASS()
UCLASS宏使虛幻引擎4能識別UMyObject
class MYPROJECT_API UMyObject:public UObject
如MyProject希望將UMyObject類公開到其他模板,則需要指定MYPROJECT_API
GENERATED_BODY()
GENERATED_BODY宏不獲取參數,但會對類進行設置,以支持引擎要求的基礎結構,GENERATED_BODY宏將成員訪問等級設為“public”,而非設置“private”的缺省語言
創建UObject類對象指針
動態構建對象的時候使用到這個函數
NewObject()是最為簡單的UObject工廠模式,他需要可選的外部對象和類,并會創建擁有自動生成的名稱的新實例。
UCLASS宏標記
語法結構
UCLASS(描述指令,描述指令,...)
常用標記
BlueprintType
此類可以作為藍圖中的一種變量類型使用,類默認均可被藍圖訪問,一般我們用此標記描述結構體,提供給藍圖訪問
Blueprintable或NotBlueprintable
用來標明當前類是否可以被藍圖繼承,默認可被繼承,標記關系向子類傳遞,子類可覆蓋描述關系
Abstract
Abstract類修飾符將類聲明為“抽象基類”,這樣會阻止用戶在虛幻編輯器中向這個世界中添加這個類的Actor,或者在游戲過程中創建這個類的實例
Const
本類中的所有屬性及函數均為常量,并應作為常量導出,該標記由子類繼承
Config
將類內的成員變量數據信息保存到本地配置文件中,需要顯式調用函數SaveConfig使用,并配合UPROPERTY宏操作
例:UCLASS(Config=Game)
ClassGroup
用來配置組件在添加時分組情況
例:UCLASS(ClassGroup=(ZQSELF))? ? ? ? //分組名字不要寫中文
meta
額外操作宏
例:meta=(BlueprintSpawnableComponent)? ? ? ? //當前組件可以在藍圖中再被延展
UFUNCTION宏標記
用途:將成員函數暴露到藍圖中去,在藍圖中享有操作權限
語法結構(標注在函數頂端,禁止在尾端加分號)
UFUNCTION(指令,指令...,meta(key=value))
注意:在UFUNCTION修飾的函數中,如果參數類型是引用型參數,則在藍圖中將當作返回參數使用,無法查到輸入陣腳。如果參數類型是const修飾的引用型參數,則參數被當作輸入陣腳使用
常用標記
BlueprintCallable
表明此函數可在藍圖中被調用(當類被藍圖繼承后才有效果)
注意:如果函數參數是引用類型,則在藍圖中調用被當作輸入陣腳,如果從傳入參數是const修飾的引用類型,則在藍圖中被當作輸入針腳
例1:UFUNCTION(BlueprintCallable)
int32 CTime(const int32& num);
例2:UFUNCTION(BlueprintCallable)
int32 CTime(int32& num);? ? ? ? //被當作函數輸出陣腳使用
Category
標明此函數在藍圖中的分類
用法Category="UE4TEST|MyActor"? ? ? ? //? ? ? ? |符號用來劃分分類級別
BlueprintImplementableEvent
標記的函數只能在C++中調用,在藍圖中無法調用——在CPP中無需定義即可調用,調用的是藍圖中的一個節點
用此標記可以在C++中構建函數聲明,但定義由藍圖完成,從而打到C++向藍圖進行調用的目的,在CPP中無需定義
UFUNCTION(BlueprintImplementableEvent,Category="UECPP|ACPPActory")
類似純虛函數,但是繼承關系中的藍圖并不用必須重寫此函數
注意:
如果函數沒有返回類型,則在藍圖中當作事件Event使用
如果函數存在返回類型,則在藍圖中當作函數使用(需要在函數的overlap中尋找)
如果帶有參數
沒有返回值,參數是基本數據類型,則當作普通事件輸入參數使用
沒有返回值,參數是基本數據類型引用,則當作函數看待,在藍圖函數重載表中尋找
沒有返回值,參數是自定義數據類型(如FString),編譯不過
沒有返回式,參數是自定義數據類型引用,則當作普通函數看待,在藍圖函數重載表中尋找
如果有返回值,則當作函數使用
傳遞引用操作如BlueprintCallable
BlueprintNativeEvent
標記的函數只能在C++中調用,在藍圖中無法調用
此標記可以標注函數可以在藍圖中被重寫,并且具備在C++中有另一個標記函數聲明格式為:
函數名_Implementation(參數列表)? ? ? ? //必須要在C++中聲明且定義
達到效果,如果藍圖重寫此函數,則函數邏輯實現在藍圖,如果藍圖沒有重寫此函數,則函數實現在(函數名_Implementation)上
藍圖中實現后,可以右鍵函數節點,選擇add call to parent function可以調用父類的函數邏輯(類似類中的虛函數,在繼承關系中子類可以調用父類的虛函數)
注意:
如果函數沒有返回類型,則在藍圖中當作事件Event使用
如果函數存在返回類型,則在藍圖中當作函數使用(需要在函數的overlap中尋找)
基本效果同上
傳遞引用操作如BlueprintCallable
BlueprintPure
特殊標記,構建一個藍圖中的純函數,用來獲取對象數據。所以此標記的函數必須有有效返回值(無返回值編譯報錯),并且在藍圖中無法加入到執行隊列,只能以輸出值的操作方式被使用,定義實現均放在CPP中
例:UFUNCTION(BlueprintCallable,BlueprintPure)
UPROPERTY宏標記
語法結構
注意:用于將對象屬性暴露到藍圖中操作,整型中只有int32能暴露到藍圖中
UPROPERTY(標記,標記,...,meta(key=value,key=value,...))
類型 參數名稱
注意:就算沒有標記,該加UPROPERTY的地方也要加,否則沒有智能指針的操作特性,例如UClass* ActorClass? ? ? ? //用來拾取類資產
常用標記
BlueprintReadOnly
標記屬性可以在藍圖中獲取,但是只能讀取無法操作
BlueprintReadWrite
在藍圖中即可讀取也可以操作設置
Category
標明此變量在藍圖中的分類
用法Category="UE4Test|MyActor"? ? ? ? //????????|符號用來劃分分類級別
Config
標記此屬性可被存儲到指定的配置文件中,啟動時屬性內容將從配置文件中獲取,UPROPERTY下方應跟上配置文件存的數據內容
//相應的UClass也應,如CLASS(Config=UE4CPP)
對應的兩個函數:
SaveConfig()
LoadConfig()
EditAnywhere
表示此屬性可以在編輯器窗口中進行編輯也可以在場景細節面板中編輯(都可以)
EditDefaultsOnly
可以在藍圖編輯器中編輯原型數據,但無法在場景細節中編輯場景中的具體對象(藍圖里可以改)
EditInstanceOnly
表明該屬性的修改權限在實例,不能在藍圖編輯器原型中修改(實例中可以改)
VisibleAnywhere
表明屬性可以在屬性窗口可見(原型實例中均可以看到),但無法編輯(都可以看到但不能修改)
注意如果標記組件指針,則表示組件內容在細節面板中顯示所有編輯項
VisibleDefaultsOnly
屬性僅能在原型藍圖編輯屬性窗口中可見,無法編輯(藍圖右側可以看到但不能修改)
注意如果標記組件指針,則表示組件內容在細節面板中顯示所有編輯項
VisibleInstanceOnly
屬性僅能在實例屬性窗口中可見,無法編輯(實例可以看到不能修改)
注意如果標記組件指針,則表示組件內容在細節面板中顯示所有編輯項
EditFixedSize
限定動態數組長度禁止在藍圖屬性面板中修改(單一添加無法顯示,需要配合使用上面兩個標記)
在類里的構造函數調用Array.Init(0,10)可以在類的構造的時候為這個數組以代碼方式進行初始化
meta描述
別名標記指令(也可以用在函數上)
meta=(DisplayName="別名")
使用此標記可以幫助我們對函數或是屬性進行別名操作,可以使用在UF和UP中,可以在藍圖中或是細節面板中顯示別名
成員屬性值域約束(只能用來約束整型)
meta=(UIMin="10",UIMax="20")? ? ? ? //UI上約束,只是面板約束,如直接填入數據則不約束
meta=(ClampMin="0",ClampMax="10")? ? ? ? //UI上約束,填入數據也被約束
成員屬性修改約束
EditCondition
可以借助一個布爾變量來控制另一個變量是否可以在面板中被修改
UPROPERTY(EditAnywhere,EditFixedSize)
bool bShow;
UPROPERTY(EditAnywhere,AdvancedDisplay,meta=(ClampMin="0",ClampMax="10",EditCondition="bShow"))
int32 wock;
結構體,枚舉
結構體(后面接BREAK)
在虛幻C++中結構體和普通C++結構體構建方式相同,但是如果希望構建于藍圖交互的結構體則需要額外的處理
UE支持結構體的構建和使用,但是由于藍圖特殊,普通的結構體定義無法被藍圖訪問,我們需要借助USTRUCT宏進行構建UE中的結構體
注意:結構體名稱必須使用F開頭,不以F開頭編譯不過,必須帶兩個操作宏,如需要在藍圖中使用,需要加入BlueprintType標記
例:
USTRUCT(BlueprintType)
struct FBox
{
GENERATED_USTRUCT_BODY();
UPROPERTY(EditAnywhere,BlueprintReadWrite)? ? ??//不加這個藍圖沒法break和在右側面板調整
int32 x;
UPROPERTY(EditAnywhere,BlueprintReadWrite)
int32 y;
};
類中聲明結構體:
UPROPERTY(EditDefaultsOnly)
FBox box;
枚舉(后面接SWITCH)
列出了有窮數列的集合
語法與C++相同,總的來說可以使用以下兩種方式進行構建
第一種? ? ? ? 空間構建方式
例:
namespace EState
{
enum Type
{EGS_Blue,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //EGS是標題頭
EGS_Red};
}
使用
EState::Type type;
特點:使用空間名稱作為訪問依據,可以更清晰的標明意圖,方便使用
第二種? ? ? ? 約束枚舉大小
enum class ZColo:uint8
{
EGS_ERed,
EGS_EBlue
}
構建與藍圖交互的枚舉
借助標記宏UENUM可以將C++中的枚舉暴露到藍圖中使用,需要使用標記BlueprintType(對于枚舉定義的位置并沒有嚴格要求)
j建議使用:方法一:空間構建枚舉暴露藍圖
聲明枚舉
UENUM(BlueprintType)
namespace EState
{
enum Type
{EGS_Blue,
EGS_Red};
}
定義時枚舉對象需要注意格式:(必須使用此方法才可以暴露到藍圖)
UPROPERTY(EditAnywhere,BlueprintReadWrite)
TEnumAsByte<EState::Type> Color;? ? ? ? //Color是另外隨便起的一個名字,這是一個包裹,把枚舉類型進行了一個包裹,這個Color可以理解成就是枚舉,其實是具有枚舉特性的一個TEnumAsByte的對象類型
方法二:
聲明枚舉
UENUM(BlueprintType)
enum class ENColor:uint8
{
ENC_Red,
ENC_Blue
};
定義枚舉
UPROPERTY(EditAnywhere,BlueprintReadWrite)
ENColor Color;
UMETA宏
可以幫助枚舉名進行藍圖別名創建,方便在藍圖中尋找操作(空間聲明枚舉的方式不適用)
語法
UENUM(BlueprintType)
enum class ENColor:unit8
{
ENC_Red UMETA(DisplayName="R"),
ENC_Blue?UMETA(DisplayName="B"),
};
資源加載
在虛幻中我們需要注意處理的資源分為類資源和非類資源(粒子,音頻,材質,地圖等等)。虛幻中提供了多種機制用來將資產進行加載。資產加載到內存中,我們操作需要通過引用來完成。
引用(對于資產在內存中存儲的一種操作方式)總的來說分兩種:
硬性引用:對象A引用對象B并導致對象B在對象A加載時加載(缺點:加載了但不一定會用到B)
軟性引用:對象A通過間接機制(例如字符串形式的對象路徑)來引用對象B
直接屬性引用(在編輯器面板中拿資產,硬性引用)
編輯器直接加載(當對象實例化的時候就會直接加載那些資產)
通過使用屬性標記宏UPROPERTY(Edit三個都可以)來將資產對象指針暴露到編輯器面板,從而直接從編輯器面板拾取資產
注意:UClass類指針,專門用來拾取類模板資產
UPROPERTY(EditDefaultsOnly)
UClass* ActorClass;? ? ? ? //用來拾取任意類資產
UPROPERTY(EditDefaultsOnly)? ? ? ????????? //禁止在這里寫注釋,語法錯誤
class USoundCue* SoundSrc;? ? ? ? //用來獲取音頻資產
//UTexture,UMaterial,UStaicMesh,USkeletalMesh
TSubClassOf
TSubClassOf是提供UClass的安全模板類,通過此模板類我們可以快速在編輯器中進行類型選擇,幫助我們快速構建某種類型對象數據
TSubClassOf對類型有約束,只能選取模板類型或是繼承自模板類型的類或藍圖
語法
TSubClassOf<T> type;
type.Get();? ? ? ? //獲取UClass數據對象
構建指定類的對象
構造函數的加載
在構造函數中可以借助構造函數資產加載類進行資源引用,更加方便便捷
靜態資源引用類ConstructorHelpers可以進行類引用,源資源引用,ConstructorHelpers只能在構造函數中使用
注意:
1.操作路徑前加入/Game/前綴
2.ConstructorHelpers類將嘗試在內存中查找該資產,如果找不到則進行加載
3.ConstructorHelpers只能在構造函數中使用
4.如果加載失敗或是未找到資源,對象內的資產屬性為null
5.如果加載藍圖類模板對象時,需要加注"_C"
常用資源加入分類
FClassFinder 常用來加載創建后的藍圖對象
FObjectFinder 用來加載各種資源,如音頻,圖片,材質,靜態網格
FClassFinder語法
ConstructHelpers::FClassFinder<APawn> (TEXT("/Game/Flappybird/Blueprints/BP_Bird")).Class
//返回數據類型是TSubClassOf
也可以使用如下路徑拾取藍圖對象
ConstructHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/Flappybird/Blueprints/MyBlueprint.MyBlueprint_C'"));
//下劃線C必須要加
FObjectFinder語法
ConstructHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
BarFillTexture=BarFillObj.Object;? ? ? ? //將獲取的數據內容指針保存
查找加載(軟性引用)
在只知道目標資源路徑的基礎上,進行運行時態的資源加載,UE提供了LoadObject用來加載資產,LoadClass用來加載類,通過模板約束對象類型,增加操作安全,但是注意,資源加載可能會失敗或無效,需要對操作的結果進行判定
LoadObject可直接返回資源有效對象指針
LoadClass返回操作類,非對象返回UClass類型指針
原型如下
//Load an object
template<class T>
inline T* LoadObject(UObject* Outer,const TCHAR* Name,const TCHAR* Filename=nullptr,uint32 LoadFlags=LOAD_None,UPackageMap* Sandbox=nullptr)
{
return (T*)StaticLoadObject(T::StaticClass(),Outer,Name,Filename,LoadFlags,Sandbox);
}
//Load a class object
template<class T>
inline UClass* LoadClass(UObject* Outer,const TCHAR* Name,const TCHAR* Filename=nullptr,uint32 LoadFlags=LOAD_None,UPackageMap* Sandbox=nullptr)
{
return (T*)StaticLoadClass(T::StaticClass(),Outer,Name,Filename,LoadFlags,Sandbox);
};
Outer幫助我們進行搜索范圍鎖定,可以填入同目錄資源,如不存在填入空
Name資源文件名,可在編輯器中通過選擇資源右鍵獲取引用名,注意藍圖加載需要加入后綴"_C"
例子:
Sound=LoadObject<USoundCue>(nullptr,TEXT("路徑"));
SubActorClass=LoadClass<AActor>(nullptr,TEXT("路徑_C"));
注意:
1.查找加載想在什么地方加載就可以在什么地方加載
2.同級別的資產可以加進Outer
3.藍圖加載需要加入后綴"_C"
4.使用前必須使用If做安全檢查,因為可能加載失敗
間接屬性引用
間接屬性引用的設計目的是解決硬性引用加載方式影響啟動效率。間接屬性引用可以很好的幫助我們解決問題。在使用時它和直接屬性引用相同,但是操作時需要提前進行加載。間接屬性引用以字符串形式與模板代碼存儲在一起以便安全地檢查資產是否已經加載,而不是進行直接指針引用,使用IsPending()方法可檢查資產是否已準備好可供訪問。
簡介屬性的目的,將資源加載動作進行拆分,以降低在啟動時內存消耗問題與啟動耗時問題
FSoftObjectPath
FSoftObjectPath是一個簡單的結構體,使用一個字符串包含資源的完整名稱。可以在編輯器中拾取資源(這與直接屬性引用相同),但是并不加載資源。資源的加載需要通過額外的代碼編寫完成
FSoftObjectPath被暴露到面板中對于資源的拾取并沒有特定的要求,所有能夠被序列化的資源均能被拾取(類資源,非類資源)
例子:
UPROPERTY(EditAnywhere)
FSoftObjectPath SourcePath;
資源加載
FSoftObjectPath只是存儲了資源的路徑,使用前必須通過加載方式方可獲得資源。加載方式分為同步加載(如果資源過大會導致游戲程序卡頓)和異步加載
同步加載
在加載運行線程中,阻塞線程的流程執行,將線程停止在當前加載邏輯中,加載完成后繼續線程的執行邏輯操作,對于加載小資源可以保證資源的不為空,但加載大資源將導致調用線程卡頓
例子:
1.????????非類資源
//直接設置資產路徑替代細節面板引用,如果細節面板引用則可忽略
SourcePath.SetPath(TEXT("路徑"));
UObject* Source=SourcePath.TryLoad();? ? ? ? //嘗試加載 同步加載
USoundBase* Sound=Cast<USoundBase>(Source);
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}
2.????????類資源
UObject* Source=SourcePath.TryLoad();? ? ? ? //加載類資產
UClass* ObjClass=Cast<UClass>(Source);
if(ObjClass)
{GetWorld->SpawnActor<AActor>(ObjClass,FVector(200,200,200),FRotator::ZeroRotator);}
異步加載
在加載線程,不阻塞當前線程邏輯加載資源,加載器本身具備線程進行資源加載。較之同步加載更加的靈活,但是相對維護成本較高,資源加載成功后需要進行回調通知,以完成整個加載流程
異步加載
FStreamableManager
FStreamableManager可以幫助我們構建異步處理邏輯,用來將加載邏輯與游戲主邏輯進行,以達到高效加載資源的目的。建議FStreamableManager創建在全局游戲的單例對象中,結合FSoftObjectPath進行加載。FStreamableManager支持異步加載的同時也支持同步加載
FStreamableManager m_Streamable;? ? ? ? //構建為棧對象,需要引入頭文件,不要構建為堆對象
FSoftObjectPath同步加載
同步加載與之前使用的TryLoad基本一致
例子:
頭文件中
UPROPERTY(EditAnywhere)
FSoftObjectPath SourcePath;
FStreamableManager m_Streamable;? ? ? ? //最好構建為一個全局對象,即在全局域構建
源文件中
//借助fstreamablemanager配合fsoftobjectpath同步加載資源
UObject* Source=m_Streamable.LoadSynchronous(SourcePath);? ? ? ? //此步驟會阻塞進程
USoundBase* Sound=Cast<USoundBase>(Source);
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}
FSoftObjectPath異步加載
異步加載需要設置回調通知對象與通知函數
void LoadSourceCallBack();? ? ? ? //寫一個異步加載回調通知函數,無返回類型,無參,無標記宏
在BeginPlay里
//借助fstreamablemanager配合fsoftobjectpath同步加載資源
m_Streamable.RequestAsyncload(SourcePath,FStreamableDelegate::CreateUObject(this,&AMyAcotr::LoadSourceCallBack));? ? ? ? //此步驟不會阻塞進程
異步加載通知函數的定義
//此函數調用,則表明異步加載完成
//加載完成后轉換資產
void AMyAcotr::LoadSourceCallBack()
{USoundBase* Sound=Cast<USoundBase>(SourcePath.ResolveObject());? ? ? ? //ResolveObject函數用來探查資源是否存在內存中
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}}
模板資源拾取類
TSoftObjectPtr和TSoftClassPtr
模板類幫助我們在進行資源操作時增加了類型安全檢查,我們可以在細節面板中根據給定的模板類型拾取對應的資源,以獲得更加高效的操作
同樣的,TSoftObjectPtr和TSoftClassPtr也分為同步和異步加載,針對資源拾取類別不同,使用需要注意
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class USoundBase> SoftSound;
UPROPERTY(EditAnywhere)
TSoftClassPtr<class ACharacter> SoftCharacter;
TSoftObjectPtr同步加載例子
頭文件中
FStreamableManager m_Streamable;
UPROPERTY(EditAnywhere)
TSoftObjectPtr<class USoundBase> SoftSound;
源文件中
SoftSound.ToSoftObjectPath();? ? ? ? //可以轉換為FSoftObjectPath對象
//同步加載
UObject* Source=m_Streamable.LoadSynchronous(SoftSound);? ? ? ? //此步驟會阻塞進程
USoundBase* Sound=Cast<USoundBase>(Source);
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}
TSoftObjectPtr異步加載例子
m_Streamable.RequestAsyncload(SoftSound.ToSoftObjectPath(),FStreamableDelegate::CreateUObject(this,&AMyAcotr::LoadSourceCallBack));? ? ? ? //此步驟不會阻塞進程
異步加載通知函數的定義
//此函數調用,則表明異步加載完成
//加載完成后轉換資產
void AMyAcotr::LoadSourceCallBack()
{USoundBase* Sound=Cast<USoundBase>(SoftSound.Get());? ? ? ? //Get用來加載資源
if(Sound)
{UGameplayStatics::PlaySound2D(GetWorld(),Sound);}}
TSoftClassPtr同步加載
TSubclassOf<ACharacter>?CharacterClass=m_Streamable.LoadSynchronous(SoftCharacter);? ? ? ? //此步驟會阻塞進程
if(SoftCharacter)
{GetWorld()->SpawnActor<ACharacter>(CharacterClass);}
TSoftClassPtr異步加載
m_Streamable.RequestAsyncload(SoftCharacter.ToSoftObjectPath(),FStreamableDelegate::CreateUObject(this,&AMyAcotr::LoadSourceCallBack));? ? ? ? //此步驟不會阻塞進程
void AMyAcotr::LoadSourceCallBack()
{UClass* CharacterClass=CharacterClass.Get();? ? ? ? //Get用來加載資源
if(CharacterClass)
{GetWorld()->SpawnActor<ACharacter>(CharacterClass);}}
代理
代理可以幫助我們解決一對一或是一對多的任務分配工作。主要可以幫助我們解決通知問題。我們可以通過代理完成調用某一給對象的一個函數,而不直接持有該對象的任何指針
想去調用某個函數,但不是直接去調用,而是通過另一個入口去調用(代理)
單播代理——只能進行通知一個對象
多播代理——可以進行多人通知
動態代理——可以被序列化(這體現在于藍圖進行交互,C++中可以將通知事件進行藍圖廣播)
單播代理
通過宏進行構建,單播代理只能綁定一個通知對象,無法進行多個對象通知
構建宏分為兩種
一種是有返回類型的,一種是沒有返回類型的
語法
DECLARE_DELEGATE(GMDelegateOne)
//構建宏
GMDelegateOne GmDel;? ? ? ? //聲明代理
常用綁定函數
BindUObject? ? ? ? 綁定UObject類型對象成員函數的代理
BindSP? ? ? ? 綁定基于共享引用的成員函數代理
BindRaw? ? ? ? 綁定原始自定義對象成員函數的代理,操作調用需要注意執行需要檢查IsBound
BindStatic? ? ? ? 綁定全局函數成為代理
UnBind? ? ? ? 解除綁定代理關系
綁定需要注意,綁定中傳遞的對象類型必須和函數指針所屬類的類型相同否則綁定會報錯
GmDel.BindUObject(act,&AMyActor::Say);
調用執行
為了保證調用的安全性,執行Execute函數之前需要檢查是否存在有效綁定使用函數IsBound
Execute? ? ? ? 調用代理通知,不安全,需要注意
ExecuteIfBound? ? ? ? 調用代理通知,安全,但是有返回類型的回調函數無法使用此函數執行回調
IsBound? ? ? ? 檢查當前是否存在有效代理綁定
GmDel.ExecuteIfBound();
構建步驟
1.通過宏進行聲明代理對象類型(根據回調函數選擇不同的宏)
2.使用代理類型進行構建代理對象
3.綁定回調對象,和操作函數
4.執行代理對象回調
DECLARE_DELEGATE(CallTest);
DECLARE_DELEGATE_RetVal_OneParam(int32,CallTestRe,int32)
cb.BindUObject(am,&AMyActor::CallBackTest);
cb.ExecuteIfBound();
cbr.BindUObject(am,&AMyActor::CallBackRe);
if(cbr.IsBound())
{int32 num=0;
num=cbr.Excute(100);}
多播代理
無法構建具有返回值的多播代理
構建宏
DECLARE_MULTICAST_DELEGATE[_Const,_RetVal,etc.](DelegateName)
綁定函數
AddUObject()
廣播
調用函數Broadcast,但是調用不保證執行順序的正確性
構建步驟
1.使用宏構建代理類型
2.使用代理類型構建多播代理對象
3.添加綁定代理
4.執行調用
動態代理
允許被序列化的數據結構,這將使得代理可以被數據化提供給藍圖進行使用,達到在CPP中調用代理廣播,將事件通知到藍圖中
動態代理和普通代理基本相同,分為單向和多向,動態代理無法使用帶有返回值的函數進行構建(動態單播除外,并且單播無法再藍圖中綁定無法使用宏BlueprintAssignable修飾)
UE中的大部分通知時間均使用動態代理(方便藍圖操作),如碰撞通知
構建一個動態代理
DECLARE_DYNAMIC_DELEGATE[...Const,..._RetVal,etc.](DelegateName)
創建一個動態的多播代理
DECLARE_DYNAMIC_MULTICAST_DELEGATE[...Const,...RetVal,etc.](DelegateName)
操作函數
BindDynamic(UserObject,FuncName)? ? ? ? 在動態代理上調用BindDynamic()的輔助宏
AddDynamic(UserObject,FuncName)? ? ? ? 在動態多播代理上調用AddDynamic()的輔助宏
RemoveDynamic(UserObject,FuncName)? ? ? ? 在動態多播代理上調用RemoveDynamic()的輔助宏
動態代理與單播代理和多播代理的區別
1.動態代理構建類型名稱需要用F開頭(動態代理實現機制構建了類)
2.動態代理對象類型可以使用UPROPERTY標記,其他代理均無法使用(不加編譯可過,調用會出錯)
3.動態代理綁定對象的函數需要使用UFUNCTION進行描述(因為需要跟隨代理被序列化)
構建
動態單播構建
DECLARE_DYNAMIC_DELEGATE(FGMDynDelegate);? ? ? ? //分號不要漏了
FGMDynDelegate GmDyDele;
GmDyDele.BindDynamic(ActorName,&AMyActor::Say);
GmDyDele.ExcuteIfBound();
動態多播構建操作如上
動態代理用于藍圖
需要注意的是,在構建動態代理提供藍圖使用時,需要在代理上增加標記宏UPROPERTY(BlueprintAssignable)
構建宏
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FGMDynMulDele);
對象聲明
UPROPERTY(BlueprintAssignable,BlueprintReadWrite)
FGMDynMulDele OnGmDynMulDele;
在C++中調用
if(OnGmDynMulDele.IsBound())
{
OnGmDynMulDele.Broadcast();
}
事件
事件本身和多播代理一樣,為了操作的安全性,事件提供了額外的操作限定,即禁止在聲明事件對象的外部調用事件傳播,清理,檢查等函數。通過操作隔離,最大程度的增加了事件的安全性。派生類允許調用事件的廣播
在虛幻C++中事件和多播幾乎相同。只是構建方式略不同
構建宏
事件類型構建宏由于需要限定事件對象調用約束關系,需要提供聲明所在類型,并且需要在類內部進行聲明。事件沒有返回值
如果想要去構建一個代理,這個代理在類外部的地方都不能調用就用事件(雖然說并沒有實際上約束到的效果)
OwningType即當前聲明事件的類
一般構建語法如下:將事件聲明在類的內部,通過函數構建返回事件對象操作
public:
? ? ? ? DECLARE_EVENT(AMyActor,FChangeEvent)? ? ? ? //聲明事件類型
? ? ? ? FChangeEvent& OnChanged(){return ChangeEvent;}
private:
? ? ? ? FChangeEvent ChangeEvent;? ? ? ? //構建事件對象
綁定函數與廣播
AddUObject();
調用函數Broadcast,但是調用不保證執行順序的正確性。事件廣播無需檢查是否存在有效的綁定,事件廣播應發生在事件類型的類內部
移除
FDelegateHandle handle=NotifyEvent.AddUObject(my,&AMyActor::Call);? ? ? ? //使用一個代理句柄變量等出來
NotifyEvent.Remove(handle);? ? ? ? //移除這個代理句柄
標記宏總結:
UE中大部分的對象都是UObject,UObject通過標記宏可以使得UObject處理系統識別到它們,從而實現藍圖和C++之間的交互
標記宏有UCLASS(類), UPROPERTY(成員屬性), UFUNCTION(成員函數),在標記宏的后面括號里可以添加標記,從而實現藍圖與C++中的交互
UCLASS(BlueprintType)? ? ? ? //允許藍圖使用下一行的類的類型
UPROPERTY(BlueprintReadWrite)? ? ? ? //允許藍圖的事件圖表里進行讀寫
UPROPERTY(EditAnywhere)? ? ? ? //允許藍圖的編輯器面板和實例中可以編輯參數
UFUNCTION(BlueprintCallable)? ? ? ? //允許藍圖在事件圖表中調用該函數節點
UFUNCTION中也可以實現藍圖和C++交互的兩個較特殊的標記
1.BlueprintImplementableEvent
2.BlueprintNativeEvent
資源加載總結:
有三種加載方式:
1.通過使用屬性標記宏UPROPERTY(Edit三個都可以)來將資產對象指針暴露到編輯器面板,從而直接從編輯器面板拾取資產
UPROPERTY(EditDefaultsOnly)
UClass* ActorClass;? ? ? ? //用來拾取任意類資產
UPROPERTY(EditDefaultsOnly)? ? ? ????????? //禁止在這里寫注釋,語法錯誤
class USoundCue* SoundSrc;? ? ? ? //用來獲取音頻資產
//UTexture,UMaterial,UStaicMesh,USkeletalMesh
2.在構造函數中可以借助構造函數資產加載類進行資源引用,靜態資源引用類ConstructorHelpers可以進行類引用,源資源引用,ConstructorHelpers只能在構造函數中使用
ConstructHelpers::FClassFinder<APawn> (TEXT("/Game/Flappybird/Blueprints/BP_Bird")).Class
//返回數據類型是TSubClassOf
拾取藍圖對象
ConstructHelpers::FClassFinder<AActor> UnitSelector(TEXT("Blueprint'/Game/Flappybird/Blueprints/MyBlueprint.MyBlueprint_C'"));
//下劃線C必須要加
FObjectFinder語法
ConstructHelpers::FObjectFinder<UTexture2D> BarFillObj(TEXT("/Game/UI/HUD/BarFill"));
BarFillTexture=BarFillObj.Object;? ? ? ? //將獲取的數據內容指針保存
3.LoadClass用來加載類,通過模板約束對象類型,增加操作安全,但是注意,資源加載可能會失敗或無效,需要對操作的結果進行判定
Sound=LoadObject<USoundCue>(nullptr,TEXT("路徑"));
SubActorClass=LoadClass<AActor>(nullptr,TEXT("路徑_C"));
代理總結:
1.單播代理
2.多播代理
3.動態代理
4.事件
總結
以上是生活随笔為你收集整理的虚幻C++入门个人笔记(2)——标记宏、结构体枚举、资源加载、代理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 监测 Windows 应用行为
- 下一篇: C语言中cvpoint后运行出错,c++