12周岁女裸体啪啪自慰网站_18岁免费网站_狠狠色综合播放一区二区_伊大人香蕉综合8在线视_亚洲精品无码久久久久小霞

您當(dāng)前的位置: 首頁 >> 智能 > >> 內(nèi)容頁

UnrealEngine - 反射系統(tǒng)分析_天天資訊

2023-03-30 15:32:44 來源:博客園
1. 反射

什么是反射?或者說反射能做什么,簡單來說,反射可以提供一種能力,能夠在運(yùn)行時動態(tài)獲取對象的成員信息,如成員函數(shù)成員變量。UE 在其反射系統(tǒng)上支持了許多功能,如:

編輯器中可供編輯的屬性GC序列化網(wǎng)絡(luò)同步1.1 使用反射的準(zhǔn)備工作

UE 中應(yīng)用反射需要與它定義的宏相結(jié)合,主要有 3 種類型,如下所示:

類注冊
#include "Weapon.generated.h" // 包含自動生成的頭文件信息UCLASS() // 注冊類信息class AWeapon : public AActor {GENERATED_BODY() // 生成類輔助代碼public:UPROPERTY()  // 注冊類屬性FName WeaponName;UFUNCTION() // 注冊類成員函數(shù)void Fire();}
結(jié)構(gòu)體注冊(需要注意的是,UFUNCTION只能在 Class 中使用)
#include "Weapon.generated.h" // 包含自動生成的頭文件信息USTRUCT() // 注冊結(jié)構(gòu)體struct FWeapon {UPROPERTY() // 注冊結(jié)構(gòu)體屬性FName WeaponName;}
枚舉注冊
#include "Weapon.generated.h" // 包含自動生成的頭文件信息UENUM() // 注冊枚舉信息enum WeaponType {Short,Middle,Far,}
1.2 反射的簡單應(yīng)用

前面注冊完畢反射后,就能簡單的使用反射了,如下:


(資料圖片)

#include "human.generated.h" // 包含自動生成的頭文件信息/** UHuman.h **/class UHuman {public:UPROPERTY()FString Name = "Hello, Reflection!!!";UPROPERTY()UHuman* Child;}UHuman* Human = NewObject();UClass* UCHuman = UHuman::StaticClass();// 轉(zhuǎn)為對應(yīng)的Propertyif (FStrProperty* StrProperty = CastField(Property)){    // 取Property地址(因?yàn)閷傩韵到y(tǒng)知道屬性在類內(nèi)存中的偏移值)    void* PropertyAddr = StrProperty->ContainerPtrToValuePtr(Human);    // 通過地址取值(其實(shí)就是類型轉(zhuǎn)換,畢竟我們都拿到內(nèi)存地址了)    FString PropertyValue = StrProperty->GetPropertyValue(PropertyAddr);    UE_LOG(LogTemp, Warning, TEXT("Property"s Value is %s"), *PropertyValue);}

但是這種使用只是最粗淺的使用,更多時候反射的應(yīng)用對我們來說是無感知的,如網(wǎng)絡(luò)同步,編輯器的屬性編輯等,都是建立在反射系統(tǒng)之上的,反射系統(tǒng)更多是一個基層系統(tǒng),輔助構(gòu)建其他高層次的系統(tǒng)。

2. 反射整體結(jié)構(gòu)

UE 的反射系統(tǒng)其整體的結(jié)構(gòu)如下:

總體來說,其各種結(jié)構(gòu)對應(yīng)收集不同類型的反射信息:

UClass :收集類數(shù)據(jù),描述一個類的成員變量,函數(shù),父類等信息UEnum:收集枚舉數(shù)據(jù)UScriptStruct :收集結(jié)構(gòu)體數(shù)據(jù)UFunction:收集函數(shù)信息以 UClass 為例,其采用 FProperty來儲存所有的簡單屬性信息(如 Bool,Int),而一些復(fù)合類型數(shù)據(jù)則使用 UField存儲(如 AActor,TArray)。這里需要認(rèn)識到:UClass 等反射結(jié)構(gòu)其本質(zhì)上只是描述一個類的結(jié)構(gòu),本身與業(yè)務(wù)類無實(shí)際耦合關(guān)系,每個標(biāo)記了 UCLASS(...)宏的 class 都會有一個 UClass* Object儲存其反射信息。3. 構(gòu)建流程

從寫代碼的角度來說,我們只需要對變量,類等定義標(biāo)注一個 宏,再 include 一個頭文件就完事了,具體構(gòu)建的過程則是由 UE 的編譯工具去完成的。也就是 Unreal Build ToolUBT) 和 Unreal Header ToolUHT)。接下來以前面的 class AWeapon為例,展示其自動生成的內(nèi)容和如何初始化其反射信息。

[!note]UHT是一個用于預(yù)處理源代碼文件的工具,它可以識別 UCLASS、UFUNCTION等宏,并通過生成額外的 C++ 代碼來擴(kuò)展類的功能。UHT還可以用于生成反射信息,例如類的元數(shù)據(jù)和屬性信息,以便在運(yùn)行時進(jìn)行藍(lán)圖交互等操作。

UBT是一個用于編譯和鏈接 UE4 項(xiàng)目的構(gòu)建系統(tǒng)。它可以自動管理項(xiàng)目中的依賴項(xiàng),并生成可執(zhí)行文件和動態(tài)鏈接庫等二進(jìn)制文件。UBT 還可以執(zhí)行諸如打包、部署和測試等其他任務(wù)。

兩個工具在 UE4 開發(fā)中密切相關(guān),因?yàn)?UHT生成的反射信息需要在 UBT中使用,以便生成最終的可執(zhí)行文件和動態(tài)鏈接庫。因此,在構(gòu)建 UE4 項(xiàng)目時,UBT將首先調(diào)用 UHT來處理源代碼文件,然后使用生成的代碼來編譯和鏈接項(xiàng)目。

3.1 自動生成文件

在 [[原理#^644683|1.1 使用反射的準(zhǔn)備工作]] 中,主要工作分為兩步:

標(biāo)注宏信息(如 UCLASS,UFUNCTION,UPROPERTY)包含頭文件 #include ${filename}.generated.h這里頭文件是利用 UHT 工具掃描生成的,其附帶還會生成一個 ${filename}.gen.cpp的源文件。這兩個文件主要負(fù)責(zé)兩件事情:定義一個或多個輔助類(根據(jù) UCLASSUSTRUCT等標(biāo)注的結(jié)構(gòu)數(shù)量),收集標(biāo)注了宏信息的結(jié)構(gòu),該輔助類構(gòu)造函數(shù)會返回一個構(gòu)造好的 UClass定義一個 FCompileDeferInfo靜態(tài)變量,其構(gòu)造函數(shù)會在啟動時將輔助類的信息導(dǎo)入到一個全局的容器中,啟動時會遍歷這個容器,構(gòu)建好 UClass 等反射信息。其大致流程如下:3.2 預(yù)生成代碼

接下來分析預(yù)先生成的 generated.h 和 gen.cpp 都做了什么事情一個 Class 需要注冊反射信息時,其使用方式如下(有一個必要的前提條件為該 Class 的繼承鏈中需要有 UObject):

#include "Weapon.generated.h" // 包含自動生成的頭文件信息UCLASS() // 注冊類信息class AWeapon : public AActor {GENERATED_BODY() // 生成類輔助代碼public:UPROPERTY()  // 注冊類屬性FName WeaponName;UFUNCTION() // 注冊類成員函數(shù)void Fire();}

可以看到其相關(guān)的宏主要有如下幾個:

UCLASSGENERATED_BODYUPROPERTYUFUNCTION這里首先需要了解這些宏背后都做了什么3.2.1 宏展開

關(guān)鍵的宏定義如下:

/* 將 ABCD 4 個名稱鏈接起來*/#define BODY_MACRO_COMBINE_INNER(A,B,C,D) A##B##C##D#define BODY_MACRO_COMBINE(A,B,C,D) BODY_MACRO_COMBINE_INNER(A,B,C,D)/* 拼接成另一個宏 */#define UCLASS(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_PROLOG)#define GENERATED_BODY(...) BODY_MACRO_COMBINE(CURRENT_FILE_ID,_,__LINE__,_GENERATED_BODY);/* 純標(biāo)記,用給 UHT 掃描 */#define UPROPERTY(...)  #define UFUNCTION(...)

以 3.2 的示例為例,展開后內(nèi)容大致如下:

UdemyProject_Source_UdemyProject_AWeapon_h_3_PROLOG // 注冊類信息class AWeapon : public AActor {UdemyProject_Source_UdemyProject_AWeapon_h_5_GENERATED_BODY // 生成類輔助代碼public:UPROPERTY()  // 由于是標(biāo)記,這里展開之后是沒有特殊信息的FName WeaponName;UFUNCTION() // 由于是標(biāo)記,這里展開之后是沒有特殊信息的void Fire();}

可以看到展開后是一個個神秘的符號,其實(shí)這都是宏的名稱,其定義在自動生成的 generated.h 文件中。這里展示了一個特點(diǎn),盡管不同的類都使用的相同的宏,但是 UHT 還是能保證掃描生成的文件信息唯一性。這里主要關(guān)注兩個宏:

GENERATED_BODY_LEGACYGENERATED_BODY接著展示一下兩個宏其對應(yīng)的文件信息。
#define UdemyProject_Source_UdemyProject_AWeapon_h_3_PROLOG  #define UdemyProject_Source_UdemyProject_AWeapon_h_5_GENERATED_BODY_LEGACY \  PRAGMA_DISABLE_DEPRECATION_WARNINGS \  public: \     UdemyProject_Source_UdemyProject_AWeapon_h_5_PRIVATE_PROPERTY_OFFSET \     UdemyProject_Source_UdemyProject_AWeapon_h_5_SPARSE_DATA \     UdemyProject_Source_UdemyProject_AWeapon_h_5_RPC_WRAPPERS \     UdemyProject_Source_UdemyProject_AWeapon_h_5_INCLASS \     UdemyProject_Source_UdemyProject_AWeapon_h_5_STANDARD_CONSTRUCTORS \  public: \#define UdemyProject_Source_UdemyProject_AWeapon_h_5_GENERATED_BODY \  PRAGMA_DISABLE_DEPRECATION_WARNINGS \  public: \     UdemyProject_Source_UdemyProject_AWeapon_h_5_PRIVATE_PROPERTY_OFFSET \     UdemyProject_Source_UdemyProject_AWeapon_h_5_SPARSE_DATA \     UdemyProject_Source_UdemyProject_AWeapon_h_5_RPC_WRAPPERS_NO_PURE_DECLS \     UdemyProject_Source_UdemyProject_AWeapon_h_5_INCLASS_NO_PURE_DECLS \     UdemyProject_Source_UdemyProject_AWeapon_h_5_ENHANCED_CONSTRUCTORS \  private: \

可以看到 GENERATED_BODY_LEGACYGENERATED_BODY的內(nèi)容基本一致,查閱資料發(fā)現(xiàn)這主要是為了前向兼容。因此可以先忽略 GENERATED_BODY_LEGACY內(nèi)容,關(guān)注 GENERATED_BODY的內(nèi)容??梢钥吹?GENERATED_BODY又嵌套了一堆宏(宏的定義在自動生成的 generated.h 頭文件),其展開之后才是真正的代碼,比如

/* UFUNCTION Wrapper 函數(shù) */#define UdemyProject_Source_UdemyProject_AWeapon_h_5_RPC_WRAPPERS_NO_PURE_DECLS \   DECLARE_FUNCTION(execFire);

可以對其完整展開,還原其最終的樣貌

/* 該宏可以忽略 */UdemyProject_Source_UdemyProject_AWeapon_h_5_GENERATED_BODY_LEGACY  class AWeapon : public AActor {public:/* UdemyProject_Source_UdemyProject_AWeapon_h_5_RPC_WRAPPERS_NO_PURE_DECLSUFunction 的 Wrapper Function 集合*/static void execFire( UObject* Context, FFrame& Stack, RESULT_DECL );private: static void StaticRegisterNativesAWeapon(); friend struct Z_Construct_UClass_AWeapon_Statics; public: /* DECLARE_CLASS(AWeapon, AActor, COMPILED_IN_FLAGS(0 | CLASS_Config), CASTCLASS_None, TEXT("/Script/UdemyProject"), NO_API) 類輔助定義相關(guān) */private: \      AWeapon& operator=(AWeapon&&);   \      AWeapon& operator=(const AWeapon&);   \     TRequiredAPI static UClass* GetPrivateStaticClass(); \  public: \     /** Bitwise union of #EClassFlags pertaining to this class.*/ \     enum {StaticClassFlags=COMPILED_IN_FLAGS(0 | CLASS_Config}; \     /** Typedef for the base class ({{ typedef-type }}) */ \     typedef AActor Super;\     /** Typedef for {{ typedef-type }}. */ \     typedef AWeapon ThisClass;\     /** Returns a UClass object representing this class at runtime */ \     inline static UClass* StaticClass() \     { \        return GetPrivateStaticClass(); \     } \     /** Returns the package this class belongs in */ \     inline static const TCHAR* StaticPackage() \     { \        return TEXT("/Script/UdemyProject"); \     } \     /** Returns the static cast flags for this class */ \     inline static EClassCastFlags StaticClassCastFlags() \     { \        return CASTCLASS_None;    }    /** For internal use only; use StaticConstructObject() to create new objects. */    inline void* operator new(const size_t InSize, EInternal InInternalOnly, UObject* InOuter = (UObject*)GetTransientPackage(), FName InName = NAME_None, EObjectFlags InSetFlags = RF_NoFlags)    {       return StaticAllocateObject(StaticClass(), InOuter, InName, InSetFlags);    }    /** For internal use only; use StaticConstructObject() to create new objects. */    inline void* operator new( const size_t InSize, EInternal* InMem ) \     {       return (void*)InMem;    }/* 序列化相關(guān) */friend FArchive &operator<<( FArchive& Ar, AWeapon*& Res ) {    return Ar << (UObject*&)Res; }friend void operator<<(FStructuredArchive::FSlot InSlot, AWeapon*& Res) \  {    InSlot << (UObject*&)Res;}/* 構(gòu)造函數(shù)相關(guān) */    /** Standard constructor, called after all reflected properties have been initialized */    NO_API AWeapon(const FObjectInitializer& ObjectInitializer = FObjectInitializer::Get()) : Super(ObjectInitializer) { }; private:    /** Private move- and copy-constructors, should never be used */    NO_API AWeapon(AWeapon&&);   NO_API AWeapon(const AWeapon&); public: /* 默認(rèn)構(gòu)造函數(shù) */NO_API AWeapon(FVTableHelper& Helper);static UObject* __VTableCtorCaller(FVTableHelper& Helper){   return new (EC_InternalUseOnlyConstructor, (UObject*)GetTransientPackage(), NAME_None, RF_NeedLoad | RF_ClassDefaultObject | RF_TagGarbageTemp) AWeapon(Helper);}   static void __DefaultConstructor(const FObjectInitializer& X) { new((EInternal*)X.GetObj())AWeapon(X); }public:UPROPERTY()  // 注冊類屬性FName WeaponName;UFUNCTION() // 注冊類成員函數(shù)void Fire();}

可以看到 GENERATED_BODY宏為 AWeapon擴(kuò)展了很多功能,包括但不限于:

增加了構(gòu)造函數(shù)增加了序列化功能UFunction 增加 Wrapper Function 以供調(diào)用增加獲取當(dāng)前父類以及當(dāng)前類的 UClass 功能3.2.2 gen.cpp 內(nèi)容分析

gen.cpp 的內(nèi)容主要為構(gòu)建好描述 AWeapon反射信息的 UClass

UFUNCTION 相關(guān)代碼

首先以 AWeapon::Fire為例,對其標(biāo)記 UFUNCTION后檢查其生成的相關(guān)內(nèi)容大致如下:

實(shí)現(xiàn) Wrapper Function 的內(nèi)容,這個接口主要供 藍(lán)圖 或者 RPC 使用。
DEFINE_FUNCTION(AWeapon::execFire)  {     P_FINISH;     P_NATIVE_BEGIN;     P_THIS->Fire();  // 實(shí)際上就是調(diào)用了下 Navtive 的 Fire 函數(shù)   P_NATIVE_END;  }
生成一個結(jié)構(gòu)體 FFunctionParams,儲存構(gòu)建 UFunction所需的參數(shù),并提供構(gòu)建 UFunction的方法,參數(shù)內(nèi)容主要分為:函數(shù)的標(biāo)記(比如標(biāo)記為 Server 或者 Client 等)函數(shù)的名稱函數(shù)的參數(shù)和返回值(其統(tǒng)一用一個 List 存儲,每個元素會有一個 Flag 標(biāo)記其是引用還是返回值還是普通參數(shù))參數(shù)的數(shù)量
/* 定義一個結(jié)構(gòu)體,參數(shù)為構(gòu)建一個 UFunction 所需要的參數(shù) */    struct Z_Construct_UFunction_AWeapon_Fire_Statics     {        static const UE4CodeGen_Private::FFunctionParams FuncParams;     };   /* 初始化一個結(jié)構(gòu)體 */   const UE4CodeGen_Private::FFunctionParams Z_Construct_UFunction_AWeapon_Fire_Statics::FuncParams =    { (UObject*(*)())Z_Construct_UClass_AWeapon,    nullptr,    "Fire",    nullptr,    nullptr,    0,    nullptr,    0,    RF_Public|RF_Transient|RF_MarkAsNative,(EFunctionFlags)0x00020401,0,0, METADATA_PARAMS(Z_Construct_UFunction_AWeapon_Fire_Statics::Function_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UFunction_AWeapon_Fire_Statics::Function_MetaDataParams)) };  /* 生成一個構(gòu)造方法,用來構(gòu)造 AWeapon::Fire 的 UFunction 信息 */UFunction* Z_Construct_UFunction_AWeapon_Fire()  {     static UFunction* ReturnFunction = nullptr;     if (!ReturnFunction)     {      UE4CodeGen_Private::ConstructUFunction(ReturnFunction, Z_Construct_UFunction_AWeapon_Fire_Statics::FuncParams);     }   return ReturnFunction;  }
UPROPERTY 相關(guān)代碼

類似生成 UFunction,此處由于 WeaponName是基礎(chǔ)類型,所以直接初始化一個 FNamePropertyParams的結(jié)構(gòu)體。這里面就包含了:

變量的名稱變量的 Flag(比如標(biāo)記為 Replicated)變量的偏移(方便從類指針從偏移獲取該變量)
const UE4CodeGen_Private::FNamePropertyParams Z_Construct_UClass_AWeapon_Statics::NewProp_WeaponName = { "WeaponName",nullptr,(EPropertyFlags)0x0010000000000000,UE4CodeGen_Private::EPropertyGenFlags::Name,RF_Public|RF_Transient|RF_MarkAsNative,1,STRUCT_OFFSET(AWeapon, WeaponName), METADATA_PARAMS(Z_Construct_UClass_AWeapon_Statics::NewProp_WeaponName_MetaData,UE_ARRAY_COUNT(Z_Construct_UClass_AWeapon_Statics::NewProp_WeaponName_MetaData)) };  
UCLASS 相關(guān)代碼

前面定義的函數(shù)和成員變量的代碼都已經(jīng)生成完畢了,接下來看具體是如何將其結(jié)合到 Class 中的。首先 gen.cpp 中會生成代碼將 Function 和 Property 分開存儲,定義如下:

/** 成員變量 **/const UE4CodeGen_Private::FPropertyParamsBase* const Z_Construct_UClass_AWeapon_Statics::PropPointers[] = {     (const UE4CodeGen_Private::FPropertyParamsBase*)&Z_Construct_UClass_AWeapon_Statics::NewProp_WeaponName,  };/** 成員函數(shù) **/const FClassFunctionLinkInfo Z_Construct_UClass_AWeapon_Statics::FuncInfo[] = {     { &Z_Construct_UFunction_AWeapon_Fire, "Fire" }, // 2996945510  };

接著提供構(gòu)建 AWeaponUClass信息,類似構(gòu)建 UFunction一般,其填充了一個 FClassParams的結(jié)構(gòu)體,主要內(nèi)容包括但不限于:

成員變量列表函數(shù)列表類標(biāo)記(即 UCLASS 宏中標(biāo)記)
const UE4CodeGen_Private::FClassParams Z_Construct_UClass_AWeapon_Statics::ClassParams = {     &AWeapon::StaticClass,     "Engine",     &StaticCppClassTypeInfo,     DependentSingletons,     FuncInfo,     Z_Construct_UClass_AWeapon_Statics::PropPointers,     nullptr,     UE_ARRAY_COUNT(DependentSingletons),     UE_ARRAY_COUNT(FuncInfo),     UE_ARRAY_COUNT(Z_Construct_UClass_AWeapon_Statics::PropPointers),     0,     0x008000A4u,     METADATA_PARAMS(Z_Construct_UClass_AWeapon_Statics::Class_MetaDataParams, UE_ARRAY_COUNT(Z_Construct_UClass_AWeapon_Statics::Class_MetaDataParams))  };

然后提供一個構(gòu)建 UClass的接口

UClass* Z_Construct_UClass_AWeapon()  {     static UClass* OuterClass = nullptr;     if (!OuterClass)     {      UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_AWeapon_Statics::ClassParams);     }   return OuterClass;  }

至此,整個類的自動生成的反射代碼基本描述完了。

3.2.3 小結(jié)

3.2 主要闡述自動生成的代碼內(nèi)容大致是什么東西,個人認(rèn)為主要分為如下幾點(diǎn):

AWeapon增加輔助接口(比如 Super,StaticClass,構(gòu)造函數(shù)等)生成 AWeapon中所有標(biāo)記了 UPROPERTYUFUNCTION的反射代碼和構(gòu)建接口生成 AWeapon這個 Class 的反射代碼和構(gòu)建接口最后將接口暴露出去給引擎初始化調(diào)用即可。3.3 初始化反射信息

3.2 中預(yù)生成的代碼已經(jīng)封裝好所有反射結(jié)構(gòu)的接口了,接下來只要調(diào)用就可以生成 AWeapon的反射信息了。

3.3.1 入口調(diào)用

UE 中反射信息主要是在引擎啟動時初始化的,主要利用 gen.cpp 中自動生成的一個靜態(tài)變量

static FCompiledInDefer Z_CompiledInDefer_UClass_AWeapon(Z_Construct_UClass_AWeapon, &AWeapon::StaticClass, TEXT("/Script/UdemyProject"), TEXT("AWeapon"), false, nullptr, nullptr, nullptr);

其構(gòu)造函數(shù)會將 構(gòu)造 AWeapon的 反射接口傳入到一個全局容器,啟動時會調(diào)用 UObjectLoadAllCompiledInDefaultProperties遍歷構(gòu)造好 UClass。大致偽代碼如下:

// DeferredCompiledInRegistration 存儲了 Z_Construct_UClass_AWeaponstatic void UObjectLoadAllCompiledInDefaultProperties(){TArray PendingRegistrants = MoveTemp(DeferredCompiledInRegistration);  for (UClass* (*Registrant)() : PendingRegistrants)  {  // 此處調(diào)用 Registrant,也就會調(diào)用 Z_Construct_UClass_AWeaponUClass* Class = Registrant();  /* 省略一些代碼 */NewClasses.Add(Class);  }}
3.3.2 構(gòu)建反射信息

AWeapon 反射信息的構(gòu)建入口如下:

UClass* Z_Construct_UClass_AWeapon()  {  static UClass* OuterClass = nullptr;  if (!OuterClass)  {         UE4CodeGen_Private::ConstructUClass(OuterClass, Z_Construct_UClass_AWeapon_Statics::ClassParams);  }   return OuterClass;  }

即使有多個 AWeapon 對象也是共用一個 UClass 來描述反射信息。其具體的調(diào)用鏈如下(下面的 AWeapon 可替換為任意自定義的 Class):

4. QA4.1 如何利用 UClass 構(gòu)建一個對象

以 SpawnActor 為例,其接口格式如下:

AActor* UWorld::SpawnActor( UClass* Class, FVector const* Location, FRotator const* Rotation, const FActorSpawnParameters& SpawnParameters )

UClass*參數(shù)可以通過如 AWeapon::StaticClass()或者 TSubClassOf()獲取,核心調(diào)用鏈如下:

準(zhǔn)備構(gòu)建參數(shù),檢查 SpawnParameters.template,如果不存在則使用 CDO (每個 UClass 創(chuàng)建時會有對應(yīng)描述的 Class 的 Default Object,可以認(rèn)為是調(diào)用了 Class 的默認(rèn)構(gòu)造函數(shù)構(gòu)建出來的)調(diào)用 NewObjectStaticConstructObject_InternalStaticAllocateObject檢查對象是否已經(jīng)存在不存在則調(diào)用 AllocateUObject 分配一個 UObject調(diào)用 UClass->ClassConstructor在 UObject 上構(gòu)建對應(yīng)類返回 Actor4.2 UClass 如何獲取描述類的構(gòu)造函數(shù)

4.1 中說到,UClass是利用 ClassConstructor來構(gòu)建對應(yīng)描述的 Class 對象的,ClassConstructor初始化的時機(jī)在于構(gòu)建 UClass。UClass的構(gòu)建通過調(diào)用 TClass::StaticClass,具體執(zhí)行流程參考 [[Pasted image 20230329232659.png|3.3.2]] 中第二步初始化 UClass。其具體初始化方式便是通過宏 DECLARE_CLASSIMPLEMENT_CLASS來生成相應(yīng)代碼并將其傳入到構(gòu)建 UClass 的一環(huán)中。

4.3 UFunction 如何存儲參數(shù)及返回值

回顧類圖。

UFunction 的所有參數(shù)和返回值都存儲在父類 UStruct::PropertyLink,這是一個鏈表結(jié)構(gòu),元素類型為 FProperty,通過遍歷并且做標(biāo)記比對來判斷 Property 是參數(shù)還是返回值,以獲取返回值為例,其操作如下:

/** 獲取 UFunction 返回值 **/FProperty* UFunction::GetReturnProperty() const  {  for( TFieldIterator It(this); It && (It->PropertyFlags & CPF_Parm); ++It )  {      if( It->PropertyFlags & CPF_ReturnParm )  {return *It;  }   } return NULL;  }
4.4 UFunction 的執(zhí)行

首先在 UE 中,粗分下來有兩種函數(shù):

藍(lán)圖函數(shù)C++ 函數(shù)UE 中用了一個 FUNC_Native標(biāo)記來區(qū)分,Native 函數(shù)是 C++ 函數(shù),非 Native 函數(shù)則是藍(lán)圖函數(shù)。當(dāng)執(zhí)行 UFunction 時,需要調(diào)用 UFunction::Invoke接口。接口會調(diào)用 UFunction::Func函數(shù)指針。當(dāng) UFunction 類型為 Native 時,F(xiàn)unc 指向?qū)嶋H調(diào)用的函數(shù),反之 Func 則指向 UObject::ProcessInternal。

藍(lán)圖函數(shù)的調(diào)用原理涉及到藍(lán)圖虛擬機(jī),在[[藍(lán)圖與 CPP 之間相互調(diào)用|藍(lán)圖篇]]做補(bǔ)充。

4.5 RPC 函數(shù)如何執(zhí)行的

這里以純 C++ 實(shí)現(xiàn)武器開火為例,開火顯然是一個需要服務(wù)器認(rèn)證的 Function,為了能夠在客戶端上調(diào)用,服務(wù)器上執(zhí)行,需要加上 Server 標(biāo)記

#include "Weapon.generated.h" // 包含自動生成的頭文件信息UCLASS() // 注冊類信息class AWeapon : public AActor {GENERATED_BODY()public:UFUNCTION(Server) /* client 調(diào)用,Server 執(zhí)行 */void Fire(); /* 定義時只需要定義 Fire_Implementation */}

接著需要在 Weapon.cpp 中定義 void Fire_Implementation()接口,此接口為服務(wù)器收到請求后執(zhí)行的接口。 在調(diào)用開火時,只需要如下操作,就可以從 client 調(diào)用到 server 的 fire 函數(shù):

AWeapon* Weapon = GetWeapon();Weapon->Fire();

這里的原理是 UHT 在對 RPC 函數(shù)會在 gen.cpp 中額外生成一個新的函數(shù)定義,格式如下:

/* gen.cpp */void AWeapon::Fire()  {     ProcessEvent(FindFunctionChecked(NAME_AWeapon_Fire),NULL);  }

UObject::ProcessEvent接口會調(diào)用 UObject::CallRemoteFuntion將請求發(fā)送到服務(wù)器,服務(wù)器接受到請求后再利用反射查詢要執(zhí)行的函數(shù)名稱和對象,再對其進(jìn)行執(zhí)行。

/* gen.cpp */// 函數(shù)名稱及執(zhí)行函數(shù)關(guān)聯(lián)起來static const FNameNativePtrPair Funcs[] = {{"Fire", &AWeapon::execFire},}// 服務(wù)器執(zhí)行的函數(shù)定義DEFINE_FUNCTION(AWeapon::execFire)  {     P_FINISH;     P_NATIVE_BEGIN;     P_THIS->SpawnHero13();     P_NATIVE_END;  }

其執(zhí)行流程大致如下:

關(guān)鍵詞:
分享到:
x 廣告
x 廣告

Copyright ©  2015-2022 華中時尚網(wǎng)版權(quán)所有  備案號:京ICP備12018864號-26   聯(lián)系郵箱:2 913 236 @qq.com