面经-UE相关

本文最后更新于:2024年12月26日 凌晨

UE最基础的五个类是什么

  1. GameMode是整个关卡的控制器,用来编写整个关卡的业务逻辑
  2. GameState是整个关卡的状态,用来存储整个关卡的游戏数据;
  3. DefaultPawn是玩家角色,若是角色扮演类游戏则就是在游戏场景中看到的角色实体;
  4. PlayerController是玩家的控制器,用来编写和玩家相关的控制逻辑;
  5. PlayerState是玩家的状态,用来存储和玩家相关的状态数据。

FName和FString

FName:着重于表示名称不区分大小写不可更改,引擎中的资源名称都是FName类型,通过一个轻型系统重复使用字符串,FName创建时会根据内容创建一个Hash值,且同样的内容只会存储一次。通过Hash值在进行FName的查找和访问时速度较快,而在比较的时,也不需要比较字符串内容,直接比较Hash值来区分不同FName字符串。

另外两种字符串到FName的转换(特别注意的是,FText不能直接转换到FName,可先转换为FString,再转换为FName):

1
2
3
4
5
6
7
8
9
10
FString str = TEXT("Hello world~");
FText txt = LOCTEXT("keyName", "theValue");
FText txtNS = NSLOCTEXT("TextNameSpace", "keyName", "theValue");

// FString 转 FName:不可靠,丢失大小写信息
FName name1 = FName(*str);

FString strFromTxt = txt.ToString();
// FText 先转 FString,再转 FName:不可靠,丢失大小写信息,丢失本地化信息可能导致语言转换的潜在风险
FName name2 = FName(*strFromTxt);

FString:最接近std::string,着重在于字符串的操作,提供了大量对字符串的操作接口,是三者中唯一可修改的字符串类型,也正因如此,FString对比其它两种字符串来说消耗更高性能更低

另外两种字符串到FString的转换

1
2
3
4
5
6
7
8
FName name = TEXT("I am Frank~");
FText txt = LOCTEXT("keyName", "theValue");
FText txtNS = NSLOCTEXT("TextNameSpace", "keyName", "theValue");

// FName 转 FString:可靠
FString str1 = name.ToString();
// FText 转 FString:不可靠,丢失本地化信息可能导致语言转换的潜在风险
FString str2 = txt.ToString();

FText:着重在于显示与本地化,显示可理解玩家能直接看到的信息,本地化即多语言的处理,不可更改。相较于另外两种类型,FText会更加的臃肿,但提供了优秀的本地化功能。

另外两种字符串到FText的转换

1
2
3
4
5
6
7
FString str = TEXT("Hello world~");
FName name = TEXT("I am Frank~");

// FString 转 FText:可靠
FText txt1 = FText::FromString(str);
// FName 转 FText:可靠
FText txt2 = FText::FromName(name);

软引用和硬引用

加载的步骤

Actor如何实现一个借口类?

UE4反射机制

UHT

UE4反射机制

C++反射机制

Actor生命周期

img

作者:一勾大师
链接:https://zhuanlan.zhihu.com/p/308217207
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

UE4中创建Actor的方法主要有如下几种:从磁盘加载Play In Editor(简称PIE)SpawnActor动态生成延迟生成。下面对不同形式的actor加载进行介绍

从磁盘加载

已位于关卡中的 Actor 使用此路径,如 LoadMap 发生时、或 AddToWorld(从流关卡或子关卡)被调用时,一般是提前搭好的场景中资源的加载方式。

  1. 包/关卡中的 Actor 从磁盘中进行加载。
  2. PostLoad - 在序列化 Actor 从磁盘加载完成后被调用。在此处可执行自定义版本化和修复操作。PostLoad 与 PostActorCreated 互斥。
  3. InitializeActorsForPlay
  4. 为未初始化的 Actor 执行 RouteActorInitialize(包含Stream Level的加载)
  • PreInitializeComponents - 在 Actor 的组件上调用 InitializeComponent 之前进行调用。
  • InitializeComponent - Actor 上定义的每个组件的创建辅助函数。
  • PostInitializeComponents - Actor 的组件初始化后调用。

5.BeginPlay - 关卡开始后调用。

Play in Editor

Play in Editor 路径与 Load from Disk 十分相似,然而 Actor 却并非从磁盘中加载,而是从编辑器中复制而来,一般是在Debug时资源的加载方式。

  1. PostDuplicate 被调用。
  2. InitializeActorsForPlay
  3. 为未初始化的 Actor 执行 RouteActorInitialize(包含Stream Level的加载)
  • PreInitializeComponents - 在 Actor 的组件上调用 InitializeComponent 之前进行调用。
  • InitializeComponent - Actor 上定义的每个组件的创建辅助函数。
  • PostInitializeComponents - Actor 的组件初始化后调用。

5.BeginPlay - 关卡开始后调用。

生成

这是生成(实例)Actor 时的路径。在实际工程中一般是通过SpawnActor等函数加载的资源

  1. SpawnActor 被调用。
  2. PostSpawnInitialize
  3. PostActorCreated - 创建后即被生成的 Actor 调用,构建函数类行为在此发生。PostActorCreated 与 PostLoad 互斥。
  4. ExecuteConstruction
  • OnConstruction - Actor 的构建。蓝图 Actor 的组件在此处创建,蓝图变量在此处初始化

5.PostActorConstruction

  • PreInitializeComponents - 在 Actor 的组件上调用 InitializeComponent 之前进行调用。
  • InitializeComponent - Actor 上定义的每个组件的创建辅助函数。
  • PostInitializeComponents - Actor 的组件初始化后调用。

6.OnActorSpawned 在 UWorld 上播放。

7.BeginPlay 被调用。

延迟生成

将任意属性设为”Expose on Spawn”即可延迟 Actor 的生成。

  1. SpawnActorDeferred - 生成程序化 Actor,在蓝图构建脚本之前进行额外设置。
  2. SpawnActor 中的所有操作发生;PostActorCreated 之后发生以下操作:
  • 通过一个有效但不完整的 Actor 实例设置/调用多个”初始化函数“。
  • FinishSpawningActor -调用后对 Actor 进行最终化,在 Spawn Actor 行中选取 ExecuteConstruction。

生命周期的结束

尽管生成方式有比较大的区别,上述几种生成方法的结束流程却都一样。

Destroy - 游戏在 Actor 需要被移除时手动调用,但游戏进程仍在继续。Actor 被标记为等待销毁并从关卡的 Actor 阵列中移除。

EndPlay - 在数个地方调用,保证 Actor 的生命走向终点。在游戏过程中,如包含流关卡的 Actor 被卸载,Destroy 将发射此项和关卡过渡。调用 EndPlay 的全部情形:

  • 对 Destroy 显式调用。
  • Play in Editor 终结。
  • 关卡过渡(无缝行程或加载地图)。 包含 Actor 的流关卡被卸载。
  • Actor 的生命期已过。
  • 应用程序关闭(全部 Actor 被销毁)。

无论这些情形出现的方式如何,Actor 都将被标记为 RF_PendingKill,因此在下个垃圾回收周期中它将被解除分配。此外,可以考虑使用更整洁的 FWeakObjectPtr<AActor> 代替手动检查”等待销毁”。

OnDestroy - 这是对 Destroy 的旧有反应。也许应该将这里的所有内容移到 EndPlay,因为它被关卡过渡和其他游戏清理函数调用。

垃圾回收

一个对象被标记待销毁的一段时间后,垃圾回收会将其从内存中实际移除,释放其使用的资源。

在对象的销毁过程中,以下函数将被调用:

  1. BeginDestroy - 对象可利用此机会释放内存并处理其他多线程资源(即为图像线程代理对象)。与销毁相关的大多数游戏性功能理应在 EndPlay 中更早地被处理。
  2. IsReadyForFinishDestroy - 垃圾回收过程将调用此函数,以确定对象是否可被永久解除分配。返回 false,此函数即可延迟对象的实际销毁,直到下一个垃圾回收过程。
  3. FinishDestroy - 最后对象将被销毁,这是释放内部数据结构的另一个机会。这是内存释放前的最后一次调用。

UFUNCTION

概念

UFUNCTION/UPROPERTY/UCLASS

反射系统是可以选择加入的。你需要给暴露给反射系统的类型或属性添加注解,这样Unreal Header Tool (UHT)就会在编译工程的时候利用那些信息生成特定的代码。

标记

为了标记一个头文件包含反射类型,需要在文件顶部添加一个特殊的include文件。该文件必须放在#include的最后,这让UHT知道它需要考虑这个文件,并且在反射系统的实现里也是需要的。

1
#include "FileName.generated.h"

你现在可以使用UENUM()、UCLASS()、USTRUCT()、UFUNCTION()、以及UPROPERTY()来在头文件中注解不同的类型以及成员变量。每一个宏都会出现在类型或者成员变量的前面,并且可以包含额外的修饰符关键字。

C++是没法直接通过函数名来调用的,因为存的都是地址,但是在蓝图里面可以,这就用到了反射机制

常用的有哪些

简述UE中的UFUNCTION宏和UPROPERTY宏常用属性

UFunction

Category = "TopCategory|SubCategory|Etc" 在蓝图编辑工具中显示时指定函数的类别。使用 | 运算符定义嵌套类别。
Client 此函数仅在拥有在其上调用此函数的对象的客户端上执行。用于声明名称与主函数相同的附加函数,但是末尾添加了_Implementation。必要时,此自动生成的代码将调用 _Implementation 方法。
CustomThunk UnrealHeaderTool 代码生成器将不为此函数生成thunk,用户需要自己通过 DECLARE_FUNCTIONDEFINE_FUNCTION 宏来提供thunk。
Exec 此函数可从游戏内控制台执行。仅在特定类中声明时,Exec命令才有效。
NetMulticast 此函数将在服务器上本地执行,也将复制到所有客户端上,无论该Actor的 NetOwner 为何。
Reliable 此函数将通过网络复制,并且一定会到达,即使出现带宽或网络错误。仅在与ClientServer配合使用时才有效。
SealedEvent 无法在子类中覆盖此函数。SealedEvent关键词只能用于事件。对于非事件函数,请将它们声明为staticfinal,以密封它们。
ServiceRequest 此函数为RPC(远程过程调用)服务请求。这意味着 NetMulticastReliable
ServiceResponse 此函数为RPC服务响应。这意味着 NetMulticastReliable
Server 此函数仅在服务器上执行。用于声明名称与主函数相同的附加函数,但是末尾添加了 _Implementation,是写入代码的位置。必要时,此自动生成的代码将调用 _Implementation 方法。
Unreliable 此函数将通过网络复制,但是可能会因带宽限制或网络错误而失败。仅在与ClientServer配合使用时才有效。
WithValidation 用于声明名称与主函数相同的附加函数,但是末尾需要添加_Validate。此函数使用相同的参数,但是会返回bool,以指示是否应继续调用主函数。

UProperty

AdvancedDisplay 属性将被放置在其出现的任意面板的高级(下拉)部分中。
AssetRegistrySearchable AssetRegistrySearchable 说明符说明此属性与其值将被自动添加到将此包含为成员变量的所有资源类实例的资源注册表。不可在结构体属性或参数上使用。
BlueprintAssignable 只能与组播委托共用。公开属性在蓝图中指定。
BlueprintAuthorityOnly 此属性必须为一个组播委托。在蓝图中,其只接受带 BlueprintAuthorityOnly 标签的事件。
BlueprintCallable 仅用于组播委托。应公开属性在蓝图代码中调用。
BlueprintGetter=GetterFunctionName 此属性指定一个自定义存取器函数。如此属性不带 BlueprintSetterBlueprintReadWrite 标签,则其为隐式 BlueprintReadOnly
BlueprintReadOnly 此属性可由蓝图读取,但不能被修改。此说明符与 BlueprintReadWrite 说明符不兼容。
BlueprintReadWrite 可从蓝图读取或写入此属性。此说明符与 BlueprintReadOnly 说明符不兼容。
BlueprintSetter=SetterFunctionName 此属性拥有一个自定义编译函数,被隐式标记为 BlueprintReadWrite。注意:必须对变异函数进行命名,并为相同类的一部分。
Category="TopCategory|SubCategory|..." 指定在蓝图编辑工具中显示时的属性类别。使用 | 运算符定义嵌套类目。
Config 此属性将被设为可配置。当前值可被存入与类相关的 .ini 文件中,创建后将被加载。无法在默认属性中给定一个值。暗示为 BlueprintReadOnly
DuplicateTransient 说明在任意类型的复制中(复制/粘贴、二进制复制等),属性的值应被重设为类默认值。
EditAnywhere 说明此属性可通过属性窗口在原型和实例上进行编辑。此说明符与所有“可见”说明符均不兼容。
EditDefaultsOnly 说明此属性可通过属性窗口进行编辑,但只能在原型上进行。此说明符与所有“可见”说明符均不兼容。
EditFixedSize 只适用于动态数组。这能防止用户通过虚幻编辑器属性窗口修改数组长度。
EditInline 允许用户在虚幻编辑器的属性查看器中编辑此属性所引用的Object的属性(只适用于Object引用,包括Object引用的数组)。
EditInstanceOnly 说明此属性可通过属性窗口进行编辑,但只能在实例上进行,不能在原型上进行。此说明符与所有“可见”说明符均不兼容。
Export 只适用于Object属性(或Object数组)。说明Object被复制时(例如复制/粘贴操作)指定到此属性的Object应整体导出为一个子Object块,而非只是输出Object引用本身。
GlobalConfig 工作原理与 Config 相似,不同点是无法在子类中进行覆盖。无法在默认属性中对其给定一个值。暗示为 BlueprintReadOnly
Instanced 仅限Object(UCLASS)属性。此类的一个实例创建时,其将被给定一个Object的特殊副本,指定到默认项中的此属性。用于实例化类默认属性中定义的子Object。暗示为 EditInlineExport
Interp 说明值可随时间由Sequencer中的一个轨道驱动。
Localized 此属性的值将拥有一个定义的本地化值。多用于字符串。暗示为 ReadOnly
Native 属性为本地:C++代码负责对其进行序列化并公开到垃圾回收
NoClear 阻止从编辑器将此Object引用设为空。隐藏编辑器中的清除(和浏览)按钮。
NoExport 只适用于本地类。此属性不应包含在自动生成的类声明中。
NonPIEDuplicateTransient 属性将在复制中被重设为默认值,除非其被复制用于PIE会话。
NonTransactional 说明对此属性值的修改不会包含在编辑器的撤销/重新执行历史中。
NotReplicated 跳过复制。这只会应用到服务请求函数中的结构体成员和参数。
Replicated 属性应随网络进行复制。
ReplicatedUsing=FunctionName ReplicatedUsing 说明符指定一个回调函数,其在属性通过网络更新时执行。
RepRetry 只适用于结构体属性。如果此属性未能完全发送(举例而言:Object引用尚无法通过网络进行序列化),则重新尝试对其的复制。对简单引用而言,这是默认选择;但对结构体而言,这会产生带宽开销,并非优选项。因此在指定此标签之前其均为禁用状态。
SaveGame 此说明符可简便地将域显式包含,用于属性关卡中的检查点/保存系统。应在作为游戏存档一部分的所有域上设置此标签,并使用代理归档器对其进行读写。
SerializeText 本地属性应被序列化为文本(ImportTextExportText)。
SkipSerialization 此属性不会被序列化,但仍能导出为一个文本格式(例如用于复制/粘贴操作)。
SimpleDisplay 出现在 细节 面板中的可见或可编辑属性,无需打开“高级”部分即可见。
TextExportTransient 此属性将不会导出为一个文本格式(因此其无法用于复制/粘贴操作)。
Transient 属性为临时,意味着其无法被保存或加载。以此方法标记的属性将在加载时被零填充。
VisibleAnywhere 说明此属性在所有属性窗口中可见,但无法被编辑。此说明符与“Edit”说明符不兼容。
VisibleDefaultsOnly 说明此属性只在原型的属性窗口中可见,无法被编辑。此说明符与所有“Edit”说明符均不兼容。
VisibleInstanceOnly 说明此属性只在实例的属性窗口中可见(在原型属性窗口中不可见),无法被编辑。此说明符与所有“Edit”说明符均不兼容。

PUERTS

Sequence

ts和js有什么区别

函数压栈的过程

四元数和欧拉角

如何判断在凸多边形的内部还是外部

如何做一个反弹的手榴弹

成就系统怎么做

光照与阴影

材质系统

特效

动画系统

程序化生成

【UE5】给Landscape加一点料-地形篇(零)-前前言 - 徐凯鸣的文章 - 知乎

Procedural Landmass Generation Youtube

地形编辑

Houdini

自定义编辑器FAssetEditorToolkit

3.1 InitCustomAssetEditor方法:

在此方法中,主要分为两个内容:
① 初始化编辑器布局,可以使用以下步骤:

  1. 通过创建适当类的实例并设置其属性来定义自定义 FLayout、菜单和工具栏。
  2. 自定义 Slate 窗口现在将绑定到 TabManager,并使用自定义 FLayout、菜单和工具栏显示资产编辑器。

② 调用父类方法InitAssetEditor将自定义 Slate 窗口绑定到 TabManager。

3.2 RegistorTabSpawners/UnRegistorTabSpawner方法:

+ 我们首先需要知道,Tab(SDockTab)是引擎定义的一个控件类,每一个Tab都是可以被关闭或打开的SWidget.Tab类型的窗口右上角包含一个关闭按钮。窗口内则可以包含自定义显示的各种Slate内容。

+ 我们打开编辑器时,就会根据Layout布局加载对应的Tab控件,并显示其中的内容,即Tab装入的子控件(Child Slate)。

+ 在此方法中,最重要的是调用TabManager对象,TabManager类主要用于管理编辑器中的布局,创建Tab对象。在类中我们需要定义一个FName类型的TabID变量,用于在该方法中指示一个Slate窗口。我们需要通过调用TabManager的方法RegisterTabSpawner绑定TabID和Slate,用于后续定义Slate控件的显示。

3.3 SpawnTab_CustomWidget方法

在此方法中,将会创建并返回一个SWidget实例。
而RegistorTabSpawner方法通过调用TabManager的接口,将该Slate控件实例注册,与TabID进行绑定,并能够在InitCustomAssetEditor方法中添加到布局Flayout中被显示出来。
我们不仅可以创建简单的Slate控件(如SButton、IDetailsViews),也可以创建较为复杂的窗口,如SEditorViewport(3D预览窗口)、SGraphEditor(可以连接复杂自定义的节点图表系统)等内容,并将其添加到我们的自定义编辑器中。
在本文实例中,我在该方法中创建了一个最简单的Slate类SButton。

模拟云

Worley Noise

在空间中随机随便放点,然后计算最近的点的距离,为了连续,把这个的点放八个地方

虚幻渲染编程

虚幻4渲染编程专题概述及目录 - YivanLee的文章 - 知乎

GAS

【Unreal】虚幻GAS系统快速入门 - LunarMaxim的文章 - 知乎

GAS Document中文翻译

包括什么东西

GAS主要包含以下内容:

  • Who:ASC(Ability System Component) 主要组件,由C++编写,代码里有很多方法是蓝图未实现的。
  • How:GA(Gameplay Abilities) 角色的技能,包括攻击、疾跑、施法、翻滚、使用道具等,但不包括基础移动和UI
  • What:AS(Attribute Set) 角色身上可以用float表示的属性,如生命值、体力值、魔力值等,有BaseValue和CurrentValue,方便回滚
  • Change:GE(Gameplay Effects) 用于修改属性,如增加50移动速度10s;还能配合GA实现更多玩法
  • Visual:GC(Gameplay Cues) 播放特效、音效等
  • If:Tag:用来分类,底层是FName,用.来分隔
  • Async:Task:如果想要异步的做一些东西,比如做完技能播一个蒙太奇,可以用这个来搞
  • Send:Event:ASC之间通信

做冷却

角色在启动的时候可以保存一个specAbility,然后拿一个他的handle。

比如我想实现一个技能的冷却,其实就是做一个GE,然后这个GE启动的时候挂上在人物身上挂一个cooldown的tag,这个tag是有一个duration的,然后释放技能的时候加一个条件,有这个tag的时候无法释放,在ASC上面有一个Activation Blocked Tags,在里面加上刚刚搞的那个tag

多线程

【UE·引擎篇】Runnable、TaskGraph、AsyncTask、Async多线程开发指南 - 水曜日鸡的文章 - 知乎

《Exploring in UE4》多线程机制详解[原理分析] - Jerish的文章 - 知乎

FRunnable

创建一个继承自FRunnable的类,把这个类要执行的任务分发给其他线程去执行。

img

AsyncTask系统

TaskGraph系统

Task Graph 系统是UE4一套抽象的异步任务处理系统,可以创建多个多线程任务,指定各个任务之间的依赖关系,按照该关系来依次处理任务。具体的实现方式网上也有很多案例,这里先给出UE4Wiki的教程链接:

网络同步

《Exploring in UE4》关于网络同步的理解与思考[概念理解] - Jerish的文章 - 知乎
https://zhuanlan.zhihu.com/p/34721113

RPC

要将一个函数声明为 RPC,您只需将 ServerClientNetMulticast 关键字添加到 UFUNCTION 声明。

1
2
UFUNCTION( Client )
void ClientRPCFunction();

Root Motion

《Exploring in UE4》RootMotion详解【原理分析】 - Jerish的文章 - 知乎
https://zhuanlan.zhihu.com/p/74554876

DS 专用服务器

网络概述官方文档

优化

【[UOD2022]虚幻DS的机遇与挑战 | 晶核 刘豪

NetworkPrediction

属性同步

image-20241220222047918

等级低只同步自己

把宠物,npc全放在逻辑服,不用同步

大世界网格小,副本网格范围大

摄像机背后的actor同步频率低

不移动的时候降低同步频率

把不需要同步的属性清理掉。

使用pushModel,手动标记哪些属性需要更新,这样就不需要同步所有的信息

物理计算优化

服务器剔除角色身上所有的装饰器组件

服务器关闭动画计算,只有业务需要具体坐标时才计算。


面经-UE相关
https://rorschachandbat.github.io/找工作/面经-UE相关/
作者
R
发布于
2024年12月14日
许可协议