前言

收集ue相关问题。AI辅助。

QA1

actor与uobject的关系 gc 委托有哪些种类 蓝图通信方式 UE有哪些线程 自定义事件与函数的区别 GAS c++的类型转换 静态多态和动态多态 虚继承 右值引用和移动语义 用过哪些脚本语言 大世界流式关卡加载有卡顿怎么解决 同一范围内有大量行为树运行如何优化 游戏内存溢出如何解决

QA2

一、基础概念与引擎架构

  1. Actor与Component的区别是什么?各自的应用场景?

    1. Actor 可以独立存在于世界中有根组件的变换,可以设置位置、旋转、缩放 AActor* Actor = GetWorld()->SpawnActor();
    2. 必须依附于Actor 相对于父组件的相对变换,或相对于Actor的根组件的相对变换
    3. SceneComponent的核心作用 变换层级系统(Transform Hierarchy)
      1. 变换层级系统(Transform Hierarchy)
      2. 插槽系统(Socket System)
      3. 变换空间转换
      4. 变换更新与同步
      5. 总结: 比起Actor 的Transform 有Mesh,Collider,physics
  2. Pawn、Character、Controller分别是什么?它们之间的关系如何?

    1. **Pawn **可操控的实体
    2. Character 角色移动、动画、胶囊体碰撞
    3. Controller 处理输入、AI决策、管理状态
  3. UObject、AActor、UActorComponent的继承关系是怎样的?

    1. UObject:UE中所有对象的基类,提供基础特性如反射、序列化、垃圾回收等。

    2. AActor:继承自UObject,是可以在场景中放置和生成的对象基类。。

    3. UActorComponent:继承自UObject,是附加在Actor上的组件基类。

    4. AActor和UActorComponent是并列关系,都直接继承自UObject,但UActorComponent不是继承自AActor。

    5. UObject的设计基础
      反射:支持运行时类型信息,蓝图、编辑器集成

      GC:自动内存管理,避免内存泄漏

      序列化:支持保存/加载,热重载

      网络:内置网络复制支持

      CDO:类默认对象,支持编辑器配置默认值

    6. ```
      UObjectBase (引擎内部基类)

      UObject (所有UE对象的基类)

      ├── AActor (可放入世界的对象)
      │     ├── APawn (可被控制的Actor)
      │     │     └── ACharacter (角色Actor)
      │     ├── AController (控制Pawn的Actor)
      │     │     ├── APlayerController
      │     │     └── AAIController
      │     └── ... 其他所有Actor派生类
      │
      └── UActorComponent (附加到Actor的组件)
            ├── USceneComponent (有变换能力的组件)
            │     ├── UPrimitiveComponent (可渲染/有碰撞的组件)
            │     │     ├── UStaticMeshComponent
            │     │     ├── USkeletalMeshComponent
            │     │     ├── UCapsuleComponent
            │     │     └── ...
            │     └── UCameraComponent, USpringArmComponent等
            │
            └── 其他Component
                  ├── UMovementComponent
                  ├── UInputComponent
                  ├── UAbilitySystemComponent
                  └── ...
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19



      4. **简述UE的反射系统(Reflection System)的作用及常见用途。**

      1. **运行时类型信息**:提供完整的类、属性、函数信息

      **序列化支持**:自动保存/加载对象状态

      **网络复制**:支持属性同步和RPC调用

      **蓝图集成**:实现C++与蓝图的无缝交互

      **编辑器集成**:支持属性面板、细节定制

      **垃圾回收**:提供引用跟踪信息

      5. **Gameplay框架中GameInstance、World、GameMode、Level、Actor、Pawn的关系是什么?**

      // 继承链
      UObject
      ├── UWorld* (World不是UObject派生,但由引擎管理)
      ├── UGameInstance (继承UObject)
      ├── AGameModeBase (继承AActor)
      ├── APlayerController (继承AActor)
      ├── AActor
      │ ├── APawn
      │ │ └── ACharacter
      │ ├── AController
      │ │ ├── APlayerController
      │ │ └── AAIController
      │ └── AGameStateBase

      1
      2
      3
      4
      5



      ### 继承链

      UObjectBase (引擎内部基类)

      UObject (所有UE对象的基类)
      ├── UGameInstance (在整个游戏生命周期内始终存在的全局管理器。独立存在于服务器和所有客户端,不随关卡切换而销毁。管理游戏保存系统、在线服务(好友、游戏会话、排行榜等)以及需要在多个关卡间持久化的数据。)

      ├── AActor (可放入世界的对象。包含了诸如Replication,Spawn,Tick等基础功能。所有可放置在关卡中的对象的基类,如摄像机、静态网格体、玩家出生点等。)
      │ ├── AGameModeBase (定义游戏规则的核心类,相当于游戏的“导演”。仅在服务器上运行,每个关卡可以设置不同的GameMode。制定胜利条件、玩家数量限制、生成规则等,并在创建时实例化GameState和PlayerState。)
      │ │
      │ ├── AGameStateBase (存储游戏的全局状态信息,相当于“公告板”。在服务器和所有客户端间同步。内容:队伍得分、游戏时间、所有玩家列表等所有玩家可见的全局数据。)
      │ │
      │ ├── APawn (所有可被玩家或AI控制的Actor的基类,是玩家在游戏世界中的物理实体。)
      │ │ └── ACharacter (Pawn的特殊子类,专为角色设计,内置角色移动组件、骨架网格体组件和胶囊体碰撞组件,支持行走、奔跑、跳跃等标准动作。)
      │ │
      │ ├── AController (控制Pawn的Actor,非实体Actor,在游戏世界中没有物理表示。)
      │ │ ├── APlayerController (处理人类玩家的输入,控制摄像机,管理HUD。)
      │ │ └── AAIController (控制非玩家角色,使用行为树、状态树、寻路等AI系统。)
      │ │
      │ ├── APlayerState (存储单个玩家的状态信息。内容:生命值、子弹数量、物品栏、得分等玩家个人数据。与Player和PlayerController保持1:1:1的关系。)
      │ │
      │ ├── AHUD (平视显示器,可以理解为对部分Player State的场景可视化。比如怪物或者人物头顶的血条,名字等等。一般来说是不能交互的,显示简略信息。)
      │ │
      │ └── … 其他所有Actor派生类

      ├── UActorComponent (附加到Actor的组件。Actor的功能构成元素,控制Actor的移动、渲染、位置等不同方面。Actor可以添加UActorComponent来实现功能的扩展。)
      │ ├── USceneComponent (有变换能力的组件)
      │ │ ├── UPrimitiveComponent (可渲染/有碰撞的组件)
      │ │ │ ├── UStaticMeshComponent
      │ │ │ ├── USkeletalMeshComponent
      │ │ │ ├── UCapsuleComponent
      │ │ │ └── …
      │ │ └── UCameraComponent, USpringArmComponent等
      │ │
      │ └── 其他Component
      │ ├── UMovementComponent
      │ ├── UInputComponent
      │ ├── UAbilitySystemComponent
      │ └── …

      ├── UUserWidget (用户界面,覆盖在场景渲染之上,提供更多玩家交互和查看的信息。指的是菜单和其他互动元素。可交互的用户界面。)

      ├── UWorld (负责管理所有的ULevel。)

      └── ULevel (所有的Actor都存在于ULevel中。)

注意:UUserWidget 继承自 UWidget,UWidget 继承自 UVisual,UVisual 继承自 UObject。这里为了简化,直接连接到了 UObject。同样,UWorld 和 ULevel 的继承链也做了简化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48







## 二、C++与蓝图交互

1. **蓝图与C++各自的优缺点是什么?实际开发中如何合理分工?**

1. "在UE开发中,我们采用'C++为骨,蓝图为肉'的策略。C++负责性能敏感的核心系统,如网络同步、物理计算、AI决策和复杂算法。蓝图则用于快速迭代的内容创作,如UI设计、动画状态机、关卡脚本和数据配置。

实际分工上,程序员用C++开发可复用的组件和系统,暴露参数和事件给蓝图。策划和美术用蓝图配置游戏内容,调整参数,制作过场动画。当发现性能瓶颈时,将关键逻辑从蓝图迁移到C++。

这种分工既发挥了蓝图快速原型的优势,又通过C++保证了性能和可维护性,是大型商业项目的标准做法。"

2. **UFUNCTION()中BlueprintCallable、BlueprintPure、BlueprintImplementableEvent、BlueprintNativeEvent的区别?** (备注)

1. | 修饰符 | 中文名 | 执行流引脚 | 副作用 | 蓝图实现 | C++实现 | 常见用途 |
| --------------------------- | ------------------ | ---------- | ------ | -------- | -------- | -------------------- |
| BlueprintCallable | 蓝图可调用函数 | 有 | 允许 | 不可实现 | 必须实现 | 执行操作、修改状态 |
| BlueprintPure | 蓝图纯函数 | 无 | 禁止 | 不可实现 | 必须实现 | 计算、查询、获取值 |
| BlueprintImplementableEvent | 蓝图可实现事件 | 有 | 允许 | 必须实现 | 禁止实现 | 事件回调、可扩展行为 |
| BlueprintNativeEvent | 蓝图可重写原生事件 | 无 | 允许 | 可重写 | 默认实现 | 可扩展的模板方法 |



3. **UE的垃圾回收(GC)机制如何工作?常见内存泄漏原因有哪些?如何防止UObject被自动GC?**

1. ```
// 标记阶段(Mark Phase)
void PerformGarbageCollection() {
// 1. 暂停游戏线程(可增量式GC时部分暂停)
SuspendGameThread();

// 2. 标记根对象
MarkRoots();

// 3. 递归标记可达对象
MarkReachableObjects();

// 4. 清除不可达对象
SweepUnreachableObjects();

// 5. 恢复游戏线程
ResumeGameThread();
}
  1. 委托(Delegate)机制怎么用?单播和多播委托的区别及应用场景?

    1. 需要返回值 → 单播委托

      多个监听者 → 多播委托

      与蓝图交互 → 动态多播委托

      性能敏感 → 单播委托

  2. 蓝图通信的常用方式有哪些?各自适用场景?

三、内存管理与智能指针

  1. UE中常用的智能指针有哪些?TSharedPtr、TUniquePtr、TWeakPtr的区别和使用场景?(备注)

  2. TArray与std::vector有何异同?TMap和TSet的底层原理是什么?

    1. TArray深度集成UE(序列化、网络、蓝图)
  3. UStruct和UClass在内存布局上有何不同?

  4. UE的异步加载使用哪种智能指针存储资源?

四、网络与同步

  1. UE的网络同步原理是什么?常用的同步属性/函数声明方式?(备注)
  2. 如何优化大量Actor的网络同步性能?PushModel是什么?
  3. GAS(Gameplay Ability System)的两种网络策略是什么?区别及性能开销?
  4. FastArray常用接口有哪些?如何提升同步效率?
  5. 动画同步与多播机制如何实现?多播动画是否会在主控端和模拟端各播放一次?

五、渲染与性能优化

  1. 前向渲染与延迟渲染的区别?UE默认使用哪种?
  2. 如何优化UE项目的帧率?常用的性能分析工具有哪些?
  3. LOD(Level of Detail)的原理是什么?在UE中如何应用?
  4. 材质系统的基本原理?PBR(物理基础渲染)是什么?
  5. Nanite和Lumen的核心原理是什么?与UE4相比有哪些革新?

六、物理与碰撞

  1. 如何在UE中实现物体的碰撞检测?常用的碰撞体类型有哪些?
  2. 子弹速度过快导致穿墙的原因是什么?如何解决?
  3. 伤害检测与碰撞判定的流程?墙体遮挡的判定如何优化?

七、动画与行为树

  1. 动画蓝图(Anim Blueprint)与动画蒙太奇(Montage)的区别?
  2. 行为树(Behavior Tree)和黑板(Blackboard)是什么?多个NPC能否复用行为树?如何实现?
  3. 增强型输入系统(Enhanced Input)如何工作?

八、系统设计与场景题

  1. 设计一个回旋镖系统(包括运动轨迹、伤害判定、回收机制)。
  2. 游戏中非常卡顿,只有二三十帧,你如何定位卡顿原因?
  3. 游戏中有很多个Actor导致卡顿,如何进行性能优化?
  4. 对象池(Object Pool)如何实现?在什么场景下使用?
  5. 如果要进行关卡切换,但不想用GameInstance保存数据,可以怎么做?

九、C++底层与算法

  1. C++中的多态性和虚函数机制?在UE中的应用举例。
  2. 智能指针的原理?循环引用如何解决?
  3. 右值引用、完美转发、引用折叠的原理是什么?
  4. SFINAE是什么?模板匹配的优先级?
  5. 长度为n-1的数组,元素为1~n的数字,其中缺了一个数字,如何快速找出来?

十、项目经验与架构设计

  1. 你在项目中用GAS做了什么?请详细说明。
  2. 你实现过什么系统吗?请描述设计思路和难点。
  3. 如果要设计一个生命周期与GameMode一致的子系统,如何实现?
  4. 如何设计一个支持不定数量和类型参数的委托机制?
  5. 大世界场景下GC如何优化?除了簇和新老生代还有哪些方法?

QA3

CPP

GP

第一组:UE核心架构(必问)

1️⃣ Actor 生命周期(高频必杀题)

你讲清楚一个 Actor 从创建到销毁的完整流程:

  • 构造函数什么时候调用?(和Spawn的关系)

A:

  • PostInitProperties / PostInitializeComponents / BeginPlay 区别?

A:

避免在构造函数中进行复杂逻辑,因为此时许多系统可能未初始化

避免在PostInitProperties中访问组件,因为它们还不存在

PostInitializeComponents适合组件间的设置,但不适合开始游戏逻辑

BeginPlay是最安全、最常用的初始化点,特别是对于蓝图项目

  • Tick 是怎么被调度的?

A:执行顺序 →

  1. TG_PrePhysics // 物理计算前
  2. TG_StartPhysics // 开始物理模拟
  3. TG_DuringPhysics // 物理模拟期间(并行)
  4. TG_EndPhysics // 物理模拟结束
  5. TG_PostPhysics // 物理计算后
  6. TG_PostUpdateWork // 后更新工作
  7. TG_LastDemotable // 最后可降级的
  • Destroy 之后会立刻销毁吗?

A:

EndPlay()

调用 Destroy() 的时刻
Actor 被标记为 “待销毁” (PendingKill)
当前帧继续执行(Actor 仍然存在)
下一帧开始前执行实际清理
资源释放和内存回收

  1. 收集所有标记为 PendingKill 的对象
  2. 调用 BeginDestroy() 开始销毁
  3. 检查引用 IsReadyForFinishDestroy()
  4. 调用 FinishDestroy() 完成销毁
  5. 释放内存

👉 面试官想听的是:调用顺序 + 引擎机制,不是函数名罗列


2️⃣ UObject vs Actor(本质区别)

  • UObject 为什么不能直接放到世界里?

A:

UObject

核心功能:反射、序列化、垃圾回收、属性系统
但缺少:位置、旋转、缩放、场景表示

  • Actor 比 UObject 多了什么能力?

A:

  • Actor特有:
    Transfrom,Component,生命周期,world交互,physics,Replicated/RPC,Visible,Tag,Parent,Level,Input

  • UObject基础能力:
    Save,Blueprint,editor

  • Component 为什么必须挂在 Actor 上?

A:

组合模式(Composition over Inheritance)

创建Component时,将其注册到Actor,Actor会负责调用Component的初始化、Tick和销毁函数。

当Actor被销毁时,它所拥有的所有Component也会被销毁。

Actor 是”什么”(游戏世界中的实体)

Component 是”如何”(实体的功能和行为)

Component 必须属于某个 Actor,就像功能必须属于某个实体

👉 如果你答成“一个是基础类一个是实体类”,基本不过


3️⃣ 反射系统(UE最核心之一)

讲一下 UE 反射系统:

  • UCLASS / UPROPERTY / UFUNCTION 本质做了什么?

A:UCLASS、UPROPERTY、UFUNCTION是虚幻引擎(UE)反射系统的核心宏。它们的本质是通过预处理和代码生成,将 C++ 类、属性和函数标记为“可反射的”,从而在运行时能够被引擎识别、访问和操作。

编译前:元数据标记与代码生成,预处理展开#define,UHT扫描头文件,生成generated元数据

运行时:

  • .generated.h 是怎么来的?

.generated.h文件是虚幻引擎反射系统的编译时生成产物,由 UHT 工具自动创建。它:

是反射系统的”源代码”:将宏标记转换为实际的C++代码

在编译时生成:由 UBT 驱动,UHT 执行

实现了类型自省:让C++类在运行时能查询自身结构

支持所有UE高级特性:蓝图、序列化、网络、编辑器集成

  • 反射数据在运行时存在哪里?
  • 补:RTTI是什么

👉 这一题直接区分“会用UE”和“懂UE”


第二组:组件与模块化(中级)

4️⃣ ActorComponent 机制

  • Component 是怎么被注册的?
  • RegisterComponent() 做了什么?
  • TickComponent 是怎么进入调度的?

5️⃣ Subsystem(UE5常问)

  • GameInstanceSubsystem / WorldSubsystem 区别?
  • 生命周期分别是什么?
  • 为什么不用单例而用 Subsystem?

第三组:内存与GC(高频深水区)

6️⃣ UE 的 GC 机制

  • UE 是怎么做垃圾回收的?(引用计数吗?)

A:标记阶段:从根集(Root Set)(如全局变量、当前栈上的对象引用、显式标记为根的对象等)出发,扫描所有 UObject 对象。凡是能被直接或间接引用到的对象,都会被标记为“可达”(存活)。

清除阶段:所有未被标记的对象被判定为“垃圾”,随后在合适的时机被销毁并释放内存

  • UPROPERTY 为什么重要?
  • 什么情况下对象会被回收?

👉 这一题很多人会死在“说成引用计数”

A: 详见GC文章


7️⃣ 智能指针 vs UObject

  • TSharedPtr 和 UObject 能一起用吗?
  • 为什么 UObject 不用 shared_ptr?

A:TSharedPtr这类指针是不能直接用于UObject的(非得强行使用也不是不行,但是要自己实现Deleter)


第四组:Tick与性能(实战向)

8️⃣ Tick 系统

  • Tick 是怎么调度的?
  • Tick Group 是干嘛的?
  • 怎么减少 Tick 开销?

9️⃣ 多线程(加分题)

  • UE 主线程做什么?
  • 渲染线程是什么?
  • Gameplay 能不能多线程?

第五组:进阶(拉开差距)

🔟 模块系统(Module)

  • UE 模块是怎么加载的?
  • IMPLEMENT_MODULE 做了什么?
  • 游戏启动流程大致是什么?

回答建议

你可以这样来:

👉 直接说:“我答第1题”
我会根据你回答继续往深里追(比如直接问源码级细节)


提醒你一个现实情况

如果你目标是“一线游戏公司”:

  • 能答到 第3组(GC) → 中级稳
  • 能答到 第4组(Tick + 性能) → 中高级
  • 能答第5组 → 面试官会认真考虑你

Unlua

好,这块我会按“UE客户端 + 脚本层架构”面试强度来问,重点不是Lua语法,而是:

👉 UnLua 如何嵌入 UE、如何做工程化、以及你是否真的在项目中用过

你可以一题一题答,我会继续深挖。


第一组:UnLua 基础原理(必问)

1️⃣ UnLua 是怎么工作的?

你讲清楚:

  • UnLua 是怎么把 Lua 绑定到 UE 的?
  • C++ 类是怎么暴露给 Lua 的?
  • Lua 是怎么调用 UObject / Actor 方法的?

👉 至少要提到:反射 / 绑定 / 虚拟机交互


2️⃣ Lua 文件是怎么驱动 Actor 的?

比如你有一个 Actor:

1
2
UCLASS()
class AMyActor : public AActor

你用 UnLua:

  • Lua 是怎么“接管”这个 Actor 的?
  • 生命周期函数(BeginPlay / Tick)是怎么映射的?

👉 不要只说“写同名函数”,要说机制


第二组:架构设计(中级分水岭)

3️⃣ 为什么用 UnLua,而不是全 C++?

你需要讲清楚:

  • 优点(至少3点)
  • 缺点(至少2点)
  • 适合放 Lua 的模块有哪些?

👉 这一题考的是“工程判断力”


4️⃣ C++ 和 Lua 怎么分层?

假设一个项目:

  • 战斗系统
  • UI系统
  • 技能逻辑

你会怎么拆?

👉 如果你回答“UI用Lua,底层用C++”,这是初级答案
👉 我要听的是:边界 + 原则


第三组:性能与坑(高频)

5️⃣ UnLua 性能问题

  • Lua 调用 C++ 开销大吗?
  • 哪些地方会成为瓶颈?
  • 如何优化?

👉 能说到“减少跨语言调用”才算入门


6️⃣ GC 与内存管理

  • Lua 有 GC,UE 也有 GC → 会冲突吗?
  • UObject 在 Lua 里怎么保证不被回收?
  • 有没有遇到野指针 / dangling 的问题?

👉 这题非常关键(很多人不会)


第四组:进阶(拉开差距)

7️⃣ 热更新(Hotfix)

  • UnLua 能不能热更新?
  • 怎么做?
  • 有什么限制?

8️⃣ 生命周期问题(真实项目坑)

比如:

  • Actor 被销毁了,但 Lua 还持有引用怎么办?
  • World 切换时 Lua 状态怎么处理?

👉 能答这题,基本就是“用过项目”的人


第五组:深水区(高阶)

9️⃣ 你了解 UnLua 的绑定方式吗?

  • 是静态绑定还是动态绑定?
  • 有没有用到模板 / 宏生成?
  • 和 tolua++ / slua 的区别?

回答建议

👉 从第1题开始答(UnLua原理)
我会像面试官一样:

  • 追问你源码层细节
  • 帮你补成“中高级答案”
  • 告诉你这题能不能过一线

提醒你一个关键点

很多人写了:

“熟悉UnLua”

但实际上只能回答:

  • 写Lua脚本
  • 调UE接口

👉 这种在一线面试里是直接挂的


开始吧:
👉 先答第1题:UnLua 是怎么工作的?

DS

第一组:UE网络基础(必杀)

1️⃣ UE 网络模型(必须讲清)

你说一下 UE 是什么网络模型:

  • 是 Client-Server 还是 P2P?

A:CS(DS) P2P(LS)

  • Server 是权威的吗?

A:Server 是权威的:服务器(Server)拥有游戏状态的最终决定权,是唯一权威(Authority)的来源。

  • Client 能不能修改状态?

A:Client 不能直接修改权威状态:客户端(Client)不能直接修改服务器上的权威状态。它需要通过向服务器发送远程过程调用(RPC)来请求状态更改,由服务器验证并执行。

👉 如果你没提到 Authority / AutonomousProxy / SimulatedProxy,这一题算没答好


2️⃣ Actor 同步机制 (待 源码)

一个 Actor 在网络中是怎么同步的:

  • 什么是 Replication?
  • bReplicates 开了之后发生了什么?
  • 属性同步(UPROPERTY Replicated)是怎么走的?

👉 面试官要听:引擎在背后做了什么


3️⃣ RPC 机制

  • Server / Client / NetMulticast 区别?

A: 服务器,单播,广播

  • RPC 是立即执行的吗?

A:RPC 是非阻塞的异步网络操作,不会立即在目标端执行。执行时机受网络延迟、带宽、优先级和可靠性的影响。

  • Reliable vs Unreliable 怎么选?

A:

低频且重要、不能丢的事件:交易、授权、评分结算 等系统逻辑事件

高频或可被覆盖的事件:移动与位置,动画口型特效,UI的更新事件(扣血数据等必须要可靠),语音聊天

不可靠:Tick连续变化可以使用,只是表现出问题数据没问题可以使用

👉 很多人会答成“Server就是服务器调用”,这是不够的


第二组:移动同步(中级分水岭)

4️⃣ 角色移动同步(CharacterMovement)

移动(Movement Replication / CharacterMovement)

这是超高频题:

  • 为什么 Character 移动看起来很流畅?
  • Client Prediction 是怎么做的?
  • Server Correction 怎么处理?

👉 至少要讲:

  • 输入上报
  • 服务端重算
  • 回滚 / 修正

5️⃣ 延迟与抖动处理

  • 网络延迟会带来什么问题?
  • 怎么减少“卡顿感”?
  • 插值(Interpolation)和预测的区别?

第三组:技能同步(GAS/战斗系统核心)

6️⃣ 技能同步怎么做?

假设你用 GAS 或自己实现:

  • 技能是在 Client 触发还是 Server?
  • 如何防作弊?
  • 如何保证表现流畅?

👉 这是你简历里的重点(GAS + 网络)


7️⃣ 命中判定(Hit Detection)

  • 是客户端判定还是服务端判定?
  • 延迟情况下怎么保证公平?

👉 能说到:

  • Server Rewind / Lag Compensation → 加分

第四组:带宽与优化(中高级)

8️⃣ 网络性能优化

如果有 100 个玩家:

  • 会有哪些瓶颈?
  • 怎么减少带宽?

👉 至少要提到:

  • NetCullDistance
  • relevancy
  • dormancy

9️⃣ 属性同步优化

  • 所有变量都 Replicate 吗?
  • 如何减少同步频率?

第五组:DS(Dedicated Server)

🔟 DS 架构理解

  • DS 和 Listen Server 区别?
  • 为什么生产环境用 DS?
  • DS 不需要哪些东西?(很关键)

11️⃣ 无头服务器优化

  • DS 上怎么做性能优化?
  • 是否需要动画 / 渲染?

第六组:进阶(拉开差距)

12️⃣ 网络架构设计题

给你一个需求:

👉 设计一个“多人技能战斗系统”

你要考虑:

  • 技能释放流程
  • 同步策略
  • 延迟处理
  • 防作弊

回答方式建议

👉 从第1题开始(UE网络模型)
我会:

  • 按一线面试强度追问
  • 指出你答案的问题
  • 帮你修成“能过面试”的版本

提醒你一个现实情况

如果你目标是一线厂:

  • 只会 RPC + Replication → ❌ 不够
  • 能讲移动预测 → ✔ 中级
  • 能讲技能同步 + 延迟处理 → ✔✔ 中高级
  • 能讲 Lag Compensation → ⭐ 强候选

QA4

  1. UE4网络同步,UE的角色几种权限,说一下属性同步,RPC,以及他们的优劣

A:权威服务器,myself 自主代理, otherPlayer 模拟代理。

属性同步自动将Actor的属性更新到客户端,RPC 显式调用

  1. 在服务器生成了一个actor,actor有一个属性x,都开启了网络同步,现在由于客户端性能原因,延迟生成了actor,请问在客户端生成actor前服务器修改了x的值,这个属性可以同步吗?

A:可以同步。客户端最终生成的Actor,其属性x的值将是服务器在“初始复制”时刻的当前值。如果服务器在客户端建立网络通道、执行首次复制之前修改了x,那么客户端收到的就是修改后的值。

QA5

我为你准备了一份针对初级UE系统开发(Lua/DS网络)的面试题清单,涵盖基础概念和常见应用场景,希望能帮助你系统性地准备。

一、 Lua 基础与在UE中的应用

  1. 基础语法:Lua中pairsipairs遍历一个表有什么区别?请举例说明。

  2. UE集成:在UE项目中,通常如何使用Lua?你了解UnLuaSLUA这类绑定方案的基本原理吗?

  3. 面向对象:Lua本身没有内置的类系统,你如何用Lua的元表(metatable)模拟实现面向对象的继承?

二、 DS(Dedicated Server,专用服务器)与网络同步

  1. 权威性概念:解释一下客户端权威和服务器权威的区别。在UE的DS架构中,为什么通常要坚持服务器权威?
  2. RPC(远程过程调用):UE中的RPC_ServerRPC_ClientRPC_NetMulticast分别用在什么场景?调用它们时有哪些注意事项?
  3. 属性同步:UE中Replicated属性和RepNotify(复制通知)是如何工作的?请描述一个使用RepNotify的简单例子。

三、 UE Gameplay 框架与系统设计

  1. 框架理解:简述ActorPawnCharacterPlayerController之间的关系与区别。
  2. 蓝图与代码:你如何平衡使用蓝图和C++/Lua进行开发?在什么情况下你会倾向于将逻辑写在脚本里而不是蓝图里?
  3. 性能与优化:如果一个游戏场景中突然出现大量Actor,可能导致性能问题,你会从哪些方面入手排查和优化?(提示:可考虑渲染、Tick、网络复制等方面)

四、 综合与实践

  1. 问题解决:假设你在开发一个多人射击游戏,发现某个玩家的客户端显示其他玩家的位置有轻微延迟和抖动。你会如何一步步分析和定位这个问题?

给你的准备建议:回答时尽量结合你过去项目中的实际经历。对于概念性问题,清晰准确地定义是关键;对于场景题,展示你的排查思路和分析逻辑比直接给出一个“正确”答案更重要。祝你面试顺利!

QA6

unlua: unlua绑定主要是依赖反射系统,无需解析c++,动静态反射,动态 processEvent 去分支执行蓝图交互的u函数,此时原数据由反射得来uclass,同时对于luagc和uegc双端管理生命周期,对于性能敏感则使用静态导出,平时主要也是动态绑定。

luagc:对于lua的gc来说一个增量式的三色标记清除法,分为黑白灰三色。对于gc白 无gc访问,灰 已gc访问 但没检查引用 黑 全检查完毕。

从根集合出发,逐步把灰色对象引用的对象变灰,自身变黑,最终白色对象被回收。

相比 UE GC,它能自动处理循环引用,但因为 Lua 和 UE 各有一套 GC,UnLua 里要特别注意用弱表避免跨语言循环引用。

lua闭包:Lua 的闭包是函数加上它可以访问的外部局部变量(Upvalue)。即使外部函数返回,这些变量仍然存活,闭包可以继续访问和修改它们。
我在 UnLua 中主要用闭包做 UI 回调、事件监听和协程逻辑,但要注意闭包会延长 Upvalue 的生命周期,可能导致内存泄漏。

userdata:userdata 是 Lua 用来持有 C/C++ 对象的核心机制,它是一块由 Lua GC 管理但内容不透明的内存。

在 UnLua 中,我们用 full userdata 包装 UObject 指针,通过元表暴露反射接口,让 Lua 能安全调用 UE 对象。

为了避免内存泄漏,我们通常配合弱引用或 TWeakObjectPtr 使用,而不是让 Lua 强持有 UObject。

lua长短字符串:40字节为界线。短字符串的设计主要思想就是避免重复创建大量相同的字符串,且为了方便查到到这个唯一的对象,创建时就计算了hash值。短字符串避免了重复创建,而长字符串每次定义,都会创建新的对象。

table:Lua 的 table 是数组 + 哈希的混合结构,所有值都用 TValue 表示。

TValue 是一个带类型标签的联合体,可以存数字、布尔、GC 对象等。

整数键优先放在数组部分,非整数或越界整数放在哈希部分。开放寻址法的hash table

插入新 key 可能触发 rehash,所以在大表场景中我会提前预分配,避免性能抖动。

反射:对于ue反射,是一个模仿类似与C#的特性的语法糖的处理。对于u对象的子类,会去组织u属性,u函数等,类似特性的语法作为tag标记,并写上具体的meta属性,经过编译器uht的生成 会生成新的h和cpp文件,里面包含属性type,name,等元数据,这些元数据ue会把他们根据模块组织起来,在启动模块时会进行注册到全局反射系统,并利用引用关系链连接起来,也是方便后续gc。

uegc:对于ue的gc 是最基础的一个标记清除算法,第一步,先收集对象,根对象是gc的起点,包括全局对象,当前活跃的actor,还有addToRoot标记的对象。第二步 在合适阶段进行遍历标记所有根对象为活跃,第三部 递归gc堆 消除所有不可达与未被标记对象对象。第四部清除 从BeginDestory 中标记PendingKill 经过收集后,后再次检查引用后最后到达 FinishDestory 最后下一帧会进行回收

1.在gc中若要保持对u对象的引用 必须要在有u属性情况下,否则会被回收。

2.gc触发时机要多种,切换场景,达到gc时间间隔,内存压力触发,也可以主动根据接口触发。

sharedptr:引用计数实现,简单来说就是计数为0就会销毁对象。

构造函数=1,拷贝构造++,赋值= –= =++ 0->销毁。

sharedptr本身安全无锁 但是指向对象的读写不是,所以不是线程安全

std::move 左值是具有持久地址、可以取地址的表达式,比如变量名;

右值是临时的、即将销毁的表达式,比如字面量和临时对象。

C++11 引入了右值引用(&&)和移动语义,允许我们“偷走”临时对象的资源,避免不必要的深拷贝。

在 UE 中,TUniquePtr、FString、TArray 都大量使用移动构造来提升性能。

虚函数:虚函数是 C++ 实现运行时多态的机制,通过 virtual 关键字声明,允许派生类重写基类行为。

底层通过虚函数表(vtable)和 vptr 实现,在运行时根据对象真实类型调用对应函数。

RPC和属性同步的区别,属性回调理论上一定会执行,而RPC函数有可能由于错过执行时机而不再会执行。

例如:我在服务器上面有一个宝箱,第一个玩家过去后,宝箱会自动开启。如果使用RPC函数,当第一个玩家过去后,箱子执行多播RPC函数触发开箱子操作。但是由于其他的玩家离这个箱子很远,所有这个箱子没有同步给其他玩家,其他玩家收不到这个RPC消息。

当这些玩家之后再过去之后,会发现箱子还是关闭的。如果采用属性回调,但第一个玩家过去后,设置箱子的属性bOpen为true,然后同步到所有客户端,通过属性回调执行开箱子操作。

这时候其他玩家靠近箱子时,箱子会同步到靠近的玩家,然后玩家在客户端上会收到属性bOpen,同时执行属性回调,这时候可以实现所有靠近的玩家都会发现箱子已经被别人开过了。

完整工作流可以按“打开 UI 前、打开中、上舞台、显示后、关闭/二次打开”来看。

1. 打开 UI 前:收集依赖

业务不要直接同步加载:

1
2
3
UE.UClass.Load
LoadObject
同步 SetBrush 图片/材质

而是先把 UI 需要的资源整理出来:

1
2
3
4
5
6
UI 蓝图类
图片
材质
DataAsset
Avatar / 模型资源
列表首屏资源

然后交给异步框架加载:

1
2
3
4
5
6
CommonUtil.UClassLoad
CommonUtil.UObjectLoad
PoolLoadAsyncJob.UClassLoadAsyncJob
PoolLoadAsyncJob.UObjectLoadAsyncJob
SetBrushFromSoftObjectPath
SetBrushFromSoftMaterialPath

这一步的目标是:避免打开 UI 的那一帧触发 Flush 同步等待。

2. Loading 阶段:先异步加载资源

打开 Mdt / View 时,框架进入 Loading 状态。

这时会做几件事:

1
2
3
4
发起资源异步加载
界面暂时 Hide
注册异步 Job 生命周期
等待资源回调

如果界面还没打开完就关闭了:

1
2
3
取消 Job
或回调时判断界面是否合法
不再继续创建 UI

这一步的目标是:资源没准备好前,不让业务直接访问未完成资源,也不让无效回调操作已关闭界面。

3. 创建 UMG:创建 U 对象树

资源准备好后,开始创建 UMG 对象。

这里主要是:

1
2
3
4
5
6
CreateWidget
生成 UUserWidget
构造 WidgetTree
初始化 UButton / UImage / UTextBlock 等 UWidget
创建 Slot / 布局数据
执行部分业务初始化

这些是 UObject 层,也就是业务能拿到和操作的 U 对象。

这一步不是 S 分帧的重点,但也要注意业务逻辑别太重,比如不要在初始化里大量同步加载或一次性创建海量 Item。

4. 添加到舞台:开启 SWidget 分帧

UI 要显示时,会走类似:

1
2
3
AddChild
AddToViewport
AddChildToPanel

优化后不要用普通添加,而是用框架提供的分帧接口:

1
2
UGFUnluaHelper::AddChildWithSlateStepTakeWidget
UIRoot.AddChildToPanelWithSlateStepTakeWidget

内部会开启:

1
ENABLE_SLATE_STEP_TAKE_WIDGET

然后触发:

1
2
TakeWidget
RebuildWidget

正常情况下,TakeWidget 会递归创建整棵 Slate SWidget 树:

1
2
3
4
5
UButton -> SButton
UImage -> SImage
UTextBlock -> STextBlock
UOverlay -> SOverlay
UCanvasPanel -> SConstraintCanvas

如果一帧内创建太久,就进入分帧逻辑。

5. SWidget 分帧:先占位,再补齐

TakeWidget 检测到耗时超过阈值时:

1
2
3
4
当前真实 SWidget 暂时不继续创建
先创建一个 SSpacer 占位
把这个 UWidget 抛给 US1UIFeatureSubsystem
记录等待分帧 TakeWidget 的任务

也就是:

1
2
3
4
5
6
7
第一帧:
UI 框架和部分控件先出来
重的子控件位置先用 SSpacer 顶住

后续帧:
Subsystem 每帧继续创建一部分真实 SWidget
创建完成后替换掉 SSpacer

核心处理者是:

1
US1UIFeatureSubsystem::Tick

它会按时间预算处理:

1
TickWaitForTakeWidgetTimeLimit

避免某一帧又处理太多。

6. 生命周期和 NewlyCreated 处理

这里是这套方案比较关键的地方。

UMG 里有些逻辑依赖 “新创建完成” 的状态。如果子控件还没真正创建完,父节点就触发 NewlyCreated,可能出问题。

所以方案里用了类似这些标记:

1
2
3
bSelfUserWidgetSkipNewlyCreate
bSelfUserWidgetStepTakeWidgetComplete
WaitForGeneratedByUserWidget

它们主要用来保证:

1
2
3
分帧没完成时,先跳过 NewlyCreated
子节点分帧完成后,再回头触发
NewlyCreated 只触发一次

嵌套蓝图时,比如:

1
2
3
4
A
-> B
-> C
-> 重控件

如果 C 下面有控件被分帧,需要从 C 往上找到 A,记录 A/B/C 这条链路还有多少下层控件没完成。
等下层分帧都完成后,再从下往上恢复对应的 NewlyCreated 行为。

所以:

1
2
控件创建是从上到下
NewlyCreated 恢复是从下到上

7. 业务逻辑分帧

SWidget 分帧只能解决 Slate 创建成本。业务层还要配合。

比如这些也可能卡:

1
2
3
4
5
6
大量 AddChild
大量列表 Item 创建
ReuseList 一次刷新很多格子
Lua 里一次绑定大量数据
PreCreateCollect 收集大量资源
Avatar 创建

处理方式是:

1
2
3
4
5
6
列表分批创建
AddChild 分帧
首屏优先,非首屏延后
Loading 阶段 Hide
资源加载完成后再显示
重逻辑拆到多帧执行

文档里也提到 PreloadAction,可以把需要异步加载的资产、异步函数调用提前注册,在 Loading 阶段统一执行。

8. 显示 UI:完成后再切可见

当资源加载完成、SWidget 分帧完成、业务初始化完成后:

1
2
3
把 View 从 Hide 切到 Show
触发 OnShow / AfterShow 等逻辑
玩家看到完整 UI

这样避免玩家看到半成品界面,也避免控件还没创建完时业务就去操作它。

9. 关闭和二次打开

关闭时建议不要立刻完全销毁复杂 UI,而是按情况使用:

1
2
3
4
Hide 模式
ManualOpen
ManualClose
View 复用

注意文档里也说了:Hide 模式要明确适配,否则可能造成内存压力。因为 Hide 后资源和对象还在。

二次打开时,如果 View 还在:

1
2
3
4
不重新 Load 资源
不重新 CreateWidget
减少 TakeWidget / RebuildWidget
只刷新必要数据

这能明显减少再次打开的耗时。

整体流程一句话版

1
2
3
4
5
6
7
8
9
10
11
12
打开 UI
-> 收集资源依赖
-> 异步加载资源,避免 Flush
-> 创建 UWidget 对象
-> 用分帧 AddChild 上舞台
-> TakeWidget 创建 SWidget
-> 超时则用 SSpacer 占位
-> Subsystem 后续 Tick 分帧补齐
-> 完成后恢复真实 SWidget 和 NewlyCreated
-> 业务分批刷新
-> 最后显示完整界面
-> 关闭时按需 Hide/复用,二次打开减少重建

最简单总结:

1
2
3
4
5
资源层:异步加载
U 对象层:少同步、少一次性创建
S 对象层:TakeWidget 分帧
业务层:列表/AddChild/数据绑定分批
生命周期:Loading/Hide/取消回调/复用

这套 UMG 优化的目标就是:把打开复杂 UI 时单帧集中的资源加载、Slate 创建、Lua 逻辑和列表生成,拆成可控的异步和分帧流程。

整理这段话 用面试格式给我

UE4面试基础知识(一) - tomato-haha - 博客园

虚幻面试笔记 UE - 知乎

QA7

UE4面试基础知识(一) - tomato-haha - 博客园

虚幻面试笔记 UE - 知乎

当然。下面这些题比较适合 UE 一年经验 面试者,覆盖 UE 网络同步、RPC、Actor 复制、角色权限、常见坑位等。你可以先按“能解释清楚 + 能举项目例子”的标准准备。

基础概念

  1. UE 的网络架构是怎样的?什么是 Client-Server 模型?
  2. Dedicated Server 和 Listen Server 有什么区别?
  3. Authority 是什么意思?HasAuthority() 通常用来判断什么?
  4. Role / RemoteRole 在 UE 网络中代表什么?UE5 中相关概念有什么变化?
  5. 什么是网络复制 Replication?
  6. Actor 想要被同步到客户端,需要开启哪些设置?
  7. bReplicatesbReplicateMovement 有什么区别?
  8. 哪些对象默认支持网络复制?UObject 可以直接复制吗?
  9. GameMode、GameState、PlayerController、PlayerState、Pawn 在网络中分别存在于哪里?
  10. 为什么 GameMode 只存在于服务器?

Actor 与属性复制

  1. 如何让一个变量同步到客户端?
  2. UPROPERTY(Replicated)UPROPERTY(ReplicatedUsing=OnRep_xxx) 有什么区别?
  3. GetLifetimeReplicatedProps 是做什么的?
  4. DOREPLIFETIME 的作用是什么?
  5. OnRep 函数什么时候会调用?服务器上会自动调用吗?
  6. 如果一个变量在服务器频繁变化,客户端会每帧收到吗?

UE 的属性复制不是每帧强制同步,而是受 Actor 的网络更新频率、带宽、优先级、相关性等影响。

17 UE 的属性复制是可靠的吗?

也就是说,服务器上某个复制变量的最终值会尽力保证同步到客户端。但如果变量短时间内变化多次,客户端不一定收到每一次中间变化。

18 什么是条件复制?比如 COND_OwnerOnlyCOND_SkipOwner 适合什么场景?

条件复制可以减少带宽,也可以避免重复表现。OwnerOnly 常用于只给本人看的数据,SkipOwner 常用于拥有者已经本地预测的表现。

19 RepNotify 常用来处理哪些逻辑?

20 数组、结构体、Actor 引用可以复制吗?需要注意什么?

数组、结构体、Actor 引用都可以复制。数组要注意带宽,复杂动态数组可以用 FastArraySerializer;结构体要保证字段可序列化;Actor 引用要求目标 Actor 也能被客户端复制到,否则引用无法正确解析。

RPC

  1. UE 中 RPC 有哪几种?
  2. Server RPCClient RPCNetMulticast RPC 分别在什么机器上执行?
  3. 调用 Server RPC 的前提是什么?
  4. 为什么客户端不能随便调用任意 Actor 上的 Server RPC?
  5. ReliableUnreliable RPC 有什么区别?
  6. 哪些场景适合 Reliable?哪些场景适合 Unreliable?
  7. NetMulticast RPC 是不是一定会在所有客户端执行?
  8. 在客户端调用 Multicast RPC 会发生什么?
  9. RPC 和属性复制应该如何选择?
  10. 开火、换弹、血量变化、播放特效分别适合用 RPC 还是属性复制?

角色与权限

  1. 什么逻辑应该放在服务器执行?什么逻辑可以放在客户端?
  2. 为什么伤害计算通常要放在服务器?
  3. 客户端预测是什么?为什么需要客户端预测?
  4. 什么是服务器校验?
  5. 如何防止客户端作弊,比如客户端自己修改血量或位置?
  6. PlayerController 为什么通常只复制给拥有者?
  7. IsLocallyControlled()HasAuthority() 有什么区别?
  8. GetOwner() 在网络 RPC 中有什么作用?
  9. 为什么客户端经常需要通过自己拥有的 PlayerController 或 Pawn 调用 Server RPC?
  10. Possess 流程在网络中大概是怎样的?

移动同步

  1. CharacterMovementComponent 为什么适合做网络移动?
  2. UE 角色移动同步大致是怎么做的?
  3. 什么是客户端预测和服务器回滚校正?
  4. 为什么自己控制的角色移动很流畅,而其他玩家可能有插值?
  5. 如果自己写 Pawn 移动同步,需要考虑哪些问题?
  6. bReplicateMovement 适合什么类型的 Actor?
  7. 物理模拟对象的网络同步有什么难点?
  8. 网络延迟下,角色位置抖动可能是什么原因?
  9. Root Motion 在网络同步中有什么注意点?
  10. 如果客户端移动被服务器频繁拉回,可能是什么问题?

常见场景题

  1. 多人游戏中,玩家开火的网络流程你会怎么设计?
  2. 命中检测放客户端还是服务器?为什么?
  3. 如果是 FPS 即时射击,如何兼顾手感和安全性?
  4. 玩家拾取道具的同步流程怎么做?
  5. 背包数据应该存在 PlayerState、PlayerController 还是 Character?为什么?
  6. 血量同步应该用 RPC 还是 Replicated 变量?
  7. 死亡、复活流程在网络中怎么设计?
  8. UI 血条如何响应网络同步的血量变化?
  9. 技能释放如何同步给其他客户端?
  10. 子弹是用 Replicated Actor、RPC,还是本地表现?如何取舍?

进阶但一年经验可准备

  1. 什么是 Actor Relevancy?
  2. NetCullDistanceSquared 是什么?
  3. NetUpdateFrequencyMinNetUpdateFrequency 有什么作用?
  4. 什么是 Dormancy?适合什么场景?
  5. 如何降低网络带宽?
  6. 大量怪物同步时,你会怎么优化?
  7. 什么是 Owner Only 复制?
  8. PlayerState 为什么适合存玩家名字、分数、队伍等信息?
  9. Seamless Travel 对网络对象有什么影响?
  10. UE 网络调试常用哪些命令或工具?

高频追问

  1. 为什么客户端设置 Replicated 变量,其他客户端看不到?
  2. 为什么 RPC 没有执行?
  3. 为什么 OnRep 没有触发?
  4. 为什么 Multicast 只在服务器执行了?
  5. 为什么客户端拿不到 GameMode?
  6. 为什么服务端生成的 Actor 客户端看不到?
  7. 为什么客户端调用 Server RPC 失败?
  8. 为什么变量复制有延迟?
  9. 为什么 Listen Server 上测试正常,Dedicated Server 出问题?
  10. PIE 多人测试和打包 Dedicated Server 有哪些差异?

建议重点准备的 10 道

  1. Actor 复制需要哪些条件?
  2. RPC 三种类型的区别和使用场景。
  3. HasAuthority()IsLocallyControlled() 的区别。
  4. GameMode、GameState、PlayerController、PlayerState 的网络存在位置。
  5. 血量同步怎么做?
  6. 开火同步怎么做?
  7. Server RPC 为什么必须从拥有权链路调用?
  8. RepNotify 的作用和注意点。
  9. 客户端预测是什么?
  10. 网络同步优化有哪些方式?

面试时不要只背定义,最好用这种结构回答:

1
2
3
先说概念;
再说 UE 中怎么实现;
最后说项目里什么场景会用。

例如血量同步:

1
2
3
4
血量应该由服务器修改,客户端不能直接决定结果。
Character 或 AttributeComponent 上把 Health 设置为 ReplicatedUsing。
服务器受到伤害后修改 Health,客户端通过 OnRep_Health 更新 UI、播放受击表现。
如果需要立即表现,可以额外用 Client 或 Multicast RPC 播放特效,但最终数值以服务器复制为准。