Minecraft 1.12.2 村の座標決定部分のアルゴリズム
Minecraftの村がどういう座標に出現するのかを調査した。
- 村 – Minecraft Wiki https://minecraft-ja.gamepedia.com/%E6%9D%91
版本
-
- Minecraft 1.12.2
- forgeSrc-1.12.2-14.23.4.2705
来源
类名是反混淆了的Forge MCP的内容。
net.minecraft.world.gen.ChunkGeneratorOverworld
村生成を呼び出しているコンテキスト
net.minecraft.world.gen.structure.MapGenVillage
村の生成クラス
解释
座標決定の中心となるのはMapGenVillage#canSpawnStructureAtCoords(int chunkX, int chunkZ)メソッド。これはチャンク座標X,Zを与えるとそこに構造物が出現できるか否かを返している。このメソッドの定義の時点でほぼ決定的なのは、村は1チャンクに最大で1個までしか生成されないということだ。
让我们以几乎接近Java的伪代码为例。
int distance = 32;
boolean canSpawnStructureAtCoords(int chunkX, int chunkZ)
{
// 生成先座標の確定 (1)
int x1 = chunkX;
int z1 = chunkZ;
// 地域の計算 (2)
int x3 = chunkX;
int z3 = chunkZ;
if (x3 < 0) x3 -= distance - 1;
if (z3 < 0) z3 -= distance - 1;
int regionX = x3 / distance;
int regionZ = z3 / distance;
Random random = getRandom(regionX, regionZ, 10387312);
// 想定座標の確定 (3)
int x2 = regionX * distance + random.nextInt(distance - 8);
int z2 = regionZ * distance + random.nextInt(distance - 8);
if (x1 == x2 && z1 == z2) {
if (村生成可能なバイオームである(x1 * 16 + 8, z1 * 16 + 8)) {
return true;
}
}
return false;
}
对于整体行为的意译可能如下所示:
-
- 确定距离。 默认情况下,距离设定为32,可以在世界设置中进行更改。
-
- 世界将被分割为以distance为边长的正方形区域。
-
- 确定每个区域出现村庄的区块。
- 如果该区块的中心坐标位于合适的生物群落中,则生成村庄。(本文档不涉及生物群落。)
如果要更加意译的话,可以表述如下。
distance x distanceチャンク毎、地図の左上端から(distance - 8)x(distance - 8)チャンクのどこかに、特定のバイオームのとき、村が出現する。
それを「あるチャンクについて、そこに村があるか否か」という形で表現するとこのメソッドのようになる。
-
- 首先,预先分配生成位置的坐标。
-
- 然后,计算区域坐标(xRegion,zRegion)。(在这个过程中会使用一些变量来处理负坐标。)
-
- 接下来,根据区域坐标计算应该生成村庄的位置。
- 如果生成位置和村庄应该生成的位置相同,则可以在那里生成村庄。(前提是生态群系正确。)
随机获取
以下是关于getRandom(int regionX, int regionY, int s)的说明。
このメソッドは実際にはnet.minecraft.world.World#setRandomSeed(int, int, int)であり、ワールドに乱数生成器のシードを設定してその乱数生成器を返すものであるが、ここでは単に引数に従って乱数生成器を返すものとして考える。
long seed;
Random getRandom(int regionX, int regionY, int s)
{
return new Random((long) regionX * 341873128712L + (long) regionY * 132897987541L + seed + (long) s);
}
new Random(long)はシードを与えて乱数生成器を生成する構文である。3個の引数に対して値を乗算して和を取った物を乱数生成器のシードとしている。ここでseedはそのワールドのシード値である。村の生成座標はこの段階でシードのみに依存するものとなる。
在块内的位置
实际上,每个村庄的生成位置都是在区块内固定的,井的角落会出现在(2,2)的位置。这在net.minecraft.world.gen.structure.MapGenVillage.Start#new(World worldIn, Random rand, int x, int z, int size)中有说明。通过改造canSpawnStructureAtCoords,让村庄在所有区块中生成,可以确保井规则地排列(但非常耗资源)。
将地理坐标转换为生成的区块
当到达这一点时,就能够了解在世界的特定地区周围出现的村庄的坐标。这是以下代码的表示方式。
long seed;
int distance = 32;
Point getVillageCoord(int regionX, int regionZ)
{
Random random = new Random(regionX * 341873128712L + regionZ * 132897987541L + seed + 10387312L);
int chunkX = regionX * distance + random.nextInt(distance - 8);
int chunkZ = regionZ * distance + random.nextInt(distance - 8);
return new Point(chunkX * 16 + 2, chunkZ * 16 + 2);
}
ここでrandom.nextInt(distance – 8)という部分に注目してほしい。村は地域のどの位置にも生成可能なわけではなく、地域の+X方向と+Z方向の8チャンクには生成できないことになる。これは恐らく村が重ならないための措置であろう。村の生成座標からワールドのシード逆算を行う場合、デフォルトのdistanceならば村1個あたり24^2の絞り込み性能になることになる。シード値がintの範囲内ならばだいたい村4個分で特定可能である。
从村庄的布局来缩小候选的种子范围确实是相当困难的。发现许多村庄并不容易,而且可能会发生多个种子因某种因素而收敛到相同的布局。
同一个村庄的形成
在村庄生成中,世界的种子通过随机规则分为两个实质部分进行解释。
-
- 上位16ビット 無視される
- 下位48ビット 村の生成に影響する
如果种子和0xFFFFFFFFFFFFL之间的结果相同,并且存在相同的生物群系,则会出现相同形状的村庄。换句话说,即使在种子中加减2^48的倍数,村庄的形状也不会改变。利用这一点,可以从村庄的形状(建筑物的类型和数量)中缩小种子的范围到65536个模式。然而,由于计算并不简单,使用GPGPU是困难的,并且计算需要相当长的时间。在从村庄的布局中缩小种子候选项从65536*2^48个到约65536*1000个之后,可以使用它来将候选项进一步缩小为65535*1个。根据村庄的形状缩小种子的范围似乎具有相当大的能力。