发布日期:2023-09-27 作者: 小九体育直播在线观看
在我学习声音信号处理的时候,我的大脑很自然地联想到了地图生成。这篇博客记录了关于信号处理的一些概念与地图生成相关的东西。这些知识点不是一些新的东西,但对我来说,是以前从未接触过的,所以我想记录一下,并且分享给大家。这篇博客会覆盖一些简单的主题,频率、振幅、噪声的种类、噪声的应用等。涉及到的数学部分,基本上只有正弦波形。
我们在程序化的地图生成中要做的是生成一组输出,其中有一些东西是相同的,而有一些东西是不同的。例如,在我的世界这样的游戏中,所有的地图都有很多相似性。生物群落,方块大小,生物群落的平均大小,洞穴的平均高度,不同石头所占的比例等等。但是,也有一些不同的地的:群落的位置,黄金的位置,洞穴的大小等等。作为游戏的设计者,需要决定哪些部分需要是相同的,哪些部分需要是不同的。
对于不同的部分,通常是使用随机数生成器。让我们来做一个极其简单的地图生成器:它将包含 20 个格子,其中某些格子将包含宝箱。结果如下
请注意这一个地图有多少共同的地方:首先它都是由格子组成(每个点作为一个格子),每行有 20 个格子,然后有两种类型的块,一个是空白,一个是宝箱。
但有一点是不同的,哪一个格子是什么类型,也就是说宝箱也许会出现在从 0 到 19 的任何一个格子。
我们能够正常的使用随机数来选择将宝箱放在哪一个格子中。最简单的方式是选择从 0 到 19 的随机数。这在某种程度上预示着每一个格子都可能被选择。大部分的编程语言都包含随机数生成函数。在 Python 中,使用方式是random.randint(0, 19)。完整代码如下
假设我们想让地图中的宝箱有更多的可能性出现在左边,这时就要使用非均匀随机数选择了。有很多办法能够完成这件事情,其中一种方式是首先选择一个随机数,然后将它向左移动,例如,使用函数random(0,19)/2,下面是 Python 代码
然后,如果我们想让宝箱更多地出现在左边,但是,右边也不能一个没有,应该怎么办呢?一个方式是使用平方数,也就是先选定一个随机数,然后计算它的平方,然后再用结果除以 19(地图右边界索引),得到的结果向下取整。下面是代码和效果
还有一种方法是使用两次随机。第一次先随机选择一个随机数,然后使用得到的这个随机数作为第二次随机的右边界。这样的结果就是假设我们第一次得到的随机数是 19,那么最终宝箱的结果也许会出现在地图的任何地方。但是,假设第一次得到的随机数是 10,那么宝藏的位置,就有更多的可能性偏向左边。代码和效果如下
有很多方式能将均匀随机数变为非均匀随机数。作为游戏设计者,我们应该根据自己的需求去选择正真适合的方式。
在以前老式的电视,如果一个电台没信号,那么屏幕上就会显示很多黑白的噪点。对于收音机来说,假如没有信息,我们听到的可能就哧哧哧的声音,那也是噪声。
对于信号处理领域,噪声可理解为干扰正常信号的那些东西。例如在一个房间里有很多人说话,你很想听到特定的一个人在说什么,但是听不清,因为有其他人也在说话,那就是噪声。对于声音处理来说,这种噪声是分布在一条线D 的。而在图片处理中,一个图片因为模糊或者很多噪点而看不清原本想要的画面,这种噪声是在一个面上,是一个 2D 的。当然,也可以有分布于 3D,4D 的噪声。
对于很多应用来说,我们尝试减少噪声。但对于自然界来说,很多东西看起来并不是纯粹的,而是充满了一部分噪声。所以,如果想让程序化生成的东西看起来更自然一些,我们应该添加噪声。噪声可以使程序化生成的东西看起来不一样,同时又具有一个基本的可识别结构。这个我们要根据具体的需要来看。
让我们来看一个使用噪声的例子。在之前,我们是在 1D 的地力上生成单个宝箱,现在我们想创造一个 2D 地图,由山谷,丘陵,和山脉组成。这里首先为每一个位置使用一个均匀的随机数选择,也就是random(1,3)。我们定义 1 为山谷,2 为丘陵,3 为山脉。整个 2D 地图是存放在一个数组中,而每一个格子,存储了生成的随机数。下面是 Python 代码
上面的结果看起来太随机了,有时候我们想要不那么均匀的随机,例如,山脉比丘陵更多一点,这样的一种情况,就得使用非均匀随机
上面的非均匀随机对我们想要的地图结果来说,并没什么用,因为上面的随机是针对每一个位置,完全独立的随机,而我们想要的,是需要和周围的格子有一定关联的随机。
现在,该是噪声函数出场的时刻了。噪声函数生成的结果是一个序列,而不是随机函数那样,每次生成的是一个值。噪声函数的定义方式有很多,让我们先试一下最小值函数,也就是取邻近值中最小的那个值。假设原即噪声值数组为(1,5,2)。遍历原即数组,两两比较,取其中最小的时候,也就是先比较(1,5),取 1,然后比较(5,2),取 2,最终得到的序列为(1,2)。注意,这个噪声函数得到的最终序列元素个数,一定是比原即序列少 1 个的。将这个噪声函数应用于地图生成,代码如下
对比之前的生成结果,这一个地图上的山谷、丘陵或山脉面积更大。山脉通常在丘陵附近。由于个人会使用的噪声函数是最小值,所以山谷比山脉更常见。如果我们采用最大值,则山脉就会比山谷更常见。如果我们想让二者出现次数相当,能够使用平均数,而不是最小或最大值。上面的代码,每一次运行,都将生成不同的结果。
在程序化生成的过程中,通常做的就是尝试一下,看看结果是否 OK,如果不 OK,那就改一下代码,再尝试一下。
选择一个噪声函数有时候靠的是猜测,而了解噪声函数的原理以及如何修改,意味着能做出更有根据的猜测。
在之前的内容中,个人会使用随机数作为输出,然后对其进行平滑。这一种常见的模式。对于一个随机函数,使用一组随机数作为参数,然后使用另一个随机数,来控制我们想要的内容,例如哪里是宝箱,哪里是山脉,哪里是山丘等等。
使用随机数作为梯度的参数,这些参数用于输出。这在 Simplex/Perlin 噪声中使用。
应用一个过滤器来减少或放大某个特征。例如之前我们用平滑法来减少凹凸感,增加山谷的大小,并使山峰出现在山谷附近。
使用两个噪声函数来产生输出,通常会给每一个噪声函数赋予一个权重,以此来控制噪声函数对于输出结果的影响。
有很多方式能创造噪声。从某一些程度上来说,噪声怎么样产生,并不是很重要,但是将噪声用于游戏中,要关注两件事情
使用噪声最直接的方式是作为海拔高度。在之前的例子中,个人会使用random(1,3)来产生 1、2、3 这三个数,分别对应山谷、山丘、山脉。这是直接使用。
使用中点位移噪声或 Simplex/Perlin 噪声作为海拔,也是直接使用。
另一种使用噪声的方式是应用于移动中,简单来说就是下一步的移动,取决于上一步。例如一个噪声函数输出的值是 [2, -1, 5] 这可以表示为初始位置为 2,然后移动,2 + -1 = 1,然后再移动 1 + 5 = 6,也就是最终走到了 6 的位置。
或者你要用它来做一个形状。例如,你可以在极坐标图中使用噪声作为半径。你可以把这样的一维噪声函数转换成极坐标形式,把输出作为半径而不是作为海拔。下面是同样的函数在极地形式下的样子。
或者你可能使用噪声作为图形纹理。Simplex/Perlin 噪声经常被用于此。
你可以用噪音来选择物体的位置,如树木或金矿或火山或地震断层线。在前面的例子中,我用一个随机数来选择宝箱的位置。
你可以用噪声作为阈值函数。例如,你可以说,任何一个时间里数值大于 3,那么就会发生一件事,否则就会发生其他事情。这方面的一个例子是使用 3D Simplex/Perlin 噪声来生成洞穴。你可以说,任何高于某个密度阈值的东西都是不可行走的区域,任何低于该阈值的东西都是开放的空间(洞穴)。
频率是噪声的一个主要属性,最简单理解方式是直接看图,例如下面是正弦波的三种不同的频率,低频率,中频率,高频率的波形。
从上面三种不同的频率能看出,低频率会使起伏更平缓,范围更大,则高频率会使起伏更尖锐。频率描述的是横向的属性。而振幅描述的是纵向的属性。在之前的例子中,山丘,山谷,山脉,看起来太过于『随机』了,意思就是我们应该更低一点的随机频率,来降低这种随机性,使其更加的缓和。
如果个人会使用一个连续的噪声生成函数,增加频率,意味着输入参数的变化。而增加振幅,则意味着是对噪声函数产生的结果进行增量改变。假设噪声函数就是 sin(x)。增加频率,也就是sin(x * 2)则是两倍的频率。而增加振幅,则是2 * sin(x)。
对于上面的图来说,改变的是频率,增大频率,意味着一个一个的波峰数量,频率越高,则两个波峰越多,两个波峰之间的距离越小。
对于上面的图来说,改变的是振幅,增加振幅,不会增加波峰的数量 ,而会改变波峰的高度。
对于正弦波,我们还能够正常的使用各种奇怪的组合,以此产生不同的效果。例如下面的这个例子,就是左边的频率更低一点,而右边的频率更高一点
通常来说,在同一时刻,能够正常的使用多种不同频率的噪声,没什么标准的规则,主要是看自己的需求,想要什么样的东西,然后再看怎么使用噪声。
不同种类的噪声拥有不同的频率。对于白噪声来说,所有的频率对于结果的贡献度,也能说是权重,是一样的。例如我们前面的例子,使用 1,2,3 来代表山脉,山谷,丘陵。下面是条白噪声序列
在红色噪声,也称为布朗噪声中,低频会更加的突出。也就是说,会有更长的山谷和山丘。我们大家可以通过平均两个相邻随机数的形式,将白噪声变为布朗噪声。
在频谱的另一边,是蓝色噪声,高频会更突出。我们大家可以使用白噪声中两个随机数之差,来产生蓝色噪声。
蓝色噪声能够适用于地图中放置物体,它产生的结果不会很密集或者很稀疏,物体在地图中的分布整体比较均匀。
上面的内容我们已了解了怎么样产生白噪声,布朗噪声,蓝色噪声,后面我们还会一起探究更多类型的噪声。
在之前的内容中我们不难发现了不一样的噪声的特点。另一种生成噪声的方式是组合不同频率的噪声。例如,假设我们有一个噪声函数noise,会在特定的频率freq生成噪声。如果你想让 1000Hz 的噪声比 2000Hz 的噪声强一倍,并没其他频率的噪声,那么我们大家可以这样来生成noise(1000) + 0.5 * noise(2000)
正弦函数看起来并不是很嘈杂,但是这个函数很容易给定一个频率,所以我先从正弦开始,然后一步一步往前走。
上面的结果,是把一个基础的正弦波使用一个随机量来控制其向侧面位移。这里唯一的随机性,是将它移的有多远。
接下来我们尝试将 8 个噪声函数加在一起,频率分别是 1,2,4,8,16,32。每一个噪声函数将乘以一个因子,能够理解为不同频率的权重。
另外,做一下广告,我自己开发的独立游戏《无量钓海》,欢迎各位试玩,在 TapTap,好游快爆,AppStore 可以下载