虚幻引擎深度解析


除了逐行查看源代码或阅读官方文档中提供的有限信息外, 深入探讨Epic Games虚幻引擎架构的资源并不多. 本文旨在为那些希望更深入了解引擎的人填补这一空白.

参考材料
1. UnrealEngineDeepDive

1. 历史

理解虚幻引擎的历史背景与脉络至关重要. Tim Sweeney在发布《虚幻》(1998年) 前, 曾从《雷神之锤1》(1996年) 和《雷神之锤2》(1997年) 中汲取重要灵感. 因此, 虚幻引擎的基础架构设计与《雷神之锤》系列高度相似. 事实上, 这一段引语恰好概括了其核心关联——

“This is probably going to come out sounding demeaning, but Epic wants Unreal to be Quake. Everything they did with Unreal, they did because they wanted it to be like what Quake turned out to be.” – John Carmack, creator of Quake, id Software

《虚幻》最初以第一人称射击游戏(FPS) 形式发布, 这一点至关重要. 所有网络模块, 渲染管线及底层架构均围绕FPS游戏需求构建. 尽管相较于《雷神之锤》, 虚幻引擎在多类型支持上实现了显著突破(例如《雷神之锤》的渲染更偏向暗光走廊场景), 且Epic Games官方宣称引擎支持全类型游戏开发, 但事实是——虚幻引擎自诞生至今, 其核心优化始终聚焦于第一人称射击(FPS) 及同类游戏(如第三人称射击TPS). 以客户端预测机制和UDP网络协议选择为例, 这些设计对FPS/TPS游戏的流畅性提升显著, 但对于即时战略(RTS) 或回合制策略(TBS) 游戏则效果有限(后者更依赖TCP网络协议实现精准的回合制同步).
$\\$ 因此, 《雷神之锤》 对《虚幻》 的影响, 使得《雷神之锤》 的源代码与架构成为学习虚幻引擎基础原理的绝佳资源.

2. 两部分

虚幻引擎可分为两大核心组件: 编辑器(Editor) 与运行时引擎(Runtime Engine). 编辑器是一套用于创建和编辑游戏内容的工具套件; 运行时引擎则是负责实际运行游戏的模块.
$\\$ 与大多数其它游戏引擎不同, 虚幻引擎和雷神之锤引擎(Quake Engine) 将工具套件(如虚幻编辑器UnrealEd) 直接集成到运行时引擎中. 这一架构决策带来了诸多优势, 最核心的是游戏可通过PIE(在编辑器中运行) 模式流畅运行, 且不会因编辑器环境产生性能损耗. 该设计还支持直接加载资源内容并实时呈现完整效果, 同时减少编辑器与运行时引擎间的代码重复—— 因为编辑器直接复用了运行时引擎的代码逻辑.
$\\$ 不过, 这种架构也存在缺点, 例如文件锁定机制会阻碍开发者同时编辑资源, 影响生产效率. 具体细节后续详述.

2.1 运行时引擎架构

虚幻引擎与其它软件系统和游戏引擎一样, 采用分层架构设计. 通常, 底层模块不依赖上层模块, 这种设计避免了循环依赖问题, 提升了系统的模块化程度, 并增强了跨平台支持能力. 其中最大的优势之一是使代码更易于测试.
$\\$ 最上层包含知名的游戏框架类(如玩家控制器PlayerController和游戏模式基类GameModeBase), 而较低层则包含平台特定的实现模块, 例如Unix平台运行时(Runtime/Unix).
$\\$ 自顶向下, 虚幻引擎的层级结构如下:
$\\$ $\cdot$ 游戏特定子系统(Game-Specific Subsystems);
$\\$ $\cdot$ 游戏玩法基础模块(Gameplay Foundations), 渲染管线(Rendering), 性能剖析与调试(Profiling && Debugging), 场景图与视锥剔除(Scene Graph / Culling), 视觉特效(Visual Effects), 前端界面(Front End), 骨骼动画碰撞与物理(Skeletal Animation Collision && Physics), 动画系统(Animation), 人工智能(AI), HID音频(HID Audio), 输入系统(Input);
$\\$ $\cdot$ 资源管理(Resources / Resource Manager);
$\\$ $\cdot$ 核心系统(Core Systems);
$\\$ $\cdot$ 平台无关层(Platform Independence Layer, 含网络模块与文件系统);
$\\$ $\cdot$ 第三方SDK集成(3rd Party SDKs, 如DirectX, OpenGL, PhysX);
$\\$ $\cdot$ 操作系统层(OS);
$\\$ $\cdot$ 驱动层(Drivers);
$\\$ $\cdot$ 硬件层(Hardware).

为保持项目模块化, 这些层级中的许多功能(例如复制图Replication Graph, 游戏能力系统Gameplay Ability System) 被拆分为可选插件.

2.1.1 目标硬件层

该层级具有平台特定性. 尽管虚幻引擎总体采用平台无关设计, 但针对不同计算机或游戏主机系统仍会包含特定的平台代码与优化方案. 以《雷神之锤2》(Quake 2) 引擎为例, 其曾针对当时广受欢迎的英特尔奔腾(Intel Pentium) 处理器及其预取缓存机制进行了显著的优化适配.

2.1.1.1 Apple

2.1.1.2 Xbox

2.1.1.3 Playstation

2.1.1.4 Nintendo

2.1.1.5 Mobile

2.1.1.6 Web

2.1.1.7 VR

2.1.2 驱动层

驱动程序负责管理硬件资源, 并为操作系统提供一个接口(抽象层), 使其能够与各种硬件设备的众多变体进行交互.

2.1.3 操作系统层

虚幻引擎的这部分功能负责处理多个应用程序共享硬件资源的各类操作系统. 其中一个是你的游戏. 与旧游戏主机不同—— 旧主机中游戏可”独占” 整个设备并完全控制内存与计算资源, 现代主机和现代操作系统采用抢占式多任务处理, 允许其它应用程序与你的游戏并行运行(例如Xbox Live, Netflix, 语音聊天, 商店下载), 这些应用可能占用特定系统资源或完全暂停游戏(如Xbox仪表板). 而在当时, 此类功能层要么不存在, 最多仅限于直接访问硬件资源的库.
$\\$ 该层级存在的核心原因包括:
$\\$ $\cdot$ 实现各平台的内存访问与追踪机制: 为不同硬件平台定制内存管理方案, 确保资源分配的高效性与安全性.
$\\$ $\cdot$ 获取平台支持特性属性: 例如纹理流送(Texture Streaming), 高质量光照贴图(High Quality Light Maps), 音频流送(Audio Streaming) 等, 明确硬件能力边界以优化功能实现.
$\\$ $\cdot$ 访问并封装平台原生API: 如原子操作(Atomics), 文件I/O, 时间函数等, 通过抽象层统一调用接口, 屏蔽底层差异.
$\\$ $\cdot$ 执行通用平台命令: 例如获取屏幕方向(get orientation of screen), 识别网络类型(get network type), 支持跨平台环境下的设备状态感知.
$\\$ $\cdot$ 提供操作系统函数的平台特定实现: 如FPlatformProcess::Sleep(进程休眠), FPlatformProcess::LaunchURL(启动URL) 等, 确保系统级功能在不同平台上的兼容性.

2.1.3.1 Windows

2.1.3.1.1 窗口

引擎以窗口启动. 操作系统提供基础功能, 例如API(如Windows API) 和用于创建及管理窗口的图形子系统. 图形子系统指一组软硬件组件栈, 负责在屏幕上渲染图形. 窗口管理器负责处理窗口的绘制, 定位, 调整大小以及用户交互操作.
$\\$ 图形设备接口(GDI) 通过向图形驱动程序发送命令来创建图形内容. 图形驱动程序则将GDI的命令翻译为图形硬件能够理解的指令. 最终, 图形硬件作为物理组件, 通过创建帧缓冲区, 对帧缓冲区中的每个像素应用算法并显示最终图像, 实现屏幕上的图形渲染.

2.1.3.1.2 入口点

引擎的入口点取决于平台. 每个Windows程序都有一个名为WinMain的入口点函数. 虚幻引擎在Windows平台上的入口点(与所有其它游戏引擎一致) 是定义在Windows/LaunchWindows.cpp文件中的WinMain函数. 例如, Quake 2引擎同样拥有这个同名的函数.
$\\$ 每个支持的平台都有各自的入口点:
$\\$ 1) MacOS: Mac/LaunchMac.cpp/INT32_MAIN_INT32_ARGC_TCHAR_ARGV.
$\\$ 2) Linux: Linux/LaunchLinux.cpp/int main.
$\\$ 3) IOS: IOS/LaunchIOS.cpp/int main.


// Launch/Private/Windows/LaunchWindows.cpp

// Windows specific parameters: HINSTANCE is identification to prevent class name clashing
int32 WINAPI WinMain(_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ char* pCmdLine, _In_ int32 nCmdShow)
{
	int32 Result = LaunchWindowsStartup(hInInstance, hPrevInstance, pCmdLine, nCmdShow, nullptr); // Launch Unreal Engine
	LaunchWindowsShutdown(); 
	return Result; // 0 on success, error level otherwise
}

2.1.3.1.3 主引擎循环


// Runtime/Launch/Private/Launch.cpp

while( !IsEngineExitRequested() )
{
    EngineTick();
}

2.1.3.2 MacOS & iOS

虚幻引擎通过Apple的Core Foundation(CF) SDK在Runtime/Core/Apple目录下与Apple平台进行交互. Core Foundation是Apple为其操作系统提供的C语言API, 提供原始数据类型和包装函数(如文件I/O, 网络I/O).


// Core/Private/Apple/ApplePlatformMemory.cpp

// In this file, we keep track of the amount of memory we've allocated for an Unreal app running on an Apple device.

#include                           // c standard library
#include                     // for inspecting and manipulating Objective-C runtime data structures
#if PLATFORM_IOS && defined(__IPHONE_13_0)   // Include only for iPhone 13+
#include                          // in order to call os_proc_available_memory which determines the amount of memory available to the current app (your game running on the iPhone)
#endif                                       // Only need to include one header specific to iOS 13+.
#include            // Types used from Core Foundation: CFIndex is a typedef for a signed integer type (SInt32) used to represent indices into a CFArray or CFString
                                             // CFOptionFlags is a typedef for an unsigned integer type (UInt32) used to represent bitfields for passing special allocations into CF funcs.
                                             // CFAllocatorContext is a struct containing callbacks for allocating, deallocating, and reallocating memory, and for retaining and releasing objects.

#include "HAL/LowLevelMemTracker.h"          // for FLowLevelMemTracker in order to track memory allocations
#include "Apple/AppleLLM.h"                  // Apple's Low-Level Memory Tracker which tracks all allocations from the OS

// Skip ~250 lines including functions for memory allocation

FMalloc* FApplePlatformMemory::BaseAllocator()                        
{
#if ENABLE_LOW_LEVEL_MEM_TRACKER
	FPlatformMemoryStats MemStats = FApplePlatformMemory::GetStats(); // FPlatformMemoryStats is the Apple implementation of FPlatformMemoryStats which contains memory numbers on available/used physical/virtual memory
	FLowLevelMemTracker::Get().SetProgramSize(MemStats.UsedPhysical);
#endif

2.1.3.3 Linux

2.1.4 第三方SDK层

虚幻引擎集成了多个第三方软件开发工具包(SDK), 具体包括:
$\\$ $\cdot$ Nvidia SDKs:
$\\$ $\quad$ $\cdot$ CUDA(统一计算设备架构): 用于GPU通用计算的应用程序接口, 路径: ThirdParty/NVIDIA/CUDA;
$\\$ $\quad$ $\cdot$ GeForce NOW: 云游戏服务, 路径: Plugins/Runtime/Nvidia/GeForceNOWWrapper;
$\\$ $\quad$ $\cdot$ GPUDirect: 与Nvidia GPU直接数据交换, 路径: ThirdPartyNVIDIA/GPUDirect.
$\\$ $\cdot$ Python: 支持开发者创建编辑器小部件, 路径: ThirdParty/Python3;
$\\$ $\cdot$ Steamworks: 集成Steam在线服务功能;
$\\$ $\cdot$ Oculus: 提供Oculus VR设备支持;
$\\$ $\cdot$ WebRTC(Web实时通信): 基于Web Socket实现浏览器与移动应用间的实时通信(无需插件或外部应用), 支持视频 / 音频 / 数据流的无缝传输, 适用于视频会议, 增强现实及在线游戏场景. 在虚幻引擎中, 该技术广泛应用于像素流送(Pixel Streaming)—— 通过客户端- 服务器模型(非点对点), 将服务器端渲染的引擎内容及音频编码后, 流式传输至浏览器或移动应用端解码, 无需客户端配备高性能硬件, 路径: ThirdParty/WebRTC;
$\\$ $\cdot$ SpeedTree: 用于树木生成与渲染.
$\\$ 它们各自的源代码和预编译的.lib文件(Linux下为.a文件) 位于对应文件夹中. 然而, 在生成项目文件时, 若未向GenerateProjectFiles.bat脚本添加-THIRDPARTY标志, 这些文件不会在解决方案资源管理器中显示. .lib文件是编译过程中生成的中间库, 由对象文件(即中间SDK源代码文件) 组成, 后续会被链接到最终的可执行程序中.

2.1.4.1 图形

虚幻引擎致力于实现平台无关性, 因此将底层GPU硬件通信的抽象处理交由图形API(如DirectX和OpenGL) 完成. 这种设计使引擎能够跨平台运行, 无需直接处理不同GPU硬件的底层细节.

2.1.4.1.1 DirectX

微软的3D图形API. DirectX 9, 11和12的SDK分别位于以下目录: ThirdParty/Windows/DX9, ThirdParty/Windows/DX11和ThirdParty/Windows/DX12. 这些SDK提供了底层图形渲染接口, 使开发者能够利用不同版本的DirectX技术实现高性能3D图形处理, 同时保持与虚幻引擎平台无关性设计的兼容性.
$\\$ 这些SDK主要用于实现DirectX渲染硬件接口(RHI), 其它功能还包括着色器编译. 与OpenGL相比, DirectX提供了更高层次的抽象, 并充分利用了微软针对硬件的优化技术. 这种设计使开发者能够更高效地利用GPU资源, 同时保持与虚幻引擎跨平台特性的兼容性.

2.1.4.1.2 Vulkan

Khronos Group推出的Vulkan提供了比DirectX和OpenGL更底层的API, 实现了对GPU资源更高效的利用. 此外, 它支持直接向GPU发送GPGPU(通用计算GPU) 指令, 使得GPU不仅能处理图形渲染, 还能执行通用计算任务, 从而在高性能计算, 机器学习等场景中发挥更大价值. 这种低层级API设计赋予开发者更精细的控制能力, 同时保持与现代图形硬件的紧密兼容性.

2.1.4.1.3 OpenGL

作为Vulkan的前身, OpenGL是一个可移植的3D图形软件开发工具包(SDK). 自2006年起, 该SDK由Khronos Group负责维护, 但目前已停止积极开发, 其最后一次版本发布定格在2017年. 尽管不再更新, OpenGL凭借其跨平台特性和成熟的图形渲染能力, 仍在许多传统应用和跨平台场景中发挥着重要作用.
$\\$ 正因如此, 许多现代GPU的最新技术进展—— 例如英伟达RTX显卡的光线追踪能力—— 无法通过OpenGL实现支持, 反而由Vulkan提供兼容. 这种技术代际差异源于OpenGL自2017年后停止主动更新, 而Vulkan作为其继任者持续整合了包括光线追踪, 网格着色器等先进GPU特性, 成为支持现代图形硬件创新的核心接口标准.

2.1.4.2 物理与碰撞

2.1.4.2.1 Nvidia PhysX

2.1.5 平台无关层

虚幻引擎的平台无关层被称为硬件抽象层(Hardware Abstraction Layer, HAL). 所有位于Runtime/Core/Public/HAL路径下的内容均属于该层级. 该设计通过统一封装底层硬件差异(如内存管理, 线程调度, 输入设备驱动等), 确保引擎核心逻辑在Windows, Linux, MacOS, iOS, Android等多平台运行时无需修改, 实现真正的跨平台兼容性. HAL作为平台无关层的核心实现, 是虚幻引擎”一次编写, 多处运行” 架构的关键支撑.

2.1.5.1 平台检测

平台头文件(Platform.h) 为不同平台定义了多重头文件守卫宏, 例如:
$\\$ $\cdot$ PLATFORM_CPU_X86_FAMILY用于标识x86架构处理器平台;
$\\$ $\cdot$ PLATFORM_CPU_ARM_FAMILY用于标识ARM架构处理器平台;
$\\$ $\cdot$ PLATFORM_APPLE用于标识苹果设备平台(如MacOS / iOS).
$\\$ 这些宏通过条件编译实现跨平台代码的差异化处理. 而FPlatformAtomics类则封装了平台相关的原子操作实现细节—— 在Windows平台可能调用Interlocked系列函数, 在Linux/macOS平台可能使用GCC/Clang内置原子指令, 在移动端则适配ARM平台特有的原子操作扩展. 这种设计确保了底层硬件操作在不同架构和操作系统上的正确性与高效性, 是跨平台引擎实现线程安全, 内存同步等关键特性的基石.

2.1.5.2 原始数据类型

2.1.5.3 原始数据类型

2.1.5.4 集合与迭代器

2.1.5.5 文件系统

2.1.5.5 网络

初代《虚幻》 游戏发布时, 其多人联网层基于用户数据报协议(UDP) 构建, 作为选定的传输层, 这一设计借鉴了竞争对手《雷神之锤》(Quake) 的经验. 选择UDP而非传输控制协议(TCP), 主要源于其两大特性:
$\\$ $\cdot$ 低延迟优势—— UDP无需建立连接(如TCP的三次握手), 数据包报头更小(仅包含源端口, 目标端口, 长度和校验和), 减少了传输开销, 适合需要快速响应的多人游戏场景;
$\\$ $\cdot$ 精细控制能力—— 开发者可直接操作数据包发送时机, 重传策略及优先级排序, 避免TCP因拥塞控制, 自动重传导致的延迟波动.
$\\$ 然而, UDP的”不可靠性” 亦需明确: 它不保证数据包100%送达(可能丢失), 也不保证到达顺序(可能乱序). 这种设计取舍在竞技游戏中尤为关键—— 例如《CS:GO》 或《Apex英雄》 中, 玩家更在意”即时开火” 而非”绝对可靠传输”, 因为少量丢包可通过游戏逻辑补偿(如插值预测, 状态同步), 而高延迟则直接破坏操作手感.
$\\$ 虚幻引擎后续的联网层虽支持TCP / UDP混合模式, 但初代选择UDP的决策深刻影响了后续多人游戏框架的设计哲学—— 在”可靠性” 与”性能” 间寻找平衡, 成为实时互动应用的核心技术命题.
$\\$ 而TCP协议则采用完全不同的机制: 当数据包丢失时, TCP会触发重传机制; 同时每个数据包携带额外头部信息以维持严格顺序. 这种设计产生的额外开销, 对于延迟容忍度极低的FPS游戏而言完全不可接受.
$\\$ TCP在FPS游戏中最严重的弊端体现在优先级处理逻辑上—— 即使高优先级数据包(如狙击枪开火指令) 已就绪, TCP仍可能因重传低优先级丢失包(如语音聊天VOIP数据) 而延迟发送. 例如: 当玩家扣动狙击枪扳机时, 若TCP正在等待之前丢失的语音数据包重传确认, 该关键射击指令会被迫排队等待, 导致玩家实际感受到的”开火延迟” 远超物理网络延迟. 这种”优先级倒置” 现象在竞技类游戏中会直接破坏操作即时性, 成为影响游戏体验的核心技术缺陷.
$\\$ 因此, 实时多人游戏框架普遍采用UDP作为基础传输层, 并通过自定义可靠层(如QUIC协议, 自定义序列化与重传策略) 在保留UDP低延迟特性的同时, 针对性解决丢包补偿与顺序控制问题, 实现”可靠性” 与”性能” 的精准平衡.
$\\$ 虚幻引擎实现了其自定义网络协议—— 虚幻数据报协议(Unreal Datagram Protocol, UDPG). 该协议基于用户数据报协议(UDP) 构建, 专门针对UDP原生存在的两大不可靠性问题提供解决方案:
$\\$ $\cdot$ 丢包补偿: 通过自定义重传机制修复因网络波动导致的数据包丢失;
$\\$ $\cdot$ 乱序矫正: 利用序列号标记与动态排序算法, 确保非顺序到达的数据包仍能按正确逻辑重组.
$\\$ 这种设计使UDPG在保留UDP低延迟特性的同时, 实现了类似TCP的可靠性保障—— 例如在FPS游戏中, 既避免了TCP因重传低优先级包导致高优先级指令延迟的问题, 又通过智能重传策略确保射击, 移动等关键操作的实时性与准确性, 成为虚幻引擎多人游戏框架实现”高性能可靠传输” 的核心技术支柱.
$\\$ Tribes网络模型与虚幻引擎(UE) 网络模型高度相似, 阅读这篇论文能让你快速理解其核心设计逻辑. 论文链接: https://archive.org/details/tribes-networking-model.
$\\$ 该论文详细阐述了一种网络架构哲学—— 通过UDP协议基础 + 自定义可靠层实现低延迟高可靠性传输, 与虚幻引擎的UDPG(Unreal Datagram Protocol) 设计思路一脉相承. 两者均通过智能序列化, 动态优先级排序, 选择性重传等技术, 在保留UDP低开销特性的同时, 解决了丢包补偿与乱序矫正问题, 为多人竞技游戏提供了”高性能可靠传输” 的标杆方案.
$\\$ 阅读该论文可深入理解为何实时多人游戏普遍选择”UDP + 自定义可靠层” 而非纯TCP的架构决策, 以及这种设计如何平衡延迟敏感性与数据完整性需求.
$\\$ 相关文件: UdpMessaging/Private/Transport/UdpMessageProcessor.h.

2.1.5.5.1 客户端预测

提供上下文与历史背景: 原始版《雷神之锤1》(Quake 1) 采用客户端- 服务器网络拓扑架构, 并基于Dumb Terminal网络模型—— 所有游戏模拟均由服务器端完成. 该设计中不存在客户端预测, 航位推测或任何形式的延迟补偿机制, 仅依赖基础同步. 尽管这种设计减少了作弊途径, 但导致了糟糕的玩家体验. 具体表现为玩家移动角色时, 需经历以下完整流程:
$\\$ 1) 本地输入发送: 玩家操作(如移动) 触发本地游戏客户端向服务器发送包含输入指令的数据包;
$\\$ 2) 上行延迟: 数据包传输至服务器需耗时50ms(假设值);
$\\$ 3) 服务器处理: 服务器接收客户端数据包后更新角色位置状态;
$\\$ 4) 下行同步: 服务器将更新后的角色位置打包发送至客户端;
$\\$ 5) 下行延迟: 数据包回传至客户端需耗时50ms(实际中两端延迟通常不对称);
$\\$ 6) 客户端更新: 客户端接收服务器同步包后, 最终更新角色在本地状态中的位置.
$\\$ 至此时, 客户端甚至需要经历100毫秒的往返时间(RTT) 才能更新游戏中的位置. 更糟糕的是, 客户端现在比服务器落后50毫秒(即半程往返时间). 这导致玩家会感受到明显的操作延迟—— 以60帧每秒(每帧约16.7毫秒) 的渲染速度计算, 当玩家最终看到角色移动时, 本地已渲染了6帧画面!
$\\$ 在1996年《雷神之锤1》 的多人游戏更新《雷神世界》(Quake World) 中, 约翰·卡马克(John Carmack) 对玩家体验进行了革命性改进, 这些创新永久改变了多人游戏的形态, 并深刻影响了至今的游戏网络模型(包括虚幻引擎在内的众多作品均受其启发). 他首次引入了客户端预测技术:
$\\$ 客户端不再被动等待完整的往返时间(如100ms) 来更新本地角色位置, 而是主动运行本地移动模拟. 具体实现逻辑如下:
$\\$ 1) 本地预演: 客户端基于玩家输入立即执行移动计算, 实时渲染预测结果, 实现”即点即动” 的即时反馈;
$\\$ 2) 权威校正: 当收到服务器下发的权威状态数据包时, 客户端通过位置插值平滑过渡预测位置与服务器位置的差异, 避免视觉跳变;
$\\$ 3) 一致性保障: 由于客户端与服务器采用相同的移动逻辑代码, 在理想网络环境下, 预测轨迹与服务器计算结果高度吻合, 偏差极小.
$\\$ 1998年5月虚幻引擎问世, 其网络架构与Quake引擎高度相似.

2.1.5.5.2 像素流送

该技术通过WebRTC(Web实时通信) 协议, 在服务器- 客户端模型下实时将Unreal Engine渲染内容流式传输至远程客户端. 具体实现逻辑如下:
$\\$ $\cdot$ 云端渲染架构: 云服务器(如AWS Tesla T4 GPU实例) 运行Headless模式的Unreal Engine, 利用GPU进行实时3D渲染;
$\\$ $\cdot$ 硬件编码加速: 采用NVIDIA NVENC硬件编码器将渲染帧与音频压缩为H.264视频流, 降低CPU负载并优化传输效率;
$\\$ $\cdot$ 客户端免插件交互: 客户端通过浏览器直接接收并解码流媒体, 实现画面与音频的实时呈现, 无需安装额外插件, 外部应用或依赖本地高性能硬件;
$\\$ $\cdot$ 低延迟传输: 基于WebRTC的P2P连接特性, 结合STUN/TURN服务器穿透NAT网络, 确保端到端延迟控制在合理范围, 支持键盘, 鼠标, 触摸屏等输入设备的实时交互.
$\\$ 技术优势与行业价值: 该方案突破了传统本地渲染的硬件限制, 使低配设备也能流畅运行高画质3D应用. 在云游戏, 数字孪生, 虚拟展厅, 远程协作等场景中, 实现了”开箱即用” 的跨平台沉浸式体验, 成为5G / 云时代实时交互技术的核心基础设施.

2.1.5.6 高分辨率计时器

2.1.5.7 线程库

虚幻引擎通过利用现代CPU的多核架构, 并结合线程库的使用, 提升了其应用程序的性能表现.

2.1.5.7.1 线程

线程是代表进程组成部分的理论概念. 进程是资源单位, 而线程是调度与执行单位. 可将其类比为棉线—— 一种用于缝纫的长而细的棉纤维束, 只不过这里的”棉纤维” 是顺序指令. 这条”线程” 包含CPU对这些指令的调度与执行过程. 游戏中的函数执行即可视为一个线程.
$\\$ 系统线程的API位于Runtime/Core/Public/HAL/Thread.h.

2.1.5.7.2 用法

使用构造函数创建线程


// Runtime/Core/Public/HAL/Thread.h

/**
* Creates and immediately starts a new system thread that will execute `ThreadFunction` argument.
* Can return before the thread is actually started or when it already finished execution.
* @param ThreadName Name of the thread
* @param ThreadFunction The function that will be executed by the newly created thread
* @param StackSize The size of the stack to create. 0 means use the current thread's stack size
* @param ThreadPriority Tells the thread whether it needs to adjust its priority or not. Defaults to normal priority
* @param ThreadAffinity Tells the thread whether it needs to adjust its affinity or not. Defaults to no affinity
* @param IsForkable Tells the thread whether it can be forked. Defaults to NonForkable
*/
FThread(
    TCHAR const* ThreadName,
    TUniqueFunction&& ThreadFunction,
    uint32 StackSize = 0,
    EThreadPriority ThreadPriority = TPri_Normal,
    FThreadAffinity ThreadAffinity = FThreadAffinity(),
    EForkable IsForkable = NonForkable
);


FThread Thread = FThread(
    TEXT("MyThreadWithSingleton),                        // Give any name
    []()                                                 // Since a thread is a sequence of instructions, we pass a function for the new thread to execute
    {                                                    // One-time use anonymous lambda function
        DoWork();
    });
Thread.Join();

2.1.5.7.3 线程栈

FThread的构造函数中的StackSize参数指的是线程关键组件的大小, 即线程的线程栈大小.
$\\$ 栈数据结构是一种由连续内存块组成的容器(类似于叠放的盘子堆), 其中仅栈顶可访问, 且必须先移除(弹出) 才能访问下方的内存块. 换言之, 首个压入栈的块是最后被弹出的, 而最后压入的块则是首个被弹出的(后进先出 – Last In First Out).
$\\$ 栈图(javabycode.com – Java栈数据结构, 5分钟轻松掌握).

栈的这种行为便于表示函数调用, 因为函数的本质特性在于: 一个函数可能会调用(压入栈) 其依赖的其它(嵌套) 函数, 因此, 特定函数必须等待所有嵌套函数完成后(弹出) 才能完成自身的执行.
$\\$ 函数调用所使用的这种栈称为程序栈, 栈上的每个条目(一块内存) 称为栈帧. 每当一个函数被(其它函数) 调用时, 操作系统会将该函数中声明的所有局部变量以及CPU寄存器的内容存储在这个栈帧中, 供该函数使用. 被调用函数的返回地址也会存储在栈帧中, 因为当被调用函数返回后栈帧被弹出时, 调用方需要从离开的位置继续执行.
$\\$ 在程序被加载到内存并执行之前, 操作系统需要首先为程序栈预留一块内存区域. 栈指针(即单个CPU寄存器的值) 用于压入和弹出栈帧.

2.1.5.8 图形封装器

2.1.5.9 物理与碰撞封装器

2.1.6 核心系统层

2.1.6.1 数据结构与算法

所有软件系统都需要容器, 这些容器允许存储和操作数据, 以及使用这些数据结构来解决问题的算法. 许多项目使用标准和第三方库, 这些库提供了这些数据结构和算法的实现.
$\\$ 然而, Unreal Engine既不使用C++标准模板库(STL), 也不使用Boost, Folly或Loki等第三方库. 它选择自定义解决方案以获得性能优势和跨平台支持. 尽管引擎内部未使用STL, 但许多自定义实现都受到Boost的启发—— 这是一个流行且强大的C++数据结构与算法库, 其风格与前身STL相似. 不过, Unreal Engine引入的部分第三方库(如皮克斯制作的SDK) 确实会使用Boost.
$\\$ 注意: 由于未知原因, 存在对Boost的依赖(路径: Source/ThirdParty/Boost).

2.1.6.1.1 数据结构

容器表: Unreal Engine在Runtime/Core/Public/Containers路径下定义了其大部分容器, 详见参考材料1.

2.1.6.1.2 算法

算法表: Unreal Engine在Runtime/Core/Public/Algo路径下定义了其大部分算法, 详见参考材料1.

2.1.6.2 模块启动与关闭

虚幻引擎模块管理独立的功能区域, 通过按需加载节省资源. 模块定义启动与关闭逻辑, 这对初始化资源及清理以避免泄漏至关重要.


IMPLEMENT_MODULE(FDefaultModuleImpl, MyModule);

void FMyModule::StartupModule()
{
    // Initialization logic here
    UE_LOG(LogTemp, Log, TEXT("MyModule has started!"));
}

void FMyModule::ShutdownModule()
{
    // Cleanup logic here
    UE_LOG(LogTemp, Log, TEXT("MyModule has shut down"));
}

2.1.6.3 断言

断言通过在条件为假时停止执行来帮助识别Bugs. Unreal使用check, ensure和verify宏进行调试, 这些断言在发布版本中被移除以避免性能开销.


void MyFunction(int32 Value)
{
    // Ensures Value is not zero to avoid division by zero
    check(Value != 0);
    float Result = 100 / Value;
}

2.1.6.4 自动化

虚幻引擎的自动化系统运行测试和任务, 确保稳定性和性能. 最近我们听说一些3A游戏大作发布了未完成且Bugs极多的版本, 例如《战地2042》 和《赛博朋克2077》. 这些Bugs未被发现的主要原因之一是缺乏自动化测试. 自动化测试可以防止”回归” ——那些曾经修复过但未来又重新出现的令人沮丧的Bugs. 自动化测试对于性能测试也至关重要, 确保游戏在各种硬件上流畅运行.

2.1.6.4.1 单元测试

单元测试通过在隔离状态下验证代码行为, 确保可靠性并促进维护.


IMPLEMENT_SIMPLE_AUTOMATION_TEST(FMyUnitTest, "GameTests.Unit.MyFunctionTest", EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter)

bool FMyUnitTest::RunTest(const FString& Parameters)
{
    // Test case: ensure MyFunction returns true
    TestTrue("MyFunction should return true", MyFunction());
    return true;
}

2.1.6.4.2 Gauntlet

Gauntlet自动化框架是虚幻引擎用于在真实环境下测试游戏场景与性能的自动化工具.
$\\$ 创建全面的Gauntlet测试需要扩展FGauntletTestBase类并实现其虚拟方法, 以定义测试行为. 以下是一个扩展示例, 展示如何在虚幻引擎中利用Gauntlet自动化框架构建性能测试的结构. 本示例将重点测量固定时长内的帧率和内存使用情况, 但请记住, Gauntlet可根据游戏需求用于自动化和测试各种场景.
$\\$ 首先, 请确保已在虚幻引擎项目中配置好Gauntlet自动化框架. 本示例假设已具备创建和运行Gauntlet测试的基本知识.


// Example usage of Gauntlet to run a performance test
#include "GauntletTestController.h"
#include "Engine/World.h"
#include "Misc/Paths.h"
#include "HAL/PlatformFilemanager.h"

class FMyPerformanceTest : public FGauntletTestBase
{
    float TestDuration;
    float TimeElapsed;
    TArray FrameRates;
    TArray MemoryUsages;

public:

    FMyPerformanceTest()
        : TestDuration(60.0f) // Run the test for 60 seconds
        , TimeElapsed(0.0f)
    {
    }

    virtual void OnInit() override
    {
        FGauntletTestBase::OnInit();
        UE_LOG(LogGauntlet, Log, TEXT("Starting My Performance Test"));

        // Initialize your test variables here
        FrameRates.Empty();
        MemoryUsages.Empty();

        // You might want to load a specific level or set up your game state in a certain way here
        FString MapName = TEXT("/Game/Maps/YourMapName");
        GetWorld()->ServerTravel(MapName);
    }

    virtual void OnTick(float DeltaTime) override
    {
        TimeElapsed += DeltaTime;

        // Record frame rate and memory usage at each tick
        FrameRates.Add(1.0f / DeltaTime); // FPS = 1 / DeltaTime
        MemoryUsages.Add(FPlatformMemory::GetStats().UsedPhysical); // Get current used physical memory

        // Check if the test has run for the desired duration
        if (TimeElapsed >= TestDuration)
        {
            EndTest();
        }
    }

    virtual void EndTest() override
    {
        // Calculate average frame rate and memory usage
        float AvgFrameRate = 0.0f;
        float AvgMemoryUsage = 0.0f;
        for (int i = 0; i < FrameRates.Num(); i++)
        {
            AvgFrameRate += FrameRates[i];
            AvgMemoryUsage += MemoryUsages[i];
        }
        AvgFrameRate /= FrameRates.Num();
        AvgMemoryUsage /= MemoryUsages.Num();

        // Log the results
        UE_LOG(LogGauntlet, Log, TEXT("Average Frame Rate: %f"), AvgFrameRate);
        UE_LOG(LogGauntlet, Log, TEXT("Average Memory Usage: %f MB"), AvgMemoryUsage / (1024 * 1024));

        // Save results to a file
        FString ResultsString = FString::Printf(TEXT("Average Frame Rate: %f\nAverage Memory Usage: %f MB\n"), AvgFrameRate, AvgMemoryUsage / (1024 * 1024));
        FFileHelper::SaveStringToFile(ResultsString, *(FPaths::ProjectDir() + TEXT("TestResults.txt")));

        // Make sure to call the base class's EndTest to properly terminate the test
        FGauntletTestBase::EndTest();
    }
};

// Register the test so Gauntlet can find it
IMPLEMENT_GAUNTLET_TEST(FMyPerformanceTest, "Performance.MyPerformanceTest")

这是寻找游戏优化的绝佳方法.

2.1.6.5 内存分配

虚幻引擎通过自定义内存分配来提升性能并追踪使用情况, 同时与平台的内存管理器集成.


void* MyAllocate(SizeType Size)
{
    void* Result = FMemory::Malloc(Size);
    // Additional logic, such as tracking allocations
    return Result;
}

void MyFree(void* Pointer)
{
    // Clean up before freeing memory
    FMemory::Free(Pointer);
}

2.1.6.6 数学库

虚幻引擎的数学库(作为核心子系统的一部分) 为游戏计算提供向量, 矩阵等更多数学工具.


FVector A(1, 0, 0);
FVector B(0, 1, 0);
// Cross product
FVector C = FVector::CrossProduct(A, B);

2.1.6.7 字符串与哈希字符串标识符

2.1.6.7.1 FName

FName是一个用于高效字符串处理的系统, 通过哈希值实现快速比较和查找.


FName MyName(TEXT("MyString"));
// Fast comparison
if (MyName == FName(TEXT("MyString")))
{
    // True
}

2.1.6.8 调试打印与日志记录

2.1.6.9 本地化服务

2.1.6.10 电影播放器

2.1.6.11 解析器

2.1.6.12 性能剖析与统计收集

2.1.6.13 引擎配置

2.1.6.14 随机数生成器

2.1.6.15 曲线与曲面库

2.1.6.16 运行时类型信息 / 反射与序列化

2.1.6.17 对象句柄 / 唯一标识符

2.1.6.18 异步文件I/O

2.1.7 资源(游戏资产) 层

2.1.7.1 资源管理器(虚幻编辑器)

虚幻引擎的高度集中式资源管理器是一个统一接口, 用于访问所有类型的游戏资产, 其中包括umap和uasset文件.

2.1.7.2 三维模型资源

2.1.7.3 纹理资源

2.1.7.4 材质资源

2.1.7.5 字体资源

2.1.7.6 骨骼资源

2.1.7.7 碰撞资源

2.1.7.8 物理参数

2.1.7.9 游戏世界 / 地图

2.1.8 碰撞与物理层

引擎的这一层负责处理碰撞检测与刚体动力学(这也是其被称为"物理" 的由来). 将其称为"物理" 略显用词不当, 因为该引擎核心关注的是作用在刚体上的力和力矩, 而非其它更广泛的物理现象.
$\\$ 通常, 游戏引擎不会自行实现物理引擎, 而是使用第三方物理与碰撞引擎提供的SDK. 因此, 虚幻引擎采用了英伟达的PhysX SDK(免费且开源), 但也包含部分自定义实现(如ARigidBodyBase类). 值得注意的是, 虚幻引擎并未使用Havok或Open Dynamics Engine.
$\\$ 虚幻引擎使用PhysX的physx命名空间中的PxRigidActor类来表示动态和静态刚体.
$\\$ 物理引擎的实现代码位于以下路径: Runtime/Engine/Private/PhysicsEngine, Runtime/Engine/Public/Physics以及Runtime/Engine/PhysicsEngine.

2.1.8.1 力与约束

2.1.8.2 射线/形状投射(查询)

2.1.8.3 刚体

2.1.8.4 Phantoms

2.1.8.5 形状 / 可碰撞对象

2.1.8.6 物理 / 碰撞世界

2.1.9 人机接口设备(HID) 层

2.1.9.1 游戏特定接口

2.1.9.2 物理设备输入 / 输出

2.1.10 性能分析与调试层

2.1.10.1 录制与回放

2.1.10.2 内存与性能统计

2.1.10.3 游戏内菜单或控制台

2.1.11 底层渲染器层

2.1.11.1 图形设备接口

Unreal Engine将此称为RHI(渲染硬件接口), 它通过图形SDK(如DirectX, OpenGL, Vulkan) 枚举可用图形设备, 初始化设备并配置渲染缓冲(如后缓冲, 模板缓冲区, 深度缓冲区等).
实现代码位于路径Runtime/RHI与Runtime/RHICore下, 详见参考材料1.

2.1.11.1.1 DirectX RHI

2.1.11.1.2 Vulkan RHI

2.1.11.1.3 OpenGL RHI

2.1.11.2 材质与着色器

2.1.11.3 静态与动态光照

2.1.11.4 摄像机

2.1.11.5 文本与字体

2.1.11.6 基本图元提交

2.1.11.7 视口与虚拟屏幕

2.1.11.8 纹理与表面管理

2.1.11.9 调试绘制(线条等)

2.1.12 场景图 / 剔除优化层

一般而言, 任何形式的"空间分割" 均被称为"场景图". 常见的空间分割算法包括: 八叉树, 四叉树, 二分空间分割树, k-d树及球体层次结构. 虚幻引擎采用"场景大纲"(一种基于树结构的层次化场景表示形式) 来执行视锥剔除等性能优化操作.
$\\$ 剔除操作可包含简单算法(如视锥剔除—— 代表观察世界的梯形棱柱体空间), 或通过空间划分算法减少需执行剔除的几何体基数.

2.1.12.1 空间哈希(BSP, 树结构, k-d树等)

2.1.12.2 遮挡剔除与潜在可见集(PVS)

2.1.12.3 LOD系统

2.1.13 视觉效果层

2.1.13.1 光照贴图与动态阴影

2.1.13.2 高动态范围(HDR) 光照

2.1.13.3 预计算辐射传输(PRT) 光照, 次表面散射

2.1.13.4 粒子与贴花系统

2.1.13.5 后期效果

2.1.13.6 环境映射

2.1.14 前端层

2.1.14.1 平视显示器(HUD)

2.1.14.2 全动态视频(FMV)

2.1.14.3 游戏内过场动画(IGC)

2.1.14.4 游戏内图形用户界面

2.1.14.5 游戏内菜单

2.1.14.6 包装器与吸引模式(设备在空闲状态(如游戏机, 街机, 数字标牌) 自动播放演示内容(如游戏片段, 动态壁纸) 以吸引用户交互的功能)

2.1.15 骨骼动画层

2.1.15.1 动画状态树与层

2.1.15.2 反向运动学(IK)

2.1.15.3 层级对象附着(+游戏玩法基础层)

2.1.15.4 游戏专用后处理

2.1.15.5 线性插值(LERP) 与加法混合

2.1.15.6 动画播放

2.1.15.7 子骨骼动画

2.1.15.8 动画压缩

2.1.15.9 骨骼网格渲染(+底层渲染层)

2.1.15.10 布娃娃物理(+碰撞与物理层)

2.1.16 音频层

2.1.16.1 数字信号处理(DSP) 与效果

2.1.16.2 三维音频模型

2.1.16.3 音频播放 / 音频管理

2.1.17 在线多人游戏层

2.1.17.1 复制图

复制图通过将网络角色组织成空间组和逻辑组, 优化大型多人游戏的网络流量. 该系统基于玩家位置与游戏上下文实现高效状态复制.
$\\$ 实战应用: 在大逃杀类游戏中, 复制图可仅向玩家更新附近敌人的状态, 而非游戏中的所有玩家, 从而显著降低网络负载.
$\\$ 示例代码:


// Initialize Replication Graph in your GameMode's StartPlay function
void AMyGameMode::StartPlay()
{
    Super::StartPlay();
    GetWorld()->GetNetDriver()->SetReplicationDriver();
}

// Custom Replication Graph Node
void UMyReplicationGraphNode::GatherActorListsForConnection(const FConnectionGatherActorListParameters& Params)
{
    // Custom logic to determine which actors to replicate to which players
}

### Network Prediction

Network Prediction minimizes the effects of latency by predicting player actions and movements. This creates a smoother multiplayer experience by compensating for network delays.

Practical Use: In a first-person shooter, network prediction can be used to anticipate a player's movement, allowing other players to see that player moving smoothly, even with network latency.

**Example Code:**
```c++
// Implementing a simple movement prediction in a character class
void AMyCharacter::PredictMovement(float DeltaTime)
{
    if (IsLocallyControlled())
    {
        // Predict local player's movement
        PredictedVelocity = CalculateNewVelocity();
        SetActorLocation(GetActorLocation() + (PredictedVelocity * DeltaTime));
    }
    else
    {
        // Interpolate non-local players for smoother movement
        SetActorLocation(FMath::VInterpTo(GetActorLocation(), RemoteTargetLocation, DeltaTime, InterpolationSpeed));
    }
}

2.1.17.2 网络代码插件框架

网络代码插件框架支持模块化网络代码, 使开发者能够根据游戏特定需求定制网络协议栈.
$\\$ 实战应用: 在大型多人在线游戏中, 不同游戏区域(如副本与开放世界区域) 可能需要差异化的网络策略. 网络代码插件框架支持开发者按需切换不同的网络模型.

2.1.17.3 匹配与游戏管理

2.1.17.4 对象权限策略

2.1.17.5 游戏状态同步

2.1.18 游戏玩法基础层

Unreal Engine中的游戏玩法基础层是引擎架构的关键组成部分, 为游戏开发提供必要的基础系统与工具. 该层涵盖从高级游戏流程, 脚本系统到静态 / 动态游戏对象管理的全方位功能.

2.1.18.1 高级游戏流程系统 / 有限状态机(FSM)

高级游戏流程系统或有限状态机(FSM) 用于控制游戏的整体流程, 例如菜单导航, 关卡过渡以及暂停或游戏结束等游戏状态. 该系统确保游戏状态管理清晰, 过渡顺畅无阻.


// Define game states
enum class EGameState : uint8
{
    MainMenu,
    InGame,
    Paused,
    GameOver
};

// Change game state
void ChangeGameState(EGameState NewState);

2.1.18.2 脚本系统

Unreal引擎中的脚本系统支持通过非编译型脚本创建游戏逻辑与行为, 无需编写编译型代码.

2.1.18.2.1 UnrealScript

此前, UnrealScript是Unreal Engine的原生脚本语言, 在UE4之前被广泛使用. 它为游戏脚本编写提供了高级抽象方法. 如今, UnrealScript已不再用于当前Unreal Engine版本, 被蓝图系统全面取代.

2.1.18.2.2 蓝图

所有图编辑器工具的底层实现均基于UEdGraph. 这包括蓝图, 材质和动画图编辑器. UEdGraph是一种简明的图数据结构, 其每个节点均内置事件监听机制.
$\\$ 蓝图的所有图表(如事件图表) 会被合并为一个超级图表(Ubergraph).

2.1.18.2.3 Verse

2.1.18.3 静态世界元素

2.1.18.4 动态游戏对象模型

2.1.18.4.1 UObject

引擎中的所有游戏可玩对象均继承自该基类.

2.1.18.4.1.1 垃圾回收

当UObjects被创建时, 它们会自动加入全局数组GUObjectArray. 垃圾回收(GC) 通过定期遍历该数组, 识别并删除未被引用的对象或明确标记为销毁的对象—— 除非这些对象携带阻止垃圾回收的标志. 其中关键标志之一是EInternalObjectFlags::RootSet, 用于标记需永久存在的核心对象(如全局单例, 引擎核心模块), 避免被GC误回收.


// Runtime/CoreUObject/Private/UObject/UObjectHash.cpp

// Global UObject array instance
FUObjectArray GUObjectArray;                                   // To keep track of all UObjects

在引擎内部引用图中维持对象引用的三种方式:
$\\$ 1) 通过强引用(UPROPERTY()) 从被引用的对象指向它们.
$\\$ 若UObject未通过UPROPERTY宏声明为属性引用, 极可能被垃圾回收机制清除. 但AActors和UActorComponents是特例: Actor会被UWorld(位于根集) 引用, 而Actor Component则被所属Actor直接引用.
$\\$ 实际影响示例: 当拥有该组件的AActor被销毁时, 其所有UActorComponents通常也会被销毁—— 因为Actor是唯一持有强引用的UObject.
$\\$ 2) 通过被引用对象调用UObject::AddReferencedObjects方法: 开发者可自定义实现该函数, 主动向垃圾回收系统声明需要跟踪的引用对象, 确保动态生成的对象(如粒子特效, 临时AI实体) 在逻辑帧间被正确保留.
$\\$ 3) 通过UObject::AddToRoot添加至根集: 将对象显式加入根集后, GC将永久跳过其回收流程, 直至手动调用RemoveFromRoot. 常用于全局单例(如游戏状态管理器), 持久化配置等需跨场景存在的核心对象.


// Runtime/CoreUObject/Public/UObject/UObjectBaseUtility.h

/**
 * Add an object to the root set. This prevents the object and all
 * its descendants from being deleted during garbage collection.
 */
void UObjectBaseUtility::AddToRoot()                           // FORCEINLINE for performance benefits                                     
{                                                              // GUObjectArray is the global array of all UObjects   
    GUObjectArray.IndexToObject(InternalIndex)->SetRootSet();  // Use the int32 InternalIndex belonging to UObjectBase to index into GUObjectArray
}                                                              // Set RootSet flag for object


// Runtime/CoreUObject/Public/UObject/UObjectArray.h

void FUObjectItem::SetRootSet()                                 // FUObjectItem represents the UObject in the global array, it is a struct that contains a pointer to a UObject
{
    ThisThreadAtomicallySetFlag(EInternalObjectFlags::RootSet); // Sets a flag which lets the garbage collector know not to garbage collect EVEN if unreferenced
}                                                           // Set RootSet flag for object

2.1.18.4.1.2 自动属性初始化

UObjects的属性在构造函数执行前会被自动初始化为零值.

2.1.18.4.1.3 工厂方法

$\cdot$ NewObject()是Unreal Engine中最基础的UObject工厂方法.
$\\$ $\cdot$ NewNamedObject()与NewObject()功能一致, 但需额外通过FName参数显式指定对象名称.
$\\$ $\cdot$ ConstructObject()是一个更具灵活性的工厂方法.

2.1.18.4.1.4 自动引用更新

标记了UPROPERTY()宏或存储在Unreal容器类(如TArray / TMap) 中的UObjects, 会被反射系统识别并跟踪. 当对象被销毁时, 反射系统会自动将其引用置为null, 而非保留指向已释放内存的"悬空指针". 这种设计源于安全性考量—— 空值检查(null-check) 比依赖非空指针更可靠, 可避免因内存重复释放导致的崩溃或数据污染.

2.1.18.4.1.5 序列化

2.1.18.4.2 AActor

"Actor" 一词并非Unreal引擎独有, 甚至在Nvidia的PhysX物理引擎中也能找到其踪迹. 它是所有可被放置于场景世界中的对象的基础类, 具体表现为一个带有变换(Transform) 属性的UObject基类.

2.1.18.4.3 组合模式—— 面向对象设计中的组件

组合是一种常见的面向对象编程设计模式, 它通过定义可复用行为并建立"has-a"(包含) 关系而非"is-a"(继承) 关系来实现模块化设计. 正如《设计模式: 可复用面向对象软件的基础》(1994年, Gang of Four合著) 所阐述的, 该模式在Unreal Engine中具有核心实践价值—— 强烈推荐阅读此书, 因为引擎广泛采用了书中其它经典模式. 采用组合模式的关键优势在于: 将游戏功能封装在独立组件(如生命值组件, 移动组件) 而非庞大游戏类中, 可避免代码高度耦合形成的"代码团块"—— 这类代码因万物相互依赖导致编译耗时激增, 维护难度陡增, 而组件化架构通过解耦实现模块独立编译与迭代, 显著提升开发效率与代码可维护性.
$\\$ 组件的基类是UActorComponent类, 详见参考材料1.

2.1.18.5 实时智能体仿真

2.1.18.6 事件与消息传递系统

2.1.18.7 世界加载与流式传输

2.1.19 游戏特定子系统层

2.1.19.1 游戏特定渲染

2.1.19.1.1 地形渲染

2.1.19.1.2 水模拟与渲染

2.1.19.2 玩家机制

2.1.19.2.1 状态机与动画

2.1.19.2.2 游戏能力系统

游戏能力系统(GAS) 是一个框架, 用于简化能力复制, 取消, 施法, 授予及阻止等复杂逻辑的流程化处理. 若没有GAS, 开发者将不得不依赖愈发难以维护的杂乱条件标志检查, 计时器, 状态机及远程过程调用(RPC) 来实现能力逻辑. GAS架构模式源自《魔兽世界》—— 这款游戏显然是大规模应用能力系统的典型代表.

2.1.19.2.2.1 游戏能力

支持蓝图配置的功能模块, 可动态添加或移除, 支持手动操作或输入事件触发. 可与动画蒙太奇深度集成, 实现技能效果与动画的精准同步. 系统内置网络同步机制, 确保多人游戏场景下的状态一致性.

2.1.19.2.2.2 游戏玩法效果

2.1.19.2.2.2 游戏玩法标签

虽然游戏玩法标签(Gameplay Tags) 并非游戏能力系统(GAS) 独有, 但它承担了能力逻辑中的"条件标志检查" 功能. 这是一个简洁的标签化系统, 允许通过任意层级字符串(如"Damage.Fire" 或"Damage.Fire.Fireball") 对游戏对象进行标记. 单个对象可同时持有父级标签(如Damage.Fire) 与子级标签(如Damage.Fire.Fireball), 实现精准的逻辑筛选与效果触发.

2.1.19.2.3 相机相对控制(人机接口设备) 及游戏摄像机系统

2.1.19.2.4 碰撞流形

2.1.19.2.5 移动

2.1.19.3 游戏摄像机系统

2.1.19.3.1 固定摄像机

2.1.19.3.2 脚本化 / 动画化摄像机系统

2.1.19.3.3 玩家跟随摄像机

2.1.19.3.4 调试飞行穿越摄像机

2.1.19.4 AI

2.1.19.4.1 目标与决策系统

2.1.19.4.2 引擎接口动作系统

2.1.19.4.3 视线追踪与感知系统

2.1.19.4.4 寻路(A*算法)

2.1.20 游戏逻辑层(游戏代码架构)

注意: 这并非引擎本身的一部分, 而是基于引擎构建的附加内容. 将其包含在内是因为它可能具有实用性, 且能作为对引擎实际应用的良好总结.

2.1.20.1 即时战略(RTS) 与回合制策略(TBS)

2.1.20.1.1 战争迷雾

2.1.20.2 第一人称射击(FPS), 第三人称射击(TPS) 与角色扮演(RPG)

2.1.20.3 武器系统

2.1.20.4 能力系统

2.2 编辑器架构 - 虚幻引擎编辑器

2.2.1 骨骼与动画

2.2.1.1 骨骼编辑器

2.2.1.2 骨骼网格编辑器

2.2.1.3 动画编辑器

2.2.1.4 动画蓝图编辑器

2.2.1.5 物理编辑器

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注