程序结构生成

时间:2015-09-12 22:28:55

标签: voxel procedural-generation

我现在正在开发基于体素的游戏,到目前为止我使用Simplex Noise生成我的世界。现在我想生成一些其他结构,如河流,城市和其他东西,这些都不容易生成,因为我将我的世界(实际上是无限的)分成了64x128x64的块。我已经生成了树(叶子可以长成相邻的块),通过生成一个块的树,加上围绕它的8个块的树,所以叶子不会丢失。但是如果我进入可能变得困难的更高维度,当我必须计算一个块时,考虑半径为16个其他块的块。

有没有办法更好地做到这一点?

3 个答案:

答案 0 :(得分:1)

根据所生成结构的所需复杂性,您可能会发现首先在单独的数组中生成它很有用,甚至可能是映射(位置到内容字典,在高稀疏度的情况下很有用),然后将结构转移到世界?

至于自然土地特征,您可能想要谷歌如何在景观生成中使用分形。

答案 1 :(得分:0)

我在书上读到了一些关于这一点的内容,他们在这些案例中所做的是根据应用程序对块进行更精细的划分,即:如果你要生长非常大的对象,那么另一个可能是有用的例如,128x128x128的逻辑除法,仅适用于此特定应用。

本质上,数据驻留在同一个地方,您只需使用不同的逻辑分区。

说实话,从来没有做任何体素,所以不要把我的答案太严肃,只是抛出想法。顺便说一下,这本书是游戏引擎宝石1,它们在那里有体素引擎上的宝石。

关于河流,你能不能设定一个水位,让河流在山边的山梯中自动生成?为避免将水放入山区警告中,您可以执行光线投射以检查它是否有空挡N.

答案 2 :(得分:0)

我知道这个话题很老,我很会解释,但是我会分享我的方法。

例如5x5x5棵树。您想要的是让噪声函数在5x5块的区域中返回相同的值,这样即使在块之外,您仍然可以检查是否应该生成树。

// Here the returned value is different for every block
float value = simplexNoise(x * frequency, z * frequency) * amplitude; 

// Here it will return the same value for an area of blocks (you should use floorDiv instead of dividing, or you it will get negative coordinates wrong (-3 / 5 should be -1, not 0 like in normal division))
float value = simplexNoise(Math.floorDiv(x, 5) * frequency, Math.floorDiv(z, 5) * frequency) * amplitude;

现在我们将种一棵树。为此,我们需要检查当前块相对于树的起始位置在x y z位置的位置,以便我们知道该块在树的哪一部分。

if(value > 0.8) { // A certain threshold (checking if tree should be generated at this area)
    int startX = Math.floorDiv(x, 5) * 5; // flooring the x value to every 5 units to get the start position
    int startZ = Math.floorDiv(z, 5) * 5; // flooring the z value to every 5 units to get the start position
    // Getting the starting height of the trunk (middle of the tree , that's why I'm adding 2 to the starting x and starting z), which is 1 block over the grass surface
    int startY = height(startX + 2, startZ + 2) + 1;

    int relx = x - startX; // block pos relative to starting position
    int relz = z - startZ;

    for(int j = startY; j < startY + 5; j++) {
        int rely = j - startY;
        byte tile = tree[relx][rely][relz]; // Get the needing block at this part of the tree
        tiles[i][j][k] = tile;
    }
}

这里的树3d阵列几乎就像树的“预制件”,您可以使用它来知道在相对于起点的位置处设置哪个块。 (上帝,我不知道该怎么解释,将英语作为我的第五种语言也无济于事;-;随时改善我的答案或创建一个新的答案)。我已经在引擎中实现了此功能,并且完全可以正常运行。这些结构可以任意大小,而无需预先加载块。这种方法的一个问题是,我们几乎可以在网格内生成树木或结构,但是可以使用具有不同偏移量的多个八度音阶轻松解决此问题。

所以回顾

for (int i = 0; i < 64; i++) {
    for (int k = 0; k < 64; k++) {
        int x = chunkPosToWorldPosX(i); // Get world position
        int z = chunkPosToWorldPosZ(k);

        // Here the returned value is different for every block
        // float value = simplexNoise(x * frequency, z * frequency) * amplitude; 

        // Here it will return the same value for an area of blocks (you should use floorDiv instead of dividing, or you it will get negative coordinates wrong (-3 / 5 should be -1, not 0 like in normal division))
        float value = simplexNoise(Math.floorDiv(x, 5) * frequency, Math.floorDiv(z, 5) * frequency) * amplitude;

        if(value > 0.8) { // A certain threshold (checking if tree should be generated at this area)
            int startX = Math.floorDiv(x, 5) * 5; // flooring the x value to every 5 units to get the start position
            int startZ = Math.floorDiv(z, 5) * 5; // flooring the z value to every 5 units to get the start position
            // Getting the starting height of the trunk (middle of the tree , that's why I'm adding 2 to the starting x and starting z), which is 1 block over the grass surface
            int startY = height(startX + 2, startZ + 2) + 1;

            int relx = x - startX; // block pos relative to starting position
            int relz = z - startZ;

            for(int j = startY; j < startY + 5; j++) {
                int rely = j - startY;
                byte tile = tree[relx][rely][relz]; // Get the needing block at this part of the tree
                tiles[i][j][k] = tile;
            }
        }
    }
}

因此,“ i”和“ k”与块一起循环,而“ j”在结构内部循环。这几乎是应该如何工作的。

关于河流,我个人还没有做过,而且我不确定为什么在生成它们时为什么需要在块周围设置块(您可以只使用perlin蠕虫来解决问题),但这几乎是相同的想法,对于您的城市也是如此。