我目前正在开发基于体素的游戏,到目前为止,我通过使用Simplex Noise创造了我的世界。现在,我想生成一些其他结构,如河流,城市和其他东西,由于我将世界(实际上是无限的)分割成64x128x64的块,因此不容易生成。我已经生成了树(叶子可以长成相邻的块),方法是为一个块生成树,再为周围的8个块生成树,因此叶子不会丢失。但是,如果我进入更高的维度可能会遇到困难,那么当我不得不计算一个块时,要考虑半径为其他16个块的块。
有没有办法做得更好?
取决于所生成结构的所需复杂度,您可能会发现首先在单独的数组中生成它很有用,甚至可能是地图(位置对内容字典,在稀疏度较高的情况下也有用),然后进行传输世界的结构?
至于自然土地特征,您可能想问一下谷歌如何在景观生成中使用分形。
我在书上读到了一些有关这些的内容,在这种情况下,他们所做的是根据应用程序对块进行更好的划分,即:如果您要种植非常大的对象,那么将另一个对象分开可能会很有用。仅用于此特定应用的逻辑分割,例如128x128x128。
本质上,数据驻留在同一位置,您只是使用不同的逻辑划分。
说实话,从来没有做过任何体素,所以不要把我的答案太认真,只是提出想法。顺便说一下,这本书是游戏引擎宝石1,它们在体素引擎上有宝石。
关于河流,难道您不只是设定水位并让河流在山腰山梯中自动生成吗?为避免将水倒入山区告密者,您可以进行射线探测,以检查是否有N阻挡。
我知道这个线程很旧,我很会解释,但是我会分享我的方法。
例如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蠕虫来解决问题),但是这几乎是相同的想法,对于您的城市也是如此。