用自定义植被系统优化Thrive的性能:《Height Lies the Crown》

ADAM AXLER / UNITYSenior Content Marketing Manager
Jun 18, 2025
繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
为方便起见,此网页已进行机器翻译。我们无法保证翻译内容的准确性或可靠性。如果您对翻译内容的准确性有疑问,请参阅此网页的官方英文版本。

Zugalu Entertainment 成立于 2014 年,旨在制作融合创新、怀旧情结和商业吸引力的游戏。在过去的 11 年里,他们发售过许多游戏,包括 Epic Food FightTechnolitesChronique des SilencieuxSovereign Syndicate

2024 年 11 月 6 日,他们发布了《Thrive:《Heavy Lies the Crown》抢先体验版,评价极高。游戏是一个中世纪的城市建设者,具有实时战略元素以及单人和合作Multiplayer功能。玩家可以有策略地扩张自己的领土和王国,然后沿着一张大地图进行建设。随着旅程的推进,王国的命运决定着他们的每一个决定。

今天,团队发布了游戏的1.0版本。我们与Zugalu Entertainment的CTO Garrett Hau和团队的首席概念艺术家和技术美术Jackie Li坐下来讨论了他们遇到的性能挑战,以及构建自定义植被系统是游戏优化的关键。

构建自定义植被系统

游戏背景设在荒野,有很多树木、草地、灌木丛等。在最初的实现中,草地非常稀疏,但团队希望创造一个广阔而茂密的草地。Hau 说:“对于我们的许多不同的生物群系,例如草原生物群系,我们希望覆盖满植被的地面。”要实现这一目标,他们需要能够处理大量植被实例的系统。

在项目的大部分时间里,团队使用的是第三方解决方案,但效果良好,只是它占用了过多的CPU资源,耗时约3毫秒,并且游戏的CPU非常瓶颈。由于他们对系统的要求很低,他们决定将大多数计算转移到 GPU,因此需要另一种解决方案。

“我们决定制作自己的系统,同时融入游戏基于区块的性质。原始系统有自己的工作方式,并且每个区块级别的某些交互成本过高。”Hau 说。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
植被编辑器内预览

他们希望能解决这个问题,让植被更密集。由于他们使用新系统中的 GPU 进行计算,因此拥有了更多的性能空间和机会来创造一个茂密的森林。

另一个关键目标是实现每块遮罩。Hau 说:“以前,在道路下放置时,无法有效地遮挡它,所以植被只会生长在道路顶部。”由于初始方法依赖于 CPU,每增加一个遮罩就会增加一个负担,所以他们希望道路或其他任何东西都能遮罩住草丛或植被,而不消耗大量性能。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
团队图块遮罩编辑器内截图

使用计算着色器执行

团队在大型地图植被的生成上也遇到了一个大瓶颈。由于游戏中有大量植被,并且摄像机很高,可以在地图上快速移动,所以植被的生成不能让玩家停下来。结果发现这非常困难。

“在查看植被时,您会发现可能需要生成成千上万个实例。因此,我们选择了 GPU 实例化,它专为这一目的设计,允许可能有数百万个实例。”Li 说。

首先,团队为GPU实例化准备了数据。他们需要在GPU上构建并馈送一组想要生成植被的位置。植被在瓦片遮罩之外并不会与CPU端互动,因此他们使用计算着色器执行此操作。由于计算着色器在其渲染着色器执行之前在GPU上运行,因此他们在计算着色器中准备数据,然后将结果数据馈送到实例化。这也称为分层间接。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
编辑器内生成的单位截图

下一步是想出计算着色器的使用,事实证明这相对容易。Li解释说:“计算着色器只是GPU上的多线程运算。在我们的示例中,每个线程可以分别计算分层数据。你可以把它看作 Unity 作业系统,而是放在 GPU 上。”

在多线程环境中工作时,每条线程的工作负载应该被精心设计,使它们不依赖于其他线程中的执行,从而最大化性能。

Li 说:“例如,在添加随机性时,我们会使用柏林噪声、Simplex 噪声或哈希函数等。当前被评估表面还被划分成统一的网格,每条线程在每个网格点内工作,这样我们就不必担心在彼此之上生成多个重复的植被。”

由于地形在运行时无法变形,他们在开发开始时检索了这些数据并将其传递给GPU。这样一来,高度数据就可以得到预处理,最明显的是计算每个高度位置的斜率,让植被也能跟着地形轮廓进行定制。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios

整合计算着色器


虽然团队使用的是计算着色器,但他们必须执行许多运算着色器才能获得想要的数据。类似于绘制调用,越少越好。他们希望通过减少一半的派发调用,然后将 CPU 到 GPU 的数据传输组合成一个 API 调用来减少 GPU 命令的数量。

“我们的植被系统由许多不同类型的植被组成,每种植被都需要进行计算调度,”Li解释说。“50 个植被相当于 50 个调度,每个调度都有 n 个线程。”

每条线程会计算分层位置及其他数据,但很有可能一条线程会计算被剔除的位置,比如在遮挡位置或摄像机视锥体之外,这些数据不会被添加到分层数组中用于绘制植被。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios

由于线程可能会加到数组中,也可能不加到数组中,所以我们使用了某种形式的线程安全列表,如数据结构,其中我们将有效值附加到列表中。HLSL 方便地以附加缓冲区的形式提供了此功能。Li 说:“使用附加缓冲区确实有一个小缺点。“我不得不执行额外的 gpu 命令来抓取添加的项目数,并清除该计数,以便重用附加的缓冲区。”

但是,计算着色器提供了一个称为 groupshared 的方便变量,它允许线程间通信。再结合Interlocked函数 , 每次派发都能跟踪全局索引计数器 , 让分层数据紧密打包 , 让间接绘制命令在计算分层位置的同一个派发调用中更新。

在发送 CPU 数据时,团队最初面临着性能损失。他们必须更新不同植被类型的着色器属性,这些属性会逐帧变化。

Li 说:“最初,我是单独发送数据,结果产生了 50 个 SetData() 命令。”“但是,由于底层数据类型相同,我们把所有数据合并到一个缓冲区中,然后为每个植被类型在该缓冲区中提供偏移索引。这样只需要一条SetData()命令。”

该团队保守估计,他们节省了 0.1 毫秒的 CPU 时间,占 0.5 毫秒总数的 20%。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios

将图块数据从CPU发送到GPU

由于贴图非常大,大约有 1000 万个瓦片,团队需要从 CPU 到 GPU 抓取瓦片数据。“尝试每帧向 GPU 发送数百万图块将是非常昂贵的,因为发送的数据非常多。我们需要能够发送刚好足以占据屏幕的数据子集。”Hau 说。

他们使用了 Unity 的作业系统来实现这一点。它为 CPU 端提供了帮助,并提供了一种多线程方式来抓取数据并将其发送到 GPU。Hau解释说:“从数组中抓取数据是一种完美的工作负荷,作业系统可以加快速度。”

每条线程都可以抓取细分用户组的数据,再将其复制到目标数组。同时,他们将原来的 16 位数据转换为计算着色器中使用的打包的 32 位数据。

团队还将 Burst 编译器应用于作业系统数据,以创建优化的代码。“Burst 编译器显著提高了多线程性能。在加上这个属性后,它很快就从一毫秒多变成了不到0.3毫秒。它只增加了一行代码就给人留下了深刻的印象。”Hau 说。

繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios
繁荣:Heavy Lies the Crown | Zugalu Entertainment | Playside Studios

虽然团队在优化方面收获颇丰,但他们也知道渲染所有植被的性能也值得关注。

“虽然我们对性能节约感到兴奋,但过度绘制仍然是我的系统无法解决的问题,”Li 解释道。“我们需要牢记这一点。不过,我们对游戏的表现并不满意。”

要阅读有关Made with Unity项目的更多信息,请访问资源页面