我现在正在开发一款基于体素的游戏,到目前为止我使用Simplex Noise来生成我的世界。现在我想生成一些其他结构,如河流、城市和其他东西,这些不容易生成,因为我把我的世界(实际上是无限的)分成64x128x64的块。我已经生成了树(叶子可以生长到邻近的块中),通过为一个块生成树,加上周围8个块的树,所以叶子不会丢失。但是如果我进入更高的维度,当我必须计算一个块时,考虑到16个其他块的半径,这就变得困难了。
有更好的方法吗?
根据所生成结构的期望复杂性,您可能会发现首先在一个单独的数组中生成它是有用的,甚至可能是一个映射(一个位置到内容的字典,在高度稀疏的情况下很有用),然后将结构转移到世界?
对于自然地貌,你可以谷歌一下分形是如何在景观生成中使用的
我知道这个帖子很老了,我不擅长解释,但我将分享我的方法。
例如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'在结构体内循环。这就是它应该如何工作的。
关于河流,我个人还没有这样做过,我不确定为什么你需要在生成它们时将块设置在块周围(你可以使用柏林蠕虫,它会解决问题),但这几乎是相同的想法,对于你的城市也是如此。
我在一本书上读到过这方面的内容,在这种情况下,他们所做的是根据应用程序对块进行更精细的划分,即:如果你要增长非常大的对象,那么对于这个特定的应用程序,有另一个分离的逻辑划分可能是有用的,例如,128x128x128。
本质上,数据驻留在同一个位置,只是使用了不同的逻辑划分。
老实说,从来没有做过任何体素,所以不要把我的回答太当真,只是抛出想法。顺便说一下,这本书是关于游戏引擎的宝石1,他们有一个关于体素引擎的宝石。
关于河流,你不能设定一个水位,让河流在山间的梯子中自动生成吗?为了避免在山中放置水,你可以执行光线投射来检查它是否有N个块。