Untitled

UE“反射”概念:

  • 反射:UE 通过 UClass/UProperty 等系统在运行时提供类型信息和动态访问能力。
    UE 的反射系统是通过 UHT 工具和特定宏实现的代码生成机制。你用 UCLASS 标记类、UPROPERTY 标记变量、UFUNCTION 标记函数,这些宏会被 UHT 识别。
    UHT 在编译前扫描这些标记,生成.generated.h 和.cpp 文件,里面包含类的反射注册代码,比如 StaticClass () 函数和 UClass 对象的构造逻辑。
    生成的代码会把类信息注册到引擎全局的 GObjectClasses 数组里,让引擎在运行时能动态获取类结构、调用函数或访问属性,这支撑了蓝图交互、垃圾回收等核心功能。

因为 UE 需要在运行时动态处理代码信息。比如蓝图可视化编程,引擎得通过反射知道 C++ 类有哪些函数和变量,才能让蓝图调用它们。

比如你在 C++ 里写了一个角色类,里面有个 UFUNCTION 标记的跳跃函数 Jump ()。没有反射的话,蓝图编辑器根本不知道这个 Jump () 函数存在,因为编译后的机器码里,函数名和参数这些信息都被优化掉了。
有了反射,UHT 会在编译时为这个 Jump () 函数生成反射元数据,包括函数名、参数类型、返回值,以及它属于哪个类。引擎运行时能通过这些元数据,在蓝图编辑器里把 Jump () 函数显示出来,你才能拖拽节点调用它。
如果后续你在 C++ 里给 Jump () 加了一个高度参数,反射系统会自动更新元数据,蓝图里对应的函数节点也会同步显示出新参数,整个过程不需要手动写任何蓝图和 C++ 交互的绑定代码。

回退操作 Command 模式(轻量级):**

1
2
3
4
每次操作封装为 ICommand { Do(); Undo(); }
维护 undoStack 和 redoStack
执行操作 → 压入 undoStack,清空 redoStack
Undo → 弹出 undoStack,执行 Undo(),压入 redoStack

2. Snapshot 模式(适用于复杂场景):

1
2
3
4
操作前序列化整个对象状态的快照
Undo 时直接恢复快照
优点:实现简单,不容易出 bug
缺点:内存开销大

实际项目中的混合方案:

  • 简单属性修改 → Command 模式(记录 oldValue/newValue)
  • 复杂操作(节点图变更、场景编辑)→ Snapshot 或 Diff 模式
  • 合并机制:连续同类操作合并(如拖拽 Slider 时合并为一条记录)

UE智能指针对比表

指针类型 管理对象 所有权 核心作用 适用场景
TObjectPtr UObject派生类 共享 安全访问UObject,自动参与垃圾回收 替代传统UPROPERTY指针,日常UObject引用
TWeakObjectPtr UObject派生类 弱引用UObject,不阻止回收 避免循环引用,临时访问可能被销毁的UObject
TSoftObjectPtr UObject派生类 软引用UObject,支持资源异步加载 引用可能未加载的资源,如关卡外的模型、纹理
TSharedPtr 非UObject类型 共享 通过引用计数管理生命周期 需要多持有者共享非UObject资源
TUniquePtr 非UObject类型 独占 唯一拥有对象,不可复制 管理无需共享的非UObject资源,如自定义数据结构
TWeakPtr 非UObject类型 弱引用TSharedPtr,不增加引用计数 配合TSharedPtr避免循环引用

关键区别说明

  1. 管理对象边界:前三种严格用于UObject派生类,依赖UE垃圾回收系统;后三种用于非UObject类型,靠手动内存管理机制。

  2. UObject指针细分

  • TObjectPtr是强引用,会让UObject保持存活,是日常开发的首选。
  • TWeakObjectPtr是弱引用,当UObject被标记为回收时,指针会自动置空,常用在UI控件引用角色对象这类场景。
  • TSoftObjectPtr存储的是资源路径而非直接内存地址,对象未加载时可异步加载,适合开放世界游戏引用远处的资源。
  1. 非UObject指针细分
  • TSharedPtr通过引用计数共享对象,当引用计数为0时自动释放内存,但需注意手动避免循环引用。
  • TUniquePtr是独占式指针,不允许复制,只能通过移动语义转移所有权,性能开销最小。
  • TWeakPtr需要绑定到TSharedPtr使用,当TSharedPtr释放对象后,TWeakPtr会自动失效,解决循环引用问题。

ECS 架构是什么?和传统 OOP 有什么区别?

OOP ECS
数据布局 对象分散在堆上 Component 连续内存排列
缓存友好性 差(指针跳转) 好(数据局部性)
逻辑组织 方法绑定在类上 System 独立遍历 Component
组合性 需要多重继承/组合模式 天然组合(挂 Component 即可)
其实ECS节省的是cpu去查找的时间。

核心概念:

  • Entity:ID 标识,不存数据
  • Component:纯数据(Position, Velocity, Health…)
  • System:纯逻辑(MovementSystem 遍历所有 Position+Velocity 组件)

核心区别:OOP 以对象为核心,数据与逻辑封装在类中,易形成复杂继承树;ECS 将数据与逻辑分离,实体为组件容器,系统批量处理同类组件,数据连续存储提升缓存效率,支持动态组合与并行计算。
UE5 Mass 系统案例:作为 ECS 实现,Mass 用 “片段” 存储实体数据,“处理器” 统一处理逻辑。如《黑客帝国》Demo 中的万人级 crowd 模拟,通过将角色位置、速度等数据打包连续存储,移动处理器可批量更新所有角色坐标,性能远超传统 Actor 方案。

堆Stack 栈heap

  • 堆(Heap):动态分配内存,大小不固定,生命周期由程序员控制,访问速度较慢,适合存储大对象或需要在运行时确定大小的数据。(没有固定的存取顺序)
  • 栈(Stack):自动分配内存,大小固定,生命周期由函数调用控制,访问速度快,适合存储局部变量和函数参数。(有固定的存取顺序,后进先出)

Function Calling 的原理是什么?你在项目中怎么用的?

原理: LLM 不直接执行函数,而是 输出结构化的函数调用意图(函数名 + 参数),由宿主程序解析并执行。

RAG 是什么?你是怎么实现的?

RAG(Retrieval-Augmented Generation) = 先检索相关文档,再让 LLM 基于检索结果回答。

ControlNet 是什么?它解决了什么问题?

参考答案:

ControlNet 为预训练 Diffusion Model 添加 空间控制能力

解决的问题: 原始 Text-to-Image 无法精确控制生成图像的构图、姿态、边缘等空间结构。

原理:

  • 在 Stable Diffusion 的 U-Net 每个 Block 上添加一个并行的 “Zero Convolution” 分支
  • 输入额外的条件图(边缘检测/Canny、深度图、姿态/OpenPose、法线贴图等)
  • 训练时只训练 ControlNet 分支,冻结原始模型

常见 ControlNet 类型:

  • Canny Edge:控制轮廓
  • Depth:控制深度结构
  • OpenPose:控制人物姿态
  • Segment:控制区域分割
  • Scribble:控制草图

LoRA 是什么?为什么它很受欢迎?

LoRA(Low-Rank Adaptation) 是一种参数高效微调方法。
LoRA 是一种参数高效的大模型微调技术,核心是冻结原模型权重,仅训练少量低秩矩阵来模拟任务适配所需的参数更新。它参数量仅为全量微调的 0.1%-1%,大幅降低显存占用和训练成本,且推理时可合并权重无额外延迟。
在游戏领域,能快速微调图生图模型生成风格统一的角色装备、场景素材,或微调对话模型让 NPC 生成符合设定的自然台词,适配小团队高效开发需求。

MVC、MVP、MVVM 的区别是什么?

模式 组件职责 组件关系 优缺点
MVC Model(数据)
View(界面)
Controller(逻辑)
Controller 直接操作 Model 和 View 简单直观,适合小型项目;Controller 可能变得臃肿
MVP Model(数据)
View(界面)
Presenter(逻辑)
Presenter 直接操作 Model,间接更新 View Presenter 可测试性强;View 依赖 Presenter,增加耦合
MVVM Model(数据)
View(界面)
ViewModel(逻辑)
ViewModel 直接操作 Model,通过数据绑定更新 View 双向绑定简化 UI 更新;学习曲线较陡峭,可能引入性能问题
  • MVP的Preseter和MVVM的ViewModel在职责上非常相似,都是处理业务逻辑和数据交互的中介,但MVVM通过数据绑定机制让ViewModel直接更新View,减少了Presenter中大量的UI更新代码,使得代码更简洁、可测试性更强。MVVM适合复杂UI交互较多的项目,而MVP则更适合简单UI或需要严格分离测试的场景。

GPU 渲染流水线的完整阶段?

参考答案:

GPU 渲染管线(Rendering Pipeline)是 GPU 执行图形渲染的完整流程:

应用阶段(CPU 侧):

1
2
3
4
1. 应用阶段(Application Stage)
→ 视锥体裁剪(Frustum Culling)
→ 批次合批(Draw Call Batching)
→ 输出 Draw Call + 顶点数据到 GPU

几何阶段(GPU 顶点着色器):

1
2
3
4
5
6
7
8
9
2. 顶点着色器(Vertex Shader)
→ 模型空间 → 世界空间 → 视图空间 → 齐次裁剪空间
→ 顶点变换:LocalMatrix × WorldMatrix × ViewMatrix × ProjectionMatrix

3. 曲面细分(Tessellation,可选)
→ Hull Shader → Tessellator → Domain Shader

4. 几何着色器(Geometry Shader,可选)
→ 以图元为单位处理,可生成/销毁图元

光栅化阶段(Rasterization):

1
2
3
4
5
6
7
8
9
10
5. 图元装配 & 裁剪
→ Clipping(齐次空间裁剪)
→ Perspective Divide → NDC → Viewport Transform

6. 背面剔除(Back-face Culling)
→ 根据顶 点环绕顺序(顺时针/逆时针)剔除背面

7. 光栅化(Rasterization)
→ 离散化:点/线/三角形 → 片段(Fragment)
→ 视口变换:NDC → Screen Space

片段/像素阶段:

1
2
3
4
5
6
7
8
8. 片段着色器(Fragment / Pixel Shader)
→ 逐像素着色:光照计算、纹理采样、颜色输出

9. 逐片段操作(Per-Fragment Operations)
→ 深度测试(Depth Test / Z-Test)
→ 模板测试(Stencil Test)
→ 混合(Alpha Blending)
→ 输出到 Framebuffer