您想找什么?
Engine & platform

可以与 Tilemaps 一起使用的程序模式,第 1 部分

ETHAN BRUINS / UNITY TECHNOLOGIES Contributor
May 29, 2018|10 Min
可以与 Tilemaps 一起使用的程序模式,第 1 部分
为方便起见,此网页已进行机器翻译。我们无法保证翻译内容的准确性或可靠性。如果您对翻译内容的准确性有疑问,请参阅此网页的官方英文版本。

许多创作者使用程序生成来为他们的游戏增添一些多样性。一些值得一提的游戏包括 Minecraft,以及最近的 Enter the GungeonDescenders。这篇文章介绍了一些可以与 Tilemap(在 Unity 2017.2 中作为 2D 功能引入)和 RuleTile一起使用的算法。

通过程序创建的地图,您可以确保您的游戏中没有两次玩法是相同的。您可以使用各种输入,例如时间或玩家的当前级别,以确保游戏构建完成后内容也会动态变化。

这篇博文是关于什么的?

我们将了解创建程序世界的一些最常用方法,以及我创建的一些自定义变体。这是您阅读完本文后可以创建的一个例子。三种算法共同协作创建一张地图,使用 TilemapRuleTile

程序世界-简介
程序世界-简介

当我们使用任何算法生成地图时,我们将收到一个包含所有新数据的 int 数组。然后我们可以获取这些数据并继续修改它或将其渲染为图块地图。

在继续阅读之前,您需要了解以下信息:

我们通过二进制来区分什么是图块,什么不是图块。1 表示开启,0 表示关闭。

我们将把所有地图存储到二维整数数组中,该数组在每个函数结束时返回给用户(渲染时除外)。

我将使用数组函数 GetUpperBound() 来获取每个地图的高度和宽度,这样每个函数中进入的变量就更少,代码也更简洁。

我经常使用 Mathf.FloorToInt(),这是因为 Tilemap 坐标系从左下角开始,使用 Mathf.FloorToInt() 允许我们将数字四舍五入为整数。

本博文中提供的所有代码均为 C# 代码。

生成数组

GenerateArray 创建一个给定大小的新 int 数组。我们还可以说出数组应该是满的还是空的(1 或 0)。代码如下:

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

渲染图

此函数用于将我们的地图渲染为 Tilemap。我们循环遍历地图的宽度和高度,仅当数组在我们正在检查的位置有 1 时才放置图块。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

更新地图

该函数仅用于更新地图,而不是再次渲染。这样,我们可以使用更少的资源,因为我们不需要重新绘制每一个图块及其图块数据。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

柏林噪声

Perlin 噪声 有多种用途。我们使用它的第一种方法是为我们的地图创建一个顶层。这很简单,只需使用我们当前的 x 位置和种子即可获得一个新点。

简单的

这一代采用了将 Perlin Noise 实现到级别生成的最简单形式。我们可以使用 Unity 的 Perlin Noise 函数来帮助我们,因此不需要进行复杂的编程。我们还将使用函数 Mathf.FloorToInt()来确保我们的图块地图有整数。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

这是它在瓦片地图上呈现的样子:

地图生成
地图生成
平滑

我们也可以采用这个函数,并使其变得平滑。设置间隔来记录 Perlin 高度,然后在各点之间进行平滑。这个函数最终会稍微先进一些,因为我们必须考虑间隔的整数列表。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

对于此函数的第一部分,我们首先检查间隔是否大于一。如果是,我们就产生噪音。我们每隔一段时间就会进行一次此操作,以保证平滑。下一部分是通过平滑点来进行工作。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

平滑过程通过以下步骤进行:

- 获取当前位置和上一个位置

- 获取两个位置之间的差异,我们想要的关键信息是 y 轴的差异

- 下一步,我们确定应该将命中率改变多少,这是通过将 y 差异除以区间变量来实现的。

现在我们可以开始设置位置了。我们将努力将目标降至零

当我们在 y 轴上达到 0 时,我们会将高度变化添加到当前高度,并对下一个 x 位置重复该过程

一旦我们完成了上一个位置和当前位置之间的每个位置,我们将转到下一个点

如果间隔小于一,我们只需使用前一个函数来为我们完成工作。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

让我们看看它的渲染效果:

平滑
平滑
随机游走
随机游走顶部

该算法的工作方式是抛硬币。然后我们会得到两个结果之一。如果结果是正面,我们就向上移动一个区块;如果结果是反面,我们就向下移动一个区块。通过不断向上或向下移动,这会为我们的水平创造一定的高度。这种算法的唯一缺点是它看起来非常块状。让我们看看它是如何工作的。

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

与 Perlin 噪声生成相比,这一生成为我们提供了更加平滑的高度。

随机游走顶部平滑

与 Perlin 噪声生成相比,这一生成为我们提供了更加平滑的高度。

与之前的版本相比,这种随机游走变化可以实现更加平滑的效果。我们可以通过向函数添加两个新变量来实现这一点:

  • 第一个变量用来确定我们保持当前身高多长时间。这是一个整数,当我们改变高度时它会被重置。
  • 第二个变量是函数的输入,用作高度的最小截面宽度。当你看到函数时,这将更有意义

现在我们知道需要添加什么了。让我们看一下这个函数:

未知的块类型“codeBlock”,请在“serializers.types”属性中为其指定一个序列化器

正如您从下面的 gif 中看到的,随机游走算法的平滑性使得关卡中可以出现一些漂亮的平坦部分。

抛硬币
结论

我希望这可以激励您开始在项目中使用某种形式的程序生成。如果您想 了解有关程序生成地图的更多信息 ,请查看 Procedural Generation Wiki Roguebasin.com,这两个都是很好的资源。

您可以关注该系列的下一篇文章,了解我们如何使用程序生成来创建洞穴系统。

如果您使用程序生成制作了一些很酷的东西,请随时在 Twitter 上给我留言或在下面发表评论!

Unite Berlin 上的 2D 程序生成

想要了解更多信息并获得现场演示吗?我还将于 6 月 20 日在柏林 Unite 展览厅迷你剧院讨论与 Tilemaps 一起使用的程序模式。如果您想亲自聊聊的话,演讲结束后我会在场!