深入研究虛幻4反射系統實現原理(二)


上一篇文章中講解了UE4中對類(UCLASS)的反射支持,這篇文章我們還是以實例的形式來講解虛幻4對結構體(USTRUCT)以及枚舉(UENUM)的支持。

結構體

首先讓我們看一下測試結構體反射支持的代碼,我們用USTRUCT聲明了一個結構體,告訴虛幻4 要對這個類型支持反射類型,我們向其中添加了一個float類型的值來測試程序。

#pragma once

#include "ReflectionStructTest.generated.h"

USTRUCT(Blueprintable)
struct FReflectionTest
{
	GENERATED_USTRUCT_BODY()

	UPROPERTY(BlueprintReadWrite)
	float ReflectionValue;
};

  

生成的.generated.h文件

點擊編譯后,我們得到了.generated.h文件,這段代碼就是GENERATED_USTRUCT_BODY()宏展開后對應的內容,代碼比較簡單,如下所示:

// Copyright 1998-2016 Epic Games, Inc. All Rights Reserved.

/*===========================================================================

    C++ class header boilerplate exported from UnrealHeaderTool.

    This is automatically generated by the tools.

    DO NOT modify this manually! Edit the corresponding .h files instead!

===========================================================================*/

#include "ObjectBase.h"

PRAGMA_DISABLE_DEPRECATION_WARNINGS
#ifdef REFLECTIONSTUDY_ReflectionStructTest_generated_h
#error "ReflectionStructTest.generated.h already included, missing '#pragma once' in ReflectionStructTest.h"
#endif

#define REFLECTIONSTUDY_ReflectionStructTest_generated_h

#define ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h_9_GENERATED_BODY \
    friend REFLECTIONSTUDY_API class UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest(); \
    REFLECTIONSTUDY_API static class UScriptStruct* StaticStruct();

#undef CURRENT_FILE_ID
#define CURRENT_FILE_ID ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h
PRAGMA_ENABLE_DEPRECATION_WARNINGS

  

它主要做了以下三件事情:

  • friend REFLECTIONSTUDY_API class UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest() 定義了一個友元函數,用於創建這個結構體的反射對象UScriptStruct
  • REFLECTIONSTUDY_API static class UScriptStruct* StaticStruct(); 定義了一個成員函數StaticStruct(),這樣我們可以通過類來獲取它的反射結構體。
  • #define CURRENT_FILE_ID ReflectionStudy_Source_ReflectionStudy_ReflectionStructTest_h 重新定義了CURRENT_FILE_ID 詳細信息請參照開頭提到的內容有對GENERATED_USTRUCT_BODY()的講解

.generated.cpp中相關的內容

  

    UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest()
    {
        UPackage* Outer = Z_Construct_UPackage__Script_ReflectionStudy();
        extern uint32 Get_Z_Construct_UScriptStruct_FReflectionTest_CRC();
        static UScriptStruct* ReturnStruct = FindExistingStructIfHotReloadOrDynamic(Outer, TEXT("ReflectionTest"), sizeof(FReflectionTest), Get_Z_Construct_UScriptStruct_FReflectionTest_CRC(), false);
        if (!ReturnStruct)
        {
            ReturnStruct = new(EC_InternalUseOnlyConstructor, Outer, TEXT("ReflectionTest"), RF_Public|RF_Transient|RF_MarkAsNative) UScriptStruct(FObjectInitializer(), NULL, new UScriptStruct::TCppStructOps<FReflectionTest>, EStructFlags(0x00000001));
            UProperty* NewProp_ReflectionValue = new(EC_InternalUseOnlyConstructor, ReturnStruct, TEXT("ReflectionValue"), RF_Public|RF_Transient|RF_MarkAsNative) UFloatProperty(CPP_PROPERTY_BASE(ReflectionValue, FReflectionTest), 0x0010000000000004);
            ReturnStruct->StaticLink();

#if WITH_METADATA
            UMetaData* MetaData = ReturnStruct->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnStruct, TEXT("BlueprintType"), TEXT("true"));
            MetaData->SetValue(ReturnStruct, TEXT("IsBlueprintBase"), TEXT("true"));
            MetaData->SetValue(ReturnStruct, TEXT("ModuleRelativePath"), TEXT("ReflectionStructTest.h"));
            MetaData->SetValue(NewProp_ReflectionValue, TEXT("Category"), TEXT("ReflectionTest"));
            MetaData->SetValue(NewProp_ReflectionValue, TEXT("ModuleRelativePath"), TEXT("ReflectionStructTest.h"));
#endif
        }
        return ReturnStruct;
    }

    uint32 Get_Z_Construct_UScriptStruct_FReflectionTest_CRC() { return 486791486U; }

  

從上面的代碼中我們可以得知三點:
  • 創建UScriptStruct並添加到當前工程特定的package中
  • 創建我們上文中添加的ReflectionValue屬性
  • 添加元數據,供編輯器使用,比如我們上面在USTRUCT中指定的BlueprintType

 

 
class UScriptStruct* FReflectionTest::StaticStruct()
{

    extern REFLECTIONSTUDY_API class UPackage* Z_Construct_UPackage__Script_ReflectionStudy();
    static class UScriptStruct* Singleton = NULL;
    if (!Singleton)
    {
        extern REFLECTIONSTUDY_API class UScriptStruct* Z_Construct_UScriptStruct_FReflectionTest();
        extern REFLECTIONSTUDY_API uint32 Get_Z_Construct_UScriptStruct_FReflectionTest_CRC();
        Singleton = GetStaticStruct(Z_Construct_UScriptStruct_FReflectionTest, Z_Construct_UPackage__Script_ReflectionStudy(), TEXT("ReflectionTest"), sizeof(FReflectionTest), Get_Z_Construct_UScriptStruct_FReflectionTest_CRC());
    }
    return Singleton;
}

static FCompiledInDeferStruct Z_CompiledInDeferStruct_UScriptStruct_FReflectionTest(FReflectionTest::StaticStruct, TEXT("/Script/ReflectionStudy"), TEXT("ReflectionTest"), false, nullptr, nullptr);

  

上面的代碼主要做了兩件事情:

  • StaticStruct()判斷Singleton是否為空,如果為空,那么就調用GetStaticStruct(),面GetStaticStruct()就是調用了Z_Construct_UScriptStruct_FReflectionTest()函數。
class UScriptStruct *GetStaticStruct(class UScriptStruct *(*InRegister)(), UObject* StructOuter, const TCHAR* StructName, SIZE_T Size, uint32 Crc)
{
    return (*InRegister)();
}

  

  • 定義了一個靜態全局變量,用於注冊到一個列表中,在引擎初始化的時候調用StaticStruct()方法。
static struct FScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest
{
    FScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest()
    {
        UScriptStruct::DeferCppStructOps(FName(TEXT("ReflectionTest")),new UScriptStruct::TCppStructOps<FReflectionTest>);
    }
} ScriptStruct_ReflectionStudy_StaticRegisterNativesFReflectionTest;

  

  • 定義一個靜態變量用於存儲一個CppStructOps(主要用來 動態獲取結構體的構造和析造函數) ,用於在程序中使用。

    枚舉

    接下來我們來看下枚舉的實現方式,測試代碼如下所示:

UENUM(BlueprintType)
enum class EReflectionTest : uint8
{
    E0,
    E1
};

  

生成的.generated.h文件

編譯代碼,我們在.generated.h文件中會得到如下代碼,它僅僅定義了一個FOREACH_ENUM_EREFLECTIONTEST的宏。

#define FOREACH_ENUM_EREFLECTIONTEST(op) \
    op(EReflectionTest::E0) \
    op(EReflectionTest::E1)

  

.generated.cpp中相關代碼

   UEnum* Z_Construct_UEnum_ReflectionStudy_EReflectionTest()
    {
        UPackage* Outer=Z_Construct_UPackage__Script_ReflectionStudy();
        extern uint32 Get_Z_Construct_UEnum_ReflectionStudy_EReflectionTest_CRC();
        static UEnum* ReturnEnum = FindExistingEnumIfHotReloadOrDynamic(Outer, TEXT("EReflectionTest"), 0, Get_Z_Construct_UEnum_ReflectionStudy_EReflectionTest_CRC(), false);
        if (!ReturnEnum)
        {
            ReturnEnum = new(EC_InternalUseOnlyConstructor, Outer, TEXT("EReflectionTest"), RF_Public|RF_Transient|RF_MarkAsNative) UEnum(FObjectInitializer());
            TArray<TPair<FName, uint8>> EnumNames;
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EReflectionTest::E0")), 0));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EReflectionTest::E1")), 1));
            EnumNames.Add(TPairInitializer<FName, uint8>(FName(TEXT("EReflectionTest::EReflectionTest_MAX")), 2));
            ReturnEnum->SetEnums(EnumNames, UEnum::ECppForm::EnumClass);
            ReturnEnum->CppType = TEXT("EReflectionTest");
#if WITH_METADATA
            UMetaData* MetaData = ReturnEnum->GetOutermost()->GetMetaData();
            MetaData->SetValue(ReturnEnum, TEXT("BlueprintType"), TEXT("true"));
            MetaData->SetValue(ReturnEnum, TEXT("ModuleRelativePath"), TEXT("ReflectionStructTest.h"));
#endif
        }
        return ReturnEnum;
    }

    uint32 Get_Z_Construct_UEnum_ReflectionStudy_EReflectionTest_CRC() { return 1111016117U; }

  

上面代碼的主要作用就是:

  • 查看反射的UEnum有沒有生成,如果沒有生成,那么會new一個UEnum並且將我們定義的E0,E1,兩個枚舉添加進來,而且默認都會添加一個 '枚舉名+_Max'的一個枚舉值
  • 注冊編輯器需要的元數據,比如我們在UENUM()中添加的BlueprintType
static class UEnum* EReflectionTest_StaticEnum()
{
    extern REFLECTIONSTUDY_API class UPackage* Z_Construct_UPackage__Script_ReflectionStudy();
    static class UEnum* Singleton = NULL;
    if (!Singleton)
    {
        extern REFLECTIONSTUDY_API class UEnum* Z_Construct_UEnum_ReflectionStudy_EReflectionTest();
        Singleton = GetStaticEnum(Z_Construct_UEnum_ReflectionStudy_EReflectionTest, Z_Construct_UPackage__Script_ReflectionStudy(), TEXT("EReflectionTest"));
    }
    return Singleton;
}

static FCompiledInDeferEnum Z_CompiledInDeferEnum_UEnum_EReflectionTest(EReflectionTest_StaticEnum, TEXT("/Script/ReflectionStudy"), TEXT("EReflectionTest"), false, nullptr, nullptr);

  

這個代碼跟上面結構的比較相似,也是主要做了兩件事情:

  • EReflectionTest_StaticEnum()判斷Singleton是不是為空,如果為空那么通過GetStaticEnum來創建或返回UEnum對象,具體可參考GetStaticEnum實現。
  • 向一個列表中注冊EReflectionTest_StaticEnum()函數,用於在引擎啟動的時候調用。

到這里關於虛幻4中怎樣對類、結構體、函數、屬性、變量支持反射有了一個簡單的了解,相信讀者你也有了一定的認識。但是具體怎么注冊到引擎的初始化列表,以及是如何調用的,我們這里並沒有展開來講,限於篇幅問題,我們下一篇文章再來講解着方面的內容,當然如果可以我也會把在C++中支持反射類型的幾種方式也介紹一下。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM