从 Unreal Engine 中移除多人游戏功能以节省内存
Larst Of Us
如何通过“删除引擎中的多人游戏功能”来节省内存
尽管本文的标题可能暗示了相反的观点,但我并不讨厌 Unreal 的多人游戏功能。作为在 Unreal 中开发过两个不同多人游戏的人,我很喜欢 Unreal 无缝的多人游戏集成。它被深入构建到引擎中,而不是像许多其他功能一样被移动到可选插件中。您可以在不编写任何代码的情况下以多人游戏模式测试任何项目。当然,如果没有对游戏结构进行许多相应的更改,它将无法正常工作,但即便如此:多人游戏已融入到引擎的 DNA 中。
与必须先安装和激活额外的插件和库才能开始处理多人游戏功能的 Unity 相比,这有很多优点,并且使多人游戏开发更加容易。
但是,这种方法肯定有一些缺点,对吧?如果您正在开发单人游戏呢?拥有所有这些功能有什么缺点吗?
在大多数情况下,没有。未使用的代码/功能不会影响游戏的性能(至少不会以可衡量的方式影响)。但是有一些小的例外。其中之一是需要在 Unreal 的基类中存储的用于多人游戏工作所需的数据。主要是简单的变量,例如 AActor 类中的“bReplicates”,它定义了是否应通过网络复制 actor。
即使在开发单人游戏时,仍然需要在类的每个实例中存储此数据,它仍然未使用。但到目前为止,这似乎主要是一种理论上的优化,在实际项目中不值得这样做。此变量甚至作为位掩码的一部分实现,因此我们甚至不会节省单个字节,它只是一个未使用的单个位。为了节省几个字节的内存而对正在运行的代码库进行任何更改?不了,谢谢。
至少在我向一些学生展示 Visual Studio 的内存布局可视化工具,并以 AActor 类为例之前,我一直是这么认为的。此工具显示任何类的内存布局,如下所示:
直到现在,我才偶然发现了某些仅限多人游戏的数据的实际大小:
是的,为了通过网络复制组件附件和 actor 的移动,Unreal 使用了一些自定义结构,它们分别为 120 字节和 216 字节。突然,从引擎中删除某些多人游戏功能以节省内存的想法听起来不再那么糟糕了。如果我能找到更多类似这样的数据,它实际上可能会产生可衡量的差异。
浏览类中的所有数据后,我发现越来越多的变量在单人游戏中将完全未使用。诸如 NetUpdateFrequency 和 MinNetUpdateFrequency(每个 4 字节)、PhysicsReplicationMode(1 字节)或“ReplicatedComponents”列表(16 字节)之类的东西。浏览完整列表并进行计算后,我获得了 328 字节的潜在内存减少量。这将 actor 的大小从 1088 字节减少到 760 字节(编辑器构建)/从 840 字节减少到 528 字节(独立构建)。是的,由于对齐偏移,两种构建配置之间的差异并不完全相同,但我不想在所有更改之上开始移动内容。
现在我们开始讨论所有这些内存节省的缺点:以干净的方式进行操作并不容易。我的第一个想法是通过预处理器宏排除所有这些成员,以便我可以在多人游戏和单人游戏构建配置之间来回切换:
当我尝试时,我真的很喜欢这种方法,尤其是因为它与 Unreal 已经用于从专用服务器构建中编译 UI 内容的类似宏(如 UE_SERVER)保持一致。
问题:Unreal Header Tool (UHT) 会解析代码来处理 UPROPERTY 的所有反射,如果您根据某些预处理器宏排除或包含 UPROPERTY,它不喜欢这样做,并且会在您尝试这样做时中止其工作。这绝对并非不可能,例如,WITH_EDITORONLY_DATA 宏正是这样做的,但我可能需要修改 UHT 才能做到这一点——我认为这超出了我的实验范围。
(如果您对 UHT 更熟悉并且知道如何处理此类预处理器宏,请随时给我留言或发表评论。我很乐意对此想法进行跟进。)
因此,我不得不注释掉有问题的变量以及使用它们的所有代码。这是一项令人麻木但简单的任务。如果您计划尝试这种优化,我建议您在开始此阶段之前喝一大杯咖啡,这将需要一段时间。如果您在实际的生产环境中这样做并且必须记录所有引擎更改,则需要加倍。
在修改了 58 个引擎文件后,我的编辑器终于再次启动,令我非常欣慰的是,一切都按预期工作。
节省了多少?
我已经告诉过您,此方法每个 actor 可节省 328 字节,乍一看并不多。您可以对 SceneComponent 应用相同的技巧,每个 SceneComponent 可以再节省 32 字节。假设每个 actor 平均有两个 SceneComponent,则每个 actor 最多可节省 392 字节。除非您处理大量 actor,否则这仍然不是一个令人印象深刻的数字。一个假设的级别,包含 25 000 个 actor(很多,但并非不合理),将节省大约 10 MB。
值得吗?
对于大多数项目,我认为答案是否定的。有很多很多其他方法可以节省更多显着的内存量,您应该首先追求这些方法。但与许多其他可能的优化一样,最好拥有此选项而不是没有。如果您的项目由于某种原因需要大量的 actor(> 100 000),并且您无法使用其他方法(如 actor 合并或 Instanced Actors)来减少此数量,那么此优化可能比其他优化更有趣。我至少在一个实际项目中工作过,这种优化可以节省 100 MB。
如果这个实验对您来说很有趣,请考虑在其他平台上关注我,例如 Mastodon, Bluesky 或者 LinkedIn,以获取更多关于 Unreal、游戏优化或其他游戏开发主题的文章🙂 您也可以直接订阅此博客在本文下方,随时了解未来的帖子。