目录

UE虚幻引擎结构分析:UE基础类、引擎结构、Class继承关系

UE核心数据结构

  1. UE的主要逻辑代码,位于如下路径
UnrealEngine-4.27.2-release\Engine\Source\Runtime\CoreUObject

TArray

  1. 这是一个UE中通用的队列类,可以灵活的存储不同类型的数据
// @Array.h
template<typename T>
class TArray
{
public:
    T* Data;
    ULONG ItemNum;
    ULONG Max;
}

FString类

  1. FString可以认为是继承了TArray
  2. 一个FString大小为16字节
// @UnrealString.h
class FString : public TArray<wchar_t*>
{
public:
}

UObjectBase

// @UObjectBase.h
class UObjectBase 
{
public:
	PVOID VMethodTable;		
	
    /** Flags used to track and report various object states. This needs to be 8 byte aligned on 32-bit
    platforms to reduce memory waste */
	EObjectFlags					ObjectFlags;

	/** Index into GObjectArray...very private. */
	int32							InternalIndex;

	/** Class the object belongs to. */
	UClass*							ClassPrivate;

	/** Name of this object */
	FName							NamePrivate;		// 0x18偏移

	/** Object this object resides in. */
	UObject*						OuterPrivate;
}

FName

// @NameTypes.h

/** Externally, the instance number to represent no instance number is NAME_NO_NUMBER, 
    but internally, we add 1 to indices, so we use this #define internally for 
	zero'd memory initialization will still make NAME_None as expected */
#define NAME_NO_NUMBER_INTERNAL	0
// @UnrealNames.h
class FName
{
public:
     /**
	 * Returns the name of this object (with no path information)
	 * 
	 * @return Name of the object.
	 */
	FORCEINLINE FString GetName() const
	{
		return GetFName().ToString();
	}
    
    const FNameEntry* FName::GetDisplayNameEntry() const
    {
        // 这里终于出现NamePool了,同时注意这里的取GName的指针
        return &GetNamePool().Resolve(GetDisplayIndex());
    }
    
   	 /**
	 * Converts an FName to a readable format
	 *
	 * @return String representation of the name
	 */
    FString FName::ToString() const
	{
        if (GetNumber() == NAME_NO_NUMBER_INTERNAL)
        {
            // Avoids some extra allocations in non-number case
            return GetDisplayNameEntry()->GetPlainNameString();
        }
	
        FString Out;	
        ToString(Out);
        return Out;
	}
    
}

NamePoolData

// @ UnrealNames.cpp

// 虚幻引擎中,GName其实就是NamePoolData,是一个静态的全局变量,通过GetNamePool返回其指针
static bool bNamePoolInitialized;
alignas(FNamePool) static uint8 NamePoolData[sizeof(FNamePool)];

// Only call this once per public FName function called
//
// Not using magic statics to run as little code as possible
static FNamePool& GetNamePool()
{
	if (bNamePoolInitialized)
	{
		return *(FNamePool*)NamePoolData;
	}

	FNamePool* Singleton = new (NamePoolData) FNamePool;
	bNamePoolInitialized = true;
	return *Singleton;
}

FNamePool

// 这个就是所谓的GName,管理着所有的命名对象

// @UnrealNames.cpp
class FNamePool
{
public:
    // 构造方法
    FNamePool::FNamePool()
    {
        for (FNamePoolShardBase& Shard : ComparisonShards)
        {
            Shard.Initialize(Entries);
        }

    #if WITH_CASE_PRESERVING_NAME
        for (FNamePoolShardBase& Shard : DisplayShards)
        {
            Shard.Initialize(Entries);
        }
    #endif

        // Register all hardcoded names
    #define REGISTER_NAME(num, name) ENameToEntry[num] = Store(FNameStringView(#name, FCStringAnsi::Strlen(#name)));
    #include "UObject/UnrealNames.inl"
    #undef REGISTER_NAME

        // Make reverse mapping
        LargestEnameUnstableId = 0;
        for (uint32 ENameIndex = 0; ENameIndex < NAME_MaxHardcodedNameIndex; ++ENameIndex)
        {
            if (ENameIndex == NAME_None || ENameToEntry[ENameIndex])
            {
                EntryToEName.Add(ENameToEntry[ENameIndex], (EName)ENameIndex);
                LargestEnameUnstableId = FMath::Max(LargestEnameUnstableId, ENameToEntry[ENameIndex].ToUnstableInt());
            }
        }

        // Verify all ENames are unique
        if (NumAnsiEntries() != EntryToEName.Num())
        {
            // we can't print out here because there may be no log yet if this happens before main starts
            if (FPlatformMisc::IsDebuggerPresent())
            {
                UE_DEBUG_BREAK();
            }
            else
            {
                FPlatformMisc::PromptForRemoteDebugging(false);
                FMessageDialog::Open(EAppMsgType::Ok, NSLOCTEXT("UnrealEd", "DuplicatedHardcodedName", "Duplicate hardcoded name"));
                FPlatformMisc::RequestExit(false);
            }
        }
    }
}

UObject/UnrealNames.inl

// Special zero value, meaning no name.
REGISTER_NAME(0,None)

// Class property types (name indices are significant for serialization).
REGISTER_NAME(1,ByteProperty)		// 这就是为什么IDA搜索ByteProperty可以快速定位GName
REGISTER_NAME(2,IntProperty)
REGISTER_NAME(3,BoolProperty)
REGISTER_NAME(4,FloatProperty)
...

UE变量命名规范(前缀)

  • 所有代码和注释都应采用美式标准英语的拼写和语法。
  • 命名(如类型或变量)中的每个单词需大写首字母,单词间通常无下划线。例如:Health 和 UPrimitiveComponent,而非 lastMouseCoordinates 或 delta_coordinates。
  • 类型名前缀需使用额外的大写字母,用于区分其和变量命名。例如:FSkin为类型名,而Skin则是FSkin的实例。
  • 模板类的前缀为T。
  • 继承自 UObject 的类前缀为U。
  • 继承自 AActor 的类前缀为A。
  • 继承自 SWidget 的类前缀为S。

UE引擎继承关系

/uebasicsclassstructure/Image/1-UE%E7%BB%A7%E6%89%BF%E5%85%B3%E7%B3%BB%E5%AF%BC%E5%9B%BE.png

引擎结构

/uebasicsclassstructure/Image/2-%E5%BC%95%E6%93%8E%E7%B1%BB%E6%88%90%E5%91%98%E7%BB%93%E6%9E%84.png

虚幻引擎 逆向工程