Help us understand the problem. What is going on with this article?

Minecraft 1.12.2 生成アルゴリズムが変わってもエンダーアイは最も距離が近いエンド要塞を指す

More than 1 year has passed since last update.

Minecraft攻略日記です。

環境

  • Minecraft 1.12.2 Java Edition
  • forgeSrc-1.12.2-14.23.4.2705

背景

Minecraftの地上世界にはエンドへの要塞(Stronghold)が存在する。

これは世界に限られた箇所にしか存在せず、攻略するにはエンダーアイを投げて探さなければならない。エンダーアイは最寄りのエンド遺跡に向かって飛んでいく。


さて、エンド要塞の個数はバージョン1.9の更新時に、以前の3か所から128か所に増えた。そしてそれらの要塞はリング状の領域に特定の個数ずつ配置されているらしい電子殻

で、その中に The strongholds are generated at roughly equal angles from the center point of the world (for instance, each stronghold in a ring of 3 is in the region of 120 degrees from the others, measured from the origin). なる文で説明される配置で要塞が存在するという。つまり概ね等間隔ということだ。


一方、以下のサイトの図では等間隔ではなく角度もばらばらに存在している。

これは各リングを均等にn個のピースに分割し、各ピースのどこかに要塞がスポーンするということなのだろうと、私は勝手に想像している[要検証]

疑問

まあそれはそうとして、ここで一つ問題が出てくる。エンダーアイは最寄りの(距離的に最短の)遺跡を指すのか?それとも、投げた座標が所属するピースにある遺跡を指すのか?

処理上は明らかに後者の方が楽そうだ。座標をピース位置に変換して要塞座標にすれば求まる。前者だと少なくとも周辺の要塞を数個知らなければいけない。

なお、英語版Wikiではthe nearest strongholdに飛ぶと書いてある。

調査

その謎を究明すべく、エンダーアイの右クリック処理(net.minecraft.item.ItemEnderEye.onItemRightClick)を調べた。そこでは次のようなコードで座標を取得していた。

BlockPos blockpos = ((WorldServer)worldIn).getChunkProvider().getNearestStructurePos(worldIn, "Stronghold", new BlockPos(playerIn), false);

要塞の座標の計算はアイテムのイベントに書かれているのではなく「最寄りの構造物の座標」を返す関数に要塞を指定して取得するようだ。次のメソッドを経由して目的の場所にたどり着いた。

  • net.minecraft.world.gen.ChunkProviderServer.getNearestStructurePos
  • net.minecraft.world.gen.ChunkGeneratorOverworld.getNearestStructurePos

以下が最寄りのエンド要塞の位置を返すメソッドnet.minecraft.world.gen.structure.MapGenStronghold.getNearestStructurePos及び付属のメソッドである。

public BlockPos getNearestStructurePos(World worldIn, BlockPos pos, boolean findUnexplored)
{
    if (!this.ranBiomeCheck) {
        this.generatePositions();
        this.ranBiomeCheck = true;
    }

    BlockPos blockpos = null;
    BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(0, 0, 0);
    double d0 = Double.MAX_VALUE;

    for (ChunkPos chunkpos : this.structureCoords) {
        blockpos$mutableblockpos.setPos((chunkpos.x << 4) + 8, 32, (chunkpos.z << 4) + 8);
        double d1 = blockpos$mutableblockpos.distanceSq(pos);

        if (blockpos == null) {
            blockpos = new BlockPos(blockpos$mutableblockpos);
            d0 = d1;
        } else if (d1 < d0) {
            blockpos = new BlockPos(blockpos$mutableblockpos);
            d0 = d1;
        }
    }

    return blockpos;
}

private void generatePositions()
{
    this.initializeStructureData(this.world);
    int i = 0;
    ObjectIterator lvt_2_1_ = this.structureMap.values().iterator();

    while (lvt_2_1_.hasNext()) {
        StructureStart structurestart = (StructureStart) lvt_2_1_.next();

        if (i < this.structureCoords.length) {
            this.structureCoords[i++] = new ChunkPos(structurestart.getChunkPosX(), structurestart.getChunkPosZ());
        }
    }

    Random random = new Random();
    random.setSeed(this.world.getSeed());
    double d1 = random.nextDouble() * Math.PI * 2.0D;
    int j = 0;
    int k = 0;
    int l = this.structureMap.size();

    if (l < this.structureCoords.length) {
        for (int i1 = 0; i1 < this.structureCoords.length; ++i1) {
            double d0 = 4.0D * this.distance + this.distance * (double) j * 6.0D + (random.nextDouble() - 0.5D) * this.distance * 2.5D;
            int j1 = (int) Math.round(Math.cos(d1) * d0);
            int k1 = (int) Math.round(Math.sin(d1) * d0);
            BlockPos blockpos = this.world.getBiomeProvider().findBiomePosition((j1 << 4) + 8, (k1 << 4) + 8, 112, this.allowedBiomes, random);

            if (blockpos != null) {
                j1 = blockpos.getX() >> 4;
                k1 = blockpos.getZ() >> 4;
            }

            if (i1 >= l) {
                this.structureCoords[i1] = new ChunkPos(j1, k1);
            }

            d1 += (Math.PI * 2D) / (double) this.spread;
            ++k;

            if (k == this.spread) {
                ++j;
                k = 0;
                this.spread += 2 * this.spread / (j + 1);
                this.spread = Math.min(this.spread, this.structureCoords.length - i1);
                d1 += random.nextDouble() * Math.PI * 2.0D;
            }
        }
    }
}

this.structureCoordsは128要素の配列で、要塞座標列が入っている。未初期化の状態で要塞の座標にアクセスしたときにはじめて計算処理generatePositionsが行われる。getNearestStructurePosを見ると、どう見ても最寄りの要塞を取得しているように見える。

要塞の座標決定部分については今回は行わない(あとでやるかもしれないから資料としてコードを丸上げしておく)。

結果

エンダーアイは、未生成のものも含むワールド中のすべてのエンド要塞の中から最も距離的に近いものを指す

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした