一、UE 基础与框架(10 题)
1. GameInstance、GameMode、GameState、PlayerController、PlayerState、Pawn/Character 的作用和关系?
- GameInstance:游戏进程级,整个游戏生命周期存在(切关卡不销毁),适合存账号、全局配置、网络连接等。
- GameMode:只在服务器存在(单机时本机就是服务器),每个地图可以有自己的 GameMode,负责规则、Pawn 选择、玩家登录等。
- GameState:同步到客户端的游戏状态(比分、时间、回合信息等),所有机器都有。
- PlayerController:代表“玩家控制端”,处理输入、UI、客户端逻辑,客户端和服务器各有一个对应的 PC(带网络镜像)。
- PlayerState:玩家状态,可复制到所有客户端(如名字、阵营、得分)。
- Pawn/Character:被控制的实体,Character 继承 Pawn,带 MovementComponent、胶囊体等。
2. Actor 的生命周期有哪些关键函数?大概在什么时候调用?
常用顺序(略简化):
- 构造函数:对象生成时,编辑器中也可能多次调用。
PostInitializeComponentsBeginPlay:关卡开始运行时调用,Play/运行时只会调用一次。Tick(float DeltaTime):每帧调用(如果启用 Tick)。EndPlay:关卡结束或 Actor 被 Destroy 时调用。Destroyed:Actor 被销毁时调用。
3. UObject 和 AActor 有什么区别?
- UObject:最基础的可 GC 对象,没有世界坐标、没有可见性,不参与场景,只能作为逻辑数据、管理器等。
- AActor:继承自 UObject 和 Actor 框架基类,有 Transform、可以放到关卡里、可被渲染/碰撞/网络复制。
4. 在 UE4 中,Tick 的性能问题很常见,你会怎么优化 Tick?
思路:
- 能不用 Tick 就不用:用定时器
GetWorld()->GetTimerManager().SetTimer。 - 降低 Tick 频率:
PrimaryActorTick.TickInterval。 - 在远距离/不可见时禁用 Tick:
SetActorTickEnabled(false)。 - 抽离到组件或子系统中,集中管理更新(例如批量处理)。
- 蓝图 Tick 尽量少写复杂逻辑,改为 C++ 或事件驱动。
5. 说说 Level、World、GameInstance 的关系。
- World:一个运行世界实例,可以理解为当前正在跑的关卡集合(包括持久关卡 + 子关卡)。
- Level:World 中的一个关卡(地图文件),World 可以包含多个 Level。
- GameInstance:进程级单例,持有当前 World,切换关卡时 World 会重建,但 GameInstance 不会。
6. 在 C++ 中如何让一个变量在编辑器中可见并且蓝图可读写?
cpp
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Move")
float Speed;
7. Actor 的 BeginPlay 与 PostInitializeComponents 有什么差别?
PostInitializeComponents:组件初始化完成时被调用,可能出现在 BeginPlay 之前,用来处理组件间的依赖。BeginPlay:游戏开始(或 Actor 被 Spawn)时调用,通常用来做游戏逻辑初始化,比PostInitializeComponents更“晚”。
8. 什么是组件化设计?UE4 中常见的组件有哪些?
- 组件化设计:把功能拆分成可复用的模块(组件),Actor 通过组合组件获得功能。
- 常见组件:
StaticMeshComponent/SkeletalMeshComponentCapsuleComponent/BoxComponent等碰撞组件CharacterMovementComponent/ProjectileMovementComponentAudioComponentWidgetComponent
9. 为什么说蓝图适合做“逻辑拼装”,C++ 适合做“底层实现”?
- 蓝图优点:迭代快、可视化、策划和关卡设计能直接修改。
- C++ 优点:性能更好、可维护性高、工程结构更清晰、方便多人协作。
- 常见实践:核心数据结构、复杂算法、底层系统用 C++;具体技能逻辑、关卡事件、UI 交互用蓝图。
10. 在 UE4 中如何组织跨关卡的“全局管理器”?
- 推荐:UGameInstance / UGameInstanceSubsystem。
- 不建议用静态全局单例管理 UObject(容易和 GC、热重载冲突)。
二、C++ 与蓝图交互(10 题)
11. 如何让一个 C++ 函数可以在蓝图中调用?
cpp
UFUNCTION(BlueprintCallable, Category="MyFunc")
void DoSomething();
12. BlueprintCallable 和 BlueprintPure 的区别?
- BlueprintCallable:普通蓝图函数,可有执行引脚,可更改对象状态。
- BlueprintPure:纯函数,没有执行引脚,不应该有副作用,只根据输入计算输出。
13. BlueprintImplementableEvent 和 BlueprintNativeEvent 的区别?
- BlueprintImplementableEvent:函数只声明不实现,必须在蓝图中实现。
- BlueprintNativeEvent:既可以蓝图实现,也可以 C++ 有默认实现。C++ 写
Func_Implementation版本。
示例:
cpp
UFUNCTION(BlueprintNativeEvent)
void OnFire();
virtual void OnFire_Implementation();
14. C++ 中如何调用蓝图实现的 BlueprintImplementableEvent?
像普通函数一样调用:
cpp
MyActor->OnFire(); // OnFire 是 BlueprintImplementableEvent
实际运行时会调用蓝图里的实现。
15. 如何在 C++ 中通过类默认值设置一个组件?
cpp
AMyActor::AMyActor()
{
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
RootComponent = Mesh;
}
16. UPROPERTY 中 VisibleAnywhere 和 EditAnywhere 有什么区别?
- VisibleAnywhere:在编辑器中可见但不可编辑。
- EditAnywhere:在编辑器中可编辑(包括默认值、关卡实例)。
17. 如何在蓝图中创建你写的 C++ Actor?
- C++ 类标
UCLASS()且继承自AActor。 - 编译后,在 Content Browser 里右键 → Blueprint Class → 选该 C++ 类作为父类,即可做成蓝图。
18. 给一个简单例子:在 C++ 中添加一个输入“W”控制前进移动。
cpp
void AMyCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
Super::SetupPlayerInputComponent(PlayerInputComponent);
PlayerInputComponent->BindAxis("MoveForward", this, &AMyCharacter::MoveForward);
}
void AMyCharacter::MoveForward(float Value)
{
AddMovementInput(GetActorForwardVector(), Value);
}
在 Project Settings → Input 里配置 Axis Mapping: MoveForward → W=1, S=-1。
19. 如何在蓝图中调用 C++ 的静态函数?
- C++ 静态函数写为
UFUNCTION(BlueprintCallable)且static,然后在蓝图中通过“类”节点调用。
cpp
UCLASS()
class UMyLib : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category="MyLib")
static int32 AddInt(int32 A, int32 B);
};
20. BlueprintFunctionLibrary 是干什么的?
- 用来集中放“工具函数”的类,不需要实例化。
- 所有函数都是
static,蓝图中以“函数库”的形式使用。
三、网络基础与 Replication(15 题)
21. UE4 使用什么网络模型?是 P2P 还是 C/S?
- 标准的 Client-Server(服务器权威) 模型。
- 一般是 Listen Server 或 Dedicated Server(专用服务器)。
22. 让一个 Actor 支持网络复制需要做什么?
- 在 C++ 构造函数中:
cpp
bReplicates = true; - 或在蓝图中勾选
Replicates。 - 如果是移动的 Pawn/Character,还常用
bReplicateMovement = true;。
23. 变量复制(Replicated)和 RepNotify 的区别?
- Replicated:值同步到客户端,但不会自动触发回调。
- ReplicatedUsing=OnRep_Var:在变量从网络更新时会调用指定回调函数。
示例:
cpp
UPROPERTY(ReplicatedUsing=OnRep_Health)
float Health;
UFUNCTION()
void OnRep_Health();
24. 在 C++ 中如何声明一个要复制的变量?完整流程。
- 在头文件:
cpp
UPROPERTY(Replicated) int32 Score; - 在 cpp 中重写:
cpp
void AMyActor::GetLifetimeReplicatedProps(TArray< FLifetimeProperty >& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AMyActor, Score); }
25. UE4 中 RPC 有哪几种类型?分别怎么用?
常用 4 种:
- Server:从客户端调用,函数在服务器执行(需要校验逻辑)。
- Client:从服务器调用,在特定客户端执行。
- NetMulticast:从服务器调用,在所有连接的客户端执行。
- Reliable / Unreliable:是否可靠传输。
示例:
cpp
UFUNCTION(Server, Reliable)
void ServerFire();
UFUNCTION(NetMulticast, Unreliable)
void MulticastPlayFireEffect();
26. 简述一个射击流程:客户端按下开火键,到服务器判定命中并广播特效。
大致流程:
- 客户端输入本地触发 → 播放本地开火动作(可预测),并调用
ServerFire()。 ServerFire()在服务器执行:- 做射线检测/命中判定。
- 修改目标血量。
- 调用
MulticastPlayFireEffect()。
MulticastPlayFireEffect()在所有客户端执行 → 播放粒子、声音等特效。- 血量变量通过 Replication 或 RepNotify 同步到所有客户端。
27. HasAuthority() 返回什么?常见用法?
HasAuthority()在服务器返回 true,在客户端返回 false。- 常用来分支逻辑,比如只在服务器做状态修改:
cpp
if (HasAuthority()) { Health -= Damage; }
28. 为什么一般要求“只在服务器修改游戏权威数据”?
- 防止作弊:客户端数据容易被篡改。
- 保证多客户端状态一致:以服务器为唯一真相来源。
- 客户端只做表现和预测,最终结果以服务器为准。
29. 怎么让客户端“预测移动”,减少网络延迟手感?
- UE4 CharacterMovementComponent 内置“客户端预测 + 服务器回滚”机制。
- 客户端先本地移动,再把输入发送到服务器。
- 服务器计算权威位置,再对客户端位置进行纠正。
- 对于自定义移动,可参照 UE4 源码或实现类似“输入记录+回放”的机制。
30. bAlwaysRelevant、NetUpdateFrequency 大概是干嘛的?
bAlwaysRelevant:对所有玩家总是相关,总是考虑复制(比如 GameState)。NetUpdateFrequency:网络更新频率,数值越大,位置/状态同步越频繁,带宽消耗越大。
31. 两个玩家客户端看到的某个 Actor 运动不一致,你会从哪些方向排查?
- 是否在客户端直接修改 Transform(应该只在服务器修改)。
- 是否开启了
bReplicates、bReplicateMovement。 - 是否有多个 Actor 实例(一个没复制,一个复制了)。
- 网络延迟 / 丢包导致的插值问题,查看 Net Cull Distance, Net Update Frequency。
32. UE4 中 Session/匹配通常用哪些系统?(简单说)
- 在线子系统 OnlineSubsystem (Steam, Null 等)。
- 使用
CreateSession,FindSessions,JoinSession等接口。 - 对于基础面试,只要大致知道是
OnlineSubsystem即可。
33. 什么数据适合用 Replication,什么适合自己写消息协议?
- 简单频繁的小数据、Actor 状态:用 UE 原生 Replication。
- 复杂的自定义逻辑、大量数据、跨系统消息:可以考虑自己封装 RPC 或底层 Socket(少见,偏引擎/网络工程)。
34. 一般哪些类会需要网络复制字段?
- ACharacter / APawn:位置、动作状态。
- APlayerState:比分、昵称、阵营。
- AGameState:整体游戏时间、回合状态。
- 各种可交互 Actor:开关状态、血量、耐久等。
35. 蓝图中如何简单区分当前是在服务器还客户端?
- 使用节点:
Switch Has AuthorityIs Server、Is Dedicated Server
四、内存管理与性能(10 题)
36. UObject 指针为什么建议用 UPROPERTY 标记?
- 让 GC 能追踪引用关系,避免误删或内存泄漏。
- 支持序列化、复制、编辑器曝光等。
37. TSharedPtr/TWeakPtr 和 UObject 指针有什么关系?
- UObject 是由 UE 的 GC 管理,一般使用裸指针或
TWeakObjectPtr。 - TSharedPtr / TUniquePtr 适合非 UObject 类型的 C++ 对象(不会被 GC)。
- 注意:不要用
TSharedPtr管理 UObject。
38. TSoftObjectPtr 用来解决什么问题?
- 软引用资源:不立刻加载资源,只保存引用路径。
- 在需要时再异步加载,减小初始内存和加载时间。
- 常用于大资源(大贴图、音频、地图)按需加载。
39. 蓝图为什么被认为比较“慢”?你会怎么优化?
- 蓝图是解释执行,有额外虚拟机开销。
- 大量 Tick / 复杂逻辑 / 频繁循环会更慢。
- 优化方法:
- 热路径逻辑用 C++ 实现。
- 减少蓝图 Tick。
- 合理使用事件驱动、定时器。
- 避免在 Tick 里频繁做复杂字符串处理、动态加载等。
40. 遇到性能问题,你会用什么工具分析?
- UE4 Profiler / Session Frontend。
- Unreal Insights。
- Stat 命令:
stat unit,stat fps,stat game,stat gpu等。 - 对象统计:
obj list,memreport。
41. Draw Call 是什么?如何减少?
- Draw Call:一次 CPU 向 GPU 发送的绘制指令。
- 减少方法:
- 使用 Instanced Static Mesh。
- 合并 Mesh(特别是静态场景)。
- 合理使用材质实例,减少材质数量。
- 视锥裁剪、Occlusion Culling、LOD。
42. GC 卡顿一般怎么解决?
- 控制 UObject 数量,避免大量短生命周期 UObject。
- 避免在 Tick 里频繁创建/销毁 UObject。
- 调整 GC 周期参数(如
gc.TimeBetweenPurgingPendingKillObjects)。 - 合理使用 Pool(对象池)模式。
43. 编辑器里运行没问题,打包后崩溃,常见排查方向?
- 资源引用是否通过软引用但没加载。
- 是否使用了 Editor-only 的宏或模块(
#if WITH_EDITOR)。 - 是否有未初始化指针,编辑器容错高,打包版本更严格。
- 日志:查看 Saved/Logs 下打包版本的日志。
44. 如何减小打包包体?
- 移除未使用资源(Fix up redirectors + Reference Viewer)。
- 使用压缩、烘焙(Cook)优化。
- 尽量复用材质实例、贴图。
- 关掉不必要的插件、内容示例。
45. 如何处理大地图的加载时间问题?
- 使用 Level Streaming:分块加载子关卡。
- 加载界面期间异步加载主要资源。
- 合理分配 Stream Volume,靠近玩家区域才加载。
- 如果是 UE5 则考虑 World Partition(你现在 UE4,可提一下有了解)。
五、实战/场景题(5 题)
46. 设计一个“按 E 键互动”的系统,简要说一下架构。
思路:
- 在角色上加一个
UInteractionComponent。 - 使用碰撞/射线检测前方可交互对象(实现一个接口
IInteractable)。 - 按 E 时:
- 客户端先本地调用
Interact。 - 再发送
ServerInteractRPC,在服务器执行真实交互(开门、捡道具等)。
- 客户端先本地调用
- 可交互对象实现
Interact_Implementation(可蓝图/可 C++)。
47. 设计一个简单技能系统(冷却 + 消耗 + 网络同步)。
USkill基类(UObject):- 属性:冷却时间、消耗、动画、特效等。
- 方法:
CanActivate、Activate。
- 角色身上有
USkillComponent,持有技能列表。 - 客户端按键 → 调用
ServerTryActivateSkill(SkillId)。 - 服务器检查冷却、消耗,通过则:
- 修改资源(蓝量等),开始冷却。
- 调用
MulticastPlaySkillEffect播放特效。
- 冷却时间、资源消耗通过 Replicated 变量同步给客户端 UI。
48. 你做过的项目中,如果动画蒙太奇播放不同步(客户端/服务器不一致)会怎么处理?
- 不在客户端直接
PlayMontage,而是在服务器触发,再通过Multicast或变量状态同步。 - 使用状态变量(如
bIsAttacking)复制,客户端根据状态决定是否播放。 - 对关键时刻(如伤害判定)只在服务器做,客户端只做表现。
49. 如果一个 Actor 需要在所有客户端显示“被破坏”的状态,你会怎么设计?
- 有一个
bIsDestroyed变量 +RepNotify:cppUPROPERTY(ReplicatedUsing=OnRep_IsDestroyed) bool bIsDestroyed; - 服务器修改
bIsDestroyed = true; - 在
OnRep_IsDestroyed里在客户端播放破碎特效、隐藏 Mesh、禁止交互等。
50. 简单说说 UE4 项目中你是如何与策划/美术协作的,代码和蓝图如何分层?
一个“理想回答”方向:
- 核心框架、数据结构用 C++ 搭好(角色、技能组件、网络逻辑)。
- 给策划暴露
BlueprintCallable接口和可配置的UPROPERTY。 - 策划/美术通过蓝图生成具体关卡逻辑/技能配置/特效组合。
- 资源通过 DataTable 或配置表驱动,避免硬编码。

本文是原创文章,采用 CC BY-NC-SA 4.0 协议,完整转载请注明来自 布总
评论
隐私政策
0/500
滚动到此处加载评论...
