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;
}

对于整体行为的意译可能如下所示:

    1. 确定距离。 默认情况下,距离设定为32,可以在世界设置中进行更改。

 

    1. 世界将被分割为以distance为边长的正方形区域。

 

    1. 确定每个区域出现村庄的区块。

 

    如果该区块的中心坐标位于合适的生物群落中,则生成村庄。(本文档不涉及生物群落。)

如果要更加意译的话,可以表述如下。

distance x distanceチャンク毎、地図の左上端から(distance - 8)x(distance - 8)チャンクのどこかに、特定のバイオームのとき、村が出現する。

それを「あるチャンクについて、そこに村があるか否か」という形で表現するとこのメソッドのようになる。

    1. 首先,预先分配生成位置的坐标。

 

    1. 然后,计算区域坐标(xRegion,zRegion)。(在这个过程中会使用一些变量来处理负坐标。)

 

    1. 接下来,根据区域坐标计算应该生成村庄的位置。

 

    如果生成位置和村庄应该生成的位置相同,则可以在那里生成村庄。(前提是生态群系正确。)

随机获取

以下是关于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个。根据村庄的形状缩小种子的范围似乎具有相当大的能力。

广告
将在 10 秒后关闭
bannerAds