GamePlay框架

使用了快一年半的UE也没有完全了解GamePlay框架到底是什么。这时突然有一个面试的机会,正好上一次面试没有把Gameplay框架回答完整,所有这一次想着用10个题目来建立对这个的初步了解。

  1. 简述 UE Gameplay 框架的核心类及其职责

    • 核心类包括这样几种,首先是UObject、AActor、APawn、ACharacter、AController、AGamemode、AGameState、APlayerState.UGameInstance
    • 反射是用宏为提供引擎和编辑器各种功能,封装你的类。UObject是所有对象的基类,他是继承里面最头上的那个,可以有像序列化,反射,GC(垃圾回收)。而Actor是指可以防止在关卡中的一个实体,可以有位置,组件以及网络同步的能力。而APawn是可以控制游戏实体,比如角色和AI,而UGameInstance是快关卡存在的一个全局管理器。
  2. GameMode、GameState 和 PlayerState 在多人游戏中的复制行为有何不同?

    • 用一个简单的网络游戏来解释,GameMode是存在于服务器,不复制到客户端,而GameState是在服务器创建然后复制到客户端,用来同步全局的状态比较倒计时和比分。而PlayerState是每一个玩家和GameState相同,也是服务器复制到客户端
  3. . 为什么 Controller 和 Pawn 要分离?这样设计的好处是什么?

    考察点:架构设计思想

    • Controller 生命周期长于 Pawn(如角色死亡后 Controller 仍存在)。
    • 支持切换控制对象(如观战模式、复活机制)。
    • 逻辑解耦:输入、相机、UI 等由 Controller 处理,物理表现由 Pawn 处理。
    • 网络同步更清晰:Controller 只存在于本地或服务器,Pawn 可复制。

4. UGameInstance 的作用是什么?什么数据应该放在 GameInstance 中?

考察点:生命周期与数据管理

  • 作用:在整个游戏进程(从启动到退出)中唯一存在,不受关卡切换影响。

  • 适用场景

    • 跨关卡数据(如玩家总金币、设置选项)
    • 存档系统管理
    • 在线子系统(会话、好友、排行榜)
    • 全局事件总线或服务管理器

5. Pawn 和 Character 有什么区别?何时使用 Character?

考察点:类继承与功能选择

  • Pawn:基础可控制实体,无默认移动或碰撞。

  • Character

    :继承自 Pawn,内置:

    • CapsuleComponent(碰撞)
    • SkeletalMeshComponent(骨骼网格)
    • CharacterMovementComponent(行走、跳跃、重力等)
  • 使用 Character:当需要人形角色(带移动、动画、碰撞)时。


6. 描述一个玩家从进入游戏到控制角色的完整流程(含关键类的创建顺序)。

考察点:生命周期与初始化流程
典型流程:

  1. 启动游戏 → 创建 UGameInstance
  2. 加载关卡 → 创建 UWorldLevel
  3. 实例化 AGameMode(仅服务器)
  4. GameMode 创建 AGameState
  5. 玩家连接 → 创建 PlayerControllerPlayerState
  6. GameMode 调用 SpawnDefaultPawnFor() 生成 Pawn(如 Character)
  7. PlayerController::Possess(Pawn) → 玩家开始控制角色

7. 如何实现跨关卡的数据持久化?为什么不能把数据存在 GameMode 或 Pawn 中?

考察点:生命周期理解

  • GameMode/Pawn:随关卡卸载而销毁,无法跨关卡保留。

  • 正确做法

    • 使用 UGameInstance 或其子系统(如 SaveGameSubsystem
    • 使用 USaveGame 配合磁盘存档
    • 利用 PlayerState(若需网络同步且关卡间保留)

8. Gameplay 框架中哪些类是 UObject,哪些是 Actor?为什么这样区分?

考察点:引擎底层设计

  • UObject 派生

    (无位置、不可放置):

    • UGameInstance, UPlayer, UWorld(特殊 UObject)
  • Actor 派生

    (有位置、可放置、可网络复制):

    • APawn, AController, AGameMode, AGameState, APlayerState
  • 原因:Actor 需要支持变换、Tick、组件、网络复制等游戏世界特性;UObject 更轻量,适合管理器类。


9. PlayerController 和 PlayerState 的关系是什么?各自负责什么?

考察点:职责划分

  • PlayerController

    • 本地存在(客户端有自己的 PC,服务器也有)
    • 处理输入、HUD、相机、本地决策
    • 不复制(每个端独立)
  • PlayerState

    • 服务器创建,复制到所有客户端
    • 存储需要同步的玩家数据(ID、分数、队伍、状态)
  • 一对一绑定:每个 PlayerController 对应一个 PlayerState


10. 如何在 Gameplay 框架中合理分配 C++ 与蓝图的职责?

考察点:工程实践与性能意识

  • C++ 实现

    • 核心框架类(GameMode、Character 基类、Subsystem)
    • 性能敏感逻辑(移动、伤害计算、状态机)
    • 网络同步逻辑(Replicated 属性、RPC)
  • 蓝图实现

    • 关卡特定逻辑(LevelScriptActor)
    • UI 交互、动画蒙太奇触发
    • 美术/策划可配置的行为(通过暴露变量或函数)
  • 原则:底层通用逻辑用 C++,上层灵活逻辑用蓝图,通过 BlueprintImplementableEventBlueprintNativeEvent 桥接。

UE数据结构

1. UE 中常用的动态数组是什么?它与 C++ 标准库中的 std::vector 有何异同?

  • ue是使用TArray作为动态数组。作为动态数组它是支持随机访问和自动扩容的。同时还指针UE的反射系统,支持序列化以及垃圾回收。
  • 默认使用 UE 的内存分配器(如 FMemory),可定制 Allocator。
  • 提供更多游戏开发友好接口(如 AddUnique, FindByPredicate 等)

2. UE 中如何实现哈希表?TMap 和 TSet 的底层原理是什么?

  • TMap<Key, Value>TSet<Element> 基于开放寻址法(Open Addressing)实现,而非链表或红黑树。
  • 使用线性探测(Linear Probing)解决冲突。
  • 支持自定义 Key 的 Hash 函数和 Equality 函数。
  • std::unordered_map 类似,但集成 UE 内存管理和序列化系统。

3. UE 中的结构体(UStruct)和类(UClass)在内存布局上有何不同?

  • 答案要点:
    • UStruct(如 USTRUCT() 标记的结构体)是值类型,通常栈分配或内嵌在其他对象中,不参与 GC。
    • UClass 衍生自 UObject,是引用类型,堆分配,受 UE 垃圾回收系统管理。
    • UStruct 可包含 UPROPERTY(),支持反射和蓝图访问,但无生命周期管理。

3. UE 中的 TSharedPtr 和 TWeakPtr 是如何工作的?它们与 C++ 的 std::shared_ptr 有何区别?

  • 答案要点:
    • TSharedPtr 是引用计数智能指针,TWeakPtr 是弱引用,避免循环引用。
    • UE 版本支持线程安全选项(通过模板参数指定)。
    • 可与 UObject 系统配合使用(但通常 UObject 使用 UPROPERTY() + 引用而非智能指针)。
    • std::shared_ptr 功能类似,但使用 UE 内存分配器,且可在非 C++11 环境下工作(历史原因)。

4. UE 中的结构体(UStruct)和类(UClass)在内存布局上有何不同?

  • 答案要点:
    • UStruct(如 USTRUCT() 标记的结构体)是值类型,通常栈分配或内嵌在其他对象中,不参与 GC。
    • UClass 衍生自 UObject,是引用类型,堆分配,受 UE 垃圾回收系统管理。
    • UStruct 可包含 UPROPERTY(),支持反射和蓝图访问,但无生命周期管理。

5. UE 如何处理大型数据集合的内存对齐和缓存友好性?举例说明。

  • 答案要点:
    • 使用 TArray 连续内存提高缓存命中率。
    • 支持自定义 Allocator(如 TInlineAllocator<N> 将小数组放在栈上)。
    • 对频繁访问的数据(如组件数组)推荐使用 结构体数组(AoS) vs 数组结构体(SoA) 设计,ECS 架构(如 Mass Entity)采用 SoA 提升性能。

6. 解释 UE 中的“稀疏数组”(TSparseArray)和“压缩稀疏数组”(TCompressedSparseArray)的用途。

  • 答案要点:
    • TSparseArray:允许高效插入/删除,保留元素索引(即使中间有空洞),常用于 UObject 的属性复制或网络同步。
    • TCompressedSparseArray:进一步压缩存储,仅存储有效元素及其索引映射,节省内存。
    • 两者都支持 O(1) 访问(通过间接索引),适合频繁增删但需保持 ID 稳定的场景。

7. UE 中的 FScriptArray、FScriptMap、FScriptSet 是什么?为什么需要它们?

  • 答案要点:
    • 这些是 UE 反射系统内部使用的“脚本化容器”,用于在运行时动态操作 TArray/TMap/TSet
    • 蓝图或网络复制系统通过它们访问容器内容,而无需知道具体模板类型。
    • 它们是未模板化的底层结构,配合 UProperty 实现泛型操作。

8. 如何在 UE 中实现一个支持蓝图暴露的自定义数据结构?

  • 答案要点:
    • 使用 USTRUCT(BlueprintType) 标记结构体。
    • 成员变量用 UPROPERTY() 声明。
    • 若需复杂逻辑,可封装为 UFUNCTION(BlueprintCallable) 的静态工具函数。
    • 注意:蓝图不支持模板,因此不能直接暴露 TArray<MyStruct> 以外的泛型容器。

9. UE 的垃圾回收(GC)如何影响数据结构的设计?举例说明应避免的情况。

  • 答案要点:
    • 只有继承自 UObject 的对象受 GC 管理。
    • TArray<UObject*> 中,若未用 UPROPERTY() 声明,GC 无法追踪,可能导致悬空指针。
    • 应避免在非 UObject 类中裸存 UObject*,除非用 TWeakObjectPtr 或标记为 UPROPERTY()
    • 循环引用(如 A 引用 B,B 引用 A)会导致内存泄漏,需用 TWeakObjectPtr 打破。

10. UE5 中引入的 Chaos 物理系统或 Niagara 粒子系统使用了哪些特殊的数据结构优化?

  • 答案要点(选答方向):
    • Chaos:使用并行友好的空间分区结构(如 BVH、Grid-based Broadphase),支持 SIMD 加速。
    • Niagara:采用 SoA(Structure of Arrays)存储粒子属性,提升 GPU/CPU 缓存效率;使用计算着色器处理大规模数据。
    • 两者都强调数据局部性和多线程安全性,避免传统面向对象设计带来的性能瓶颈。
Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐