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