为什么我总是要考虑创建和使用对象池,而不是即时实例化新对象?


25

我已经多次(从最佳实践的角度)了解过这种模式:

内存分配:始终考虑创建和使用对象池,而不是即时实例化新对象。这将有助于减少内存碎片,并减少垃圾收集器的工作。

但是,我不知道这实际上意味着什么。我该如何实施?

例如,我可以实例化一个GameObject使用InstantiateUnity 的方法吗?

Instantiate(prefab, new Vector3(2.0F, 0, 0), Quaternion.identity);

不鼓励这种用法吗?还有什么意思?



谢谢Hellium,我没看过给定的视频(太大),但文字确实帮助我理解了“实例化和销毁行为效率低下,可能会使您的项目变慢”
Muhammad Faizan Khan

1
请注意,尽管此建议很普遍,但并非每款游戏都绝对要求。特别是如果您要制作小型/简短的桌面游戏,果酱提交或原型,则无需费劲地实现池化。在我的浸泡测试中,Unity甚至可以承受比我们认为更好的大规模产卵和破坏。;)但是,如果您正在制作一个漫长的游戏,而又不想让垃圾堆积起来并在以后收集垃圾时造成结结,或者如果您针对的移动平台对性能的影响更为强烈,那么请考虑合并。
DMGregory

谢谢@DMGregory你是对的。您的输入总是有价值的。我们不必担心小型游戏中的对象池化,因为这将需要额外的编码工作。
Muhammad Faizan Khan

1
这是一种非常常见的模式,但是请务必遵循以下规则:“先进行配置,然后进行优化”。优化无关紧要的事情很容易。
Cort Ammon-恢复莫妮卡

Answers:


41

如果您打算实例化同一预制件的许多实例,则绝对应该考虑使用对象池。调用Unity的Instantiate函数是您可以进行的最繁重的方法之一。

对象池是在使用预制实例化预制件之前。它们在实例化后立即停用,仅在需要时才重新激活。虽然这确实会增加内存使用量,但可以避免在游戏过程中实例化的CPU开销。

例如,我目前正在开发一个子弹地狱游戏,该游戏需要在运行时生成数百个子弹。最初,我试图在没有对象池的情况下制作游戏,但这最终导致灾难(小于2 fps)。现在,我在游戏开始之前先存入500枚子弹,然后游戏以惊人的速度(200 fps)惊人地运行。

在某些情况下无法使用对象池。例如,如果您有一个游戏,其中玩家的输入决定了要生成的预制件,那么您可能别无选择,只能使用常规的Instantiate调用。仅当您提前知道将需要哪些对象时,才可以进行对象池化。

塞巴斯蒂安·拉格(Sebastian Lague)的YouTube教程是学习对象池的绝佳资源:https : //youtu.be/LhqP3EghQ-Q


“实例化和销毁行为效率低下,可能会使您的项目变慢”。这是原因吗?因此,这意味着我们必须进行更多编码(例如,激活非活动状态或重新设置子弹位置,以便我们可以再次发射)
Muhammad Faizan Khan

12
值得一提的是分配和垃圾回收是重复实例化和销毁成本的主要贡献者。生成足够的垃圾,最终整个游戏不得不等待垃圾收集器将其全部清除。;)
DMGregory

3
@corsiKa,这将是80年代的最佳时代。Unity可以仅停用有问题的预制件,从而使其系统忽略它。
congusbongus

2
“例如,如果您有一个游戏,其中玩家的输入决定了要生成的预制件,那么您别无选择,只能使用常规的Instantiate调用。” - 并不是的。您可以轻松地在启动(或场景加载)时将用于不同预制件的多个对象池分配给所需的大小。或者,如果有人愿意这样做,则存储多个预制类型的对象池也可能起作用。
伊桑·比尔琳

1
@DMGregory在典型的GC环境中,取消分配是大部分成本;在典型的非GC环境中,分配是大部分成本。对象池对于两者都非常有用,实际上它们实际上只是同一枚硬币的两个侧面。在极端情况下,以前的游戏通常是从一开始就用“分配”的所有对象编写的,而在游戏运行时很少或没有内存分配。您实际上并没有使用没有以某种方式“合并”的任何内容。由于Unity使用了大量的实时“元数据”,因此池化可以提供很多帮助-尽管实现起来也比较棘手。
a安
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.