LoginSignup
3
0

More than 3 years have passed since last update.

[Java]MinecraftのModを作成しよう 1.14.4【9. 木の追加と生成】

Last updated at Posted at 2020-08-10

(この記事は一連の解説記事の一つになります)

先頭記事:入門編
前の記事:8. 鉱石の追加と生成
次の記事:99. Modの出力

木の追加

前記事では鉱石の追加について学びました。次は木を追加し、ワールドに生成させてみましょう。だいぶ複雑になってきますので、一緒に頑張っていきましょう。

原木の追加

まず木を構成する原木ブロックを追加します。2. ブロックの追加を参考にブロックを追加していきますが、ところどころ異なる点があるので順にみていきましょう。

BlockList.java
//...
public class BlockList {
    public static Block ExampleLog = new LogBlock(
            MaterialColor.CYAN,
            Block.Properties.create(Material.WOOD, MaterialColor.BLUE)
                    .hardnessAndResistance(2.0F)
                    .sound(SoundType.WOOD)
                    .lightValue(6))
            .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_log"));

    @SubscribeEvent
    public static void registerBlocks(RegistryEvent.Register<Block> event) {
        event.getRegistry().registerAll(
                ExampleLog
        );
    }

    @SubscribeEvent
    public static void registerBlockItems(RegistryEvent.Register<Item> event) {
        event.getRegistry().registerAll(
                new BlockItem(ExampleLog, new Item.Properties().group(ExampleItemGroup.DEFAULT))
                        .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_log"))
        );
    }
}

基本的には同じですが、単なるBlockクラスではなく、LogBlockクラスでつくります。
これはBlockクラスのサブクラスRotatedPillarBlockのサブクラスです。RotatedPillarBlockBlockに回転に関するものを追加したものです。そしてLogBlockではさらにマップ上での表示に関するものを追加しているようです。
コンストラクタの引数は1番目に一つ増えて、MaterialColorクラスでマップ上で表示する色を渡します。Block.Properties.create()の方でも引数が増えて色を渡していますが、これは非垂直設置時のマップ上の色で、先程のほうが垂直設置時の色になるようです。これらは適当に選びましょう。


例のごとくresourcesの設定をしていきますが、原木ブロックは面によってテクスチャが異なるため、この設定を以下に示します。これらはminecraftの原木ブロックを参考にしているので特別なことをしない限りこれでよいです。

\src\main\resources
   ├ assets
   │  └ example_mod
   │     ├ blockstates
   │     │  └ example_log.json
   │     ├ lang
   │     │  └ en_us.json
   │     │  └ ja_jp.json
   │     ├ models
   │     │  ├ block
   │     │  │  └ example_log.json
   │     │  └ item
   │     │     └ example_log.json
   │     └ textures
   │        ├ blocks
   │        │  ├ example_log.png
   │        │  └ example_log_top.png
   │        └ items
   └ data
      └ example_mod
         └ loot_tables
            └ blocks
               └ example_log.json
blockstates\example_log.json
{
  "variants": {
    "axis=y": { "model": "example_mod:block/example_log" },
    "axis=z": { "model": "example_mod:block/example_log", "x": 90 },
    "axis=x": { "model": "example_mod:block/example_log", "x": 90, "y": 90 }
  }
}

このように書くことで、おそらくLogBlock(厳密にはRotatedPillarBlock)クラスが持つメンバ変数axis(面の法線方向)がx,y,zのいずれかによって条件分岐するようになっています。(公式ドキュメントにもそれっぽいことが書いてありますね。)
"x":90"y":90などの記述は回転角です。要するに、垂直設置状態を基準に原木を倒した表示になるよう回転させています。

models\block\example_log.json
{
  "parent": "block/cube_column",
  "textures": {
    "end": "example_mod:blocks/example_log_top",
    "side": "example_mod:blocks/example_log"
  }
}

parentにはblock/cube_columnを指定します。これによって立方体型で上下面と側面で区別したテクスチャの適用ができます。それぞれのテクスチャファイルへのパスを指定しましょう。

models\item\example_log.json
{
  "parent": "example_mod:block/example_log"
}

これは今まで通りblockのモデルファイルを引き継げばよいです。

en_us.jp
{
  "block.example_mod.example_log": "Example Log"
}
ja_jp.json
{
  "block.example_mod.example_log": "例原木"
}

言語ファイルも今まで通りです。

\loot_table\blocks\example_log.json
{
  "type": "minecraft:block",
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:item",
          "name": "example_mod:example_log"
        }
      ]
    }
  ]
}

いつも通り。

キャプチャ.PNG
このように方向を持つブロックが追加できました。


もう一つ原木ブロックに関してやっておくことがあります。
ブロックの追加自体はここまでで完了していますが、あとで木を構成していく段階で必要になるので、ここで行っておきましょう。

今追加した原木ブロックをminecraft:logsのタグに追加します。
6. レシピの追加でも軽く触れましたが、「タグ」とはすなわち共通因子を持つオブジェクトたちをまとめたグループです。minecraftには原木に相当するものをまとめたタグがあり、木に関する処理をする際にこれを使います。そのため、この原木ブロックもこのタグに含めておくと無駄な実装をしなくて済みます。

\src\main\resources
   ├ assets
   └ data
      ├ example_mod
      └ minecraft
         └ tags
            └ blocks
               └ logs.json

自分のプロジェクトフォルダ内に\src\main\resources\data\minecraft\tags\blocksフォルダを作り、そこにlogs.jsonを配置します。この名前は必ず同じにしてください。

logs.json
{
  "replace": false,
  "values": [
    "example_mod:example_log"
  ]
}

replacefalseを与えることにより、同名のminecraft:logsにこのファイルでの記述が統合されます。valuesの中でブロックを指定しましょう。

キャプチャ.PNG
デバッグモードでブロックにカーソルを合わせた際、赤下線を引いた部分のように#minecraft:logsと表示されていれば完了です。

葉の追加

次に木を構成する葉ブロックを追加します。重ねてになりますが、ベースは2. ブロックの追加を参考に、順にみていきましょう。

BlockList.java
//...
public class BlockList {
    public static Block ExampleLeaves = new LeavesBlock(
            Block.Properties.create(Material.LEAVES)
                    .hardnessAndResistance(0.2F)
                    .tickRandomly()
                    .sound(SoundType.PLANT)
                    .lightValue(8))
            .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_leaves"));

    @SubscribeEvent
    public static void registerBlocks(RegistryEvent.Register<Block> event) {
        event.getRegistry().registerAll(
                ExampleLeaves
        );
    }

    @SubscribeEvent
    public static void registerBlockItems(RegistryEvent.Register<Item> event) {
        event.getRegistry().registerAll(
                new BlockItem(ExampleLeaves, new Item.Properties().group(ExampleItemGroup.DEFAULT))
                        .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_leaves"))
        );
    }
}

葉ブロックはLeavesBlockでつくります。その他はいつも通りです。


resourcesの設定をしていきます。

\src\main\resources
   ├ assets
   │  └ example_mod
   │     ├ blockstates
   │     │  └ example_leaves.json
   │     ├ lang
   │     │  └ en_us.json
   │     │  └ ja_jp.json
   │     ├ models
   │     │  ├ block
   │     │  │  └ example_leaves.json
   │     │  └ item
   │     │     └ example_leaves.json
   │     └ textures
   │        ├ blocks
   │        │  └ example_leaves.png
   │        └ items
   └ data
      └ example_mod
         └ loot_tables
            └ blocks
               └ example_leaves.json
blockstates\example_leaves.json
{
  "variants": {
    "": { "model": "example_mod:block/example_leaves" }
  }
}
models\block\example_leaves.json
{
  "parent": "block/leaves",
  "textures": {
    "all": "example_mod:blocks/example_leaves"
  }
}

parentにはblock/leavesを指定しましょう。(グラフィック設定にもよりますが)半透明なテクスチャが適用できます。

models\item\example_leaves.json
{
  "parent": "example_mod:block/example_leaves"
}
en_us.jp
{
  "block.example_mod.example_leaves": "Example Leaves"
}
ja_jp.json
{
  "block.example_mod.example_leaves": "例の葉"
}

これらはいつも通り。

\loot_table\blocks\example_log.json
{
  "type": "minecraft:block",
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:alternatives",
          "children": [
            {
              "type": "minecraft:item",
              "conditions": [
                {
                  "condition": "minecraft:alternative",
                  "terms": [
                    {
                      "condition": "minecraft:match_tool",
                      "predicate": {
                        "item": "minecraft:shears"
                      }
                    },
                    {
                      "condition": "minecraft:match_tool",
                      "predicate": {
                        "enchantments": [
                          {
                            "enchantment": "minecraft:silk_touch",
                            "levels": {
                              "min": 1
                            }
                          }
                        ]
                      }
                    }
                  ]
                }
              ],
              "name": "example_mod:example_leaves"
            },
            {
              "type": "minecraft:item",
              "conditions": [
                {
                  "condition": "minecraft:survives_explosion"
                },
                {
                  "condition": "minecraft:table_bonus",
                  "enchantment": "minecraft:fortune",
                  "chances": [
                    0.05,
                    0.0625,
                    0.083333336,
                    0.1
                  ]
                }
              ],
              "name": "example_mod:example_sapling"
            }
          ]
        }
      ]
    }
  ]
}

loot_tableファイルは今まで結構異なります。(今まではさぼってきましたが)葉ブロックは複数のドロップをもちエンチャントやツールにも依存するのできちんと実装するとこのようになります。
詳しくは参考ページに説明を譲りますが、簡単に説明します。
これは「ブロックからのドロップであり、数は1つ、childrenのどれかを返す。childrenは2つあって、まずハサミあるいはシルクタッチⅠのツールで壊した場合に葉ブロックを返すもの。次にそれ以外の場合のランダム確率(幸運エンチャで確率アップ)で苗を返すもの。」という風に書かれています。ここで先に出てしまい順番が前後しますが、次の項で苗の追加を行います。
これはminecraftのOrkの葉を参考にしています。各自色々と見て好きなように書きましょう。


最後に、MaterialColorの設定をします。これは葉や草がバイオームに応じて色が変わる効果をもたらすものです。ここは面倒な場合無視してもよいです。無視する場合全バイオームで同じ色が表示されるのでその色でテクスチャを用意しましょう。

BlockList.java
//...
public class BlockList {
    //...
    @SubscribeEvent
    public static void registerBlockColors(ColorHandlerEvent.Block event) {
        event.getBlockColors().register((p_210229_0_, p_210229_1_, p_210229_2_, p_210229_3_) -> {
            return p_210229_1_ != null && p_210229_2_ != null ? BiomeColors.getFoliageColor(p_210229_1_, p_210229_2_) : FoliageColors.getDefault();
        }, ExampleLeaves);
    }

    @SubscribeEvent
    public static void registerBlockItemColors(ColorHandlerEvent.Item event) {
        event.getItemColors().register((p_210235_1_, p_210235_2_) -> {
            BlockState blockstate = ((BlockItem)p_210235_1_.getItem()).getBlock().getDefaultState();
            return event.getBlockColors().getColor(blockstate, (IEnviromentBlockReader)null, (BlockPos)null, p_210235_2_);
        }, ExampleLeaves);
    }
}

宣言と登録をしているBlockList.javaで同時にこれも登録してしまいます。ColorHandlerEventというものがあるので、これをつかってregister()することで反映されます。ブロックとアイテムそれぞれについて行います。変数などが意味不明な文字の羅列になっていますが、これは難読化されたminecraftのコードからそのまま引っ張ってきただけなので変えてもよいです。
ブロックについては、BiomeColors.getFoliageColor()によってそのブロックが存在するバイオームに応じた色が取得され、登録されます。
アイテムについては、デフォルトの色が取得され、登録されます。
両者ともに、register()の第一引数に色(ラムダ式で取得してます)、第二引数に色を設定するオブジェクトを渡します。
この辺りは難しいことをやろうとしない限り深く理解しなくてよいです。

実際の色については、参考ページに詳しく書いてあります。基本的にはグレースケールの葉ブロックテクスチャを用意して色を載せているようですが、今回色味がかったファイルを用意してみた場合実際のブロックもその色味になったので、おそらく内部では加算してるのかな、と思っています。

TreeFeatureクラスとTreeクラスの追加

これらは違いを言葉で説明するのが難しいですが、ともに木を管理するクラスです。Treeクラスは木自体を管理するクラスで、この後で追加する苗に関連して必要になります。一方で、TreeFeatureクラスは木の生成に関することのみを管理しており、Treeクラスから該当するTreeFeatureを取得することもできます。

\src\main\java\jp\koteko\example_mod\
   ├ blocks
   │   └ trees
   │      └ ExampleTree.java
   ├ items
   ├ lists
   ├ world
   │   └ features
   │      └ ExampleTreeFeature.java
   └ ExampleMod.java

(ファイル配置はいろいろ参考にして合わせているつもりですが、悩んだら割と適当です。)

ExampleTreeFeature.java
package jp.koteko.example_mod.world.features;

import jp.koteko.example_mod.lists.BlockList;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.feature.TreeFeature;
import net.minecraftforge.common.IPlantable;

public class ExampleTreeFeature extends TreeFeature {
    public ExampleTreeFeature() {
        super(NoFeatureConfig::deserialize, false, 4, BlockList.ExampleLog.getDefaultState(), BlockList.ExampleLeaves.getDefaultState(), false);
        setSapling((IPlantable) BlockList.ExampleSapling);
    }
}

TreeFeatureクラスを継承してExampleTreeFeatureクラスを作ります。継承元のコンストラクタに渡す引数は順に以下の通りです。
第一引数はNoFeatureConfigのインスタンスで多分特に有効に使われません。
第二引数は何らかの通知の可否を決定するboolean値で、他の木々でfalseだったため真似しました。何かわかったら追記します。
第三引数は木の最小の高さを決めるint値です。
第四引数は幹となるブロックのBlockStateです。
第五引数は葉となるブロックのBlockStateです。
第六引数は木にツタを伸ばすかどうかを決めるboolean値です。
また、setSapling()は苗の設定をしており、ここに苗のインスタンスを渡します。苗は次の項で実装します。

ExampleTree.java
package jp.koteko.example_mod.blocks.trees;

import jp.koteko.example_mod.world.features.ExampleTreeFeature;
import net.minecraft.block.trees.Tree;
import net.minecraft.world.gen.feature.AbstractTreeFeature;
import net.minecraft.world.gen.feature.NoFeatureConfig;

import javax.annotation.Nullable;
import java.util.Random;

public class ExampleTree extends Tree {
    @Nullable
    protected AbstractTreeFeature<NoFeatureConfig> getTreeFeature(Random random) {
        return new ExampleTreeFeature();
    }
}

抽象クラスTreeを継承してExampleTreeクラスを作ります。getTreeFeatureというメソッドを定義します。先ほど定義したExampleTreeFeatureのインスタンスを返すようにしましょう。
引数で乱数を受け取って使っていませんが、これはオークの木などが確率で通常種と巨大種に分かれる場合などに使われています。

苗の追加

次に苗を追加していきます。
まず苗のクラスを用意します。

\src\main\java\jp\koteko\example_mod\
   ├ blocks
   │   ├ trees
   │   └ ExampleSapling.java
   ├ items
   ├ lists
   ├ world
   └ ExampleMod.java
BlockExampleSapling.java
package jp.koteko.example_mod.blocks;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.BushBlock;
import net.minecraft.block.IGrowable;
import net.minecraft.block.trees.Tree;
import net.minecraft.state.IntegerProperty;
import net.minecraft.state.StateContainer;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.shapes.ISelectionContext;
import net.minecraft.util.math.shapes.VoxelShape;
import net.minecraft.world.IBlockReader;
import net.minecraft.world.IWorld;
import net.minecraft.world.World;

import java.util.Random;

public class BlockExampleSapling extends BushBlock implements IGrowable {
    public static final IntegerProperty STAGE = BlockStateProperties.STAGE_0_1;
    protected static final VoxelShape SHAPE = Block.makeCuboidShape(2.0D, 0.0D, 2.0D, 14.0D, 12.0D, 14.0D);
    private final Tree tree;

    public BlockExampleSapling(Tree p_i48337_1_, Block.Properties properties) {
        super(properties);
        this.tree = p_i48337_1_;
        this.setDefaultState(this.stateContainer.getBaseState().with(STAGE, Integer.valueOf(0)));
    }

    public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) {
        return SHAPE;
    }

    public void tick(BlockState state, World worldIn, BlockPos pos, Random random) {
        super.tick(state, worldIn, pos, random);
        if (!worldIn.isAreaLoaded(pos, 1)) return; // Forge: prevent loading unloaded chunks when checking neighbor's light
        if (worldIn.getLight(pos.up()) >= 9 && random.nextInt(7) == 0) {
            this.grow(worldIn, pos, state, random);
        }

    }

    public void grow(IWorld worldIn, BlockPos pos, BlockState state, Random rand) {
        if (state.get(STAGE) == 0) {
            worldIn.setBlockState(pos, state.cycle(STAGE), 4);
        } else {
            if (!net.minecraftforge.event.ForgeEventFactory.saplingGrowTree(worldIn, rand, pos)) return;
            this.tree.spawn(worldIn, pos, state, rand);
        }

    }

    /**
     * Whether this IGrowable can grow
     */
    public boolean canGrow(IBlockReader worldIn, BlockPos pos, BlockState state, boolean isClient) {
        return true;
    }

    public boolean canUseBonemeal(World worldIn, Random rand, BlockPos pos, BlockState state) {
        return (double)worldIn.rand.nextFloat() < 0.45D;
    }

    public void grow(World worldIn, Random rand, BlockPos pos, BlockState state) {
        this.grow(worldIn, pos, state, rand);
    }

    protected void fillStateContainer(StateContainer.Builder<Block, BlockState> builder) {
        builder.add(STAGE);
    }
}

このコードについてですが、(ほぼ)すべてminecraftの苗のコードと同じです。じゃあ何故わざわざ作ったか、というと、minecraftのSaplingクラスのコンストラクタがprotectedで、modのコード内から直接使えなかったためです。minecraft側がこれをどう扱っているかは理解する時間が不足していたためここでは考えないことにしますが、おそらくそちらに合わせた形でのもう少しうまい実装の仕方があると思います。
パッケージ名、クラス名、コンストラクタの3つのみ変更して使います。

BlockList.java
//...
public class BlockList {
    public static Block ExampleSapling = new BlockExampleSapling(
            new ExampleTree(),
            Block.Properties.create(Material.PLANTS)
                    .doesNotBlockMovement()
                    .tickRandomly()
                    .hardnessAndResistance(0.0F)
                    .sound(SoundType.PLANT))
            .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_sapling"));

    @SubscribeEvent
    public static void registerBlocks(RegistryEvent.Register<Block> event) {
        event.getRegistry().registerAll(
                ExampleSapling
        );

    }

    @SubscribeEvent
    public static void registerBlockItems(RegistryEvent.Register<Item> event) {
        event.getRegistry().registerAll(
                new BlockItem(ExampleSapling, new Item.Properties().group(ExampleItemGroup.DEFAULT))
                        .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_sapling"))
        );
    }
}

先程用意した苗のクラスでブロックを追加します。
第一引数は対応する木を渡すので、先ほど作ったExampleTreeクラスのインスタンスを渡します。

いつも通りresourcesの設定をしていきます。特に変わらないものは解説を省略します。

\src\main\resources
   ├ assets
   │  └ example_mod
   │     ├ blockstates
   │     │  └ example_sapling.json
   │     ├ lang
   │     │  └ en_us.json
   │     │  └ ja_jp.json
   │     ├ models
   │     │  ├ block
   │     │  │  └ example_sapling.json
   │     │  └ item
   │     │     └ example_sapling.json
   │     └ textures
   │        ├ blocks
   │        │  └ example_sapling.png
   │        └ items
   └ data
      └ example_mod
         └ loot_tables
            └ blocks
               └ example_sapling.json
blockstates\example_sapling.json
{
  "variants": {
    "": { "model": "example_mod:block/example_sapling" }
  }
}
models\block\example_sapling.json
{
    "parent": "block/cross",
    "textures": {
        "cross": "example_mod:blocks/example_sapling"
    }
}

parentにはblock/crossを指定します。これによって平面が交差したような形でテクスチャの適用ができます(実際に形を見れば言ってる意味が分かると思います)。

models\item\example_sapling.json
{
    "parent": "item/generated",
    "textures": {
        "layer0": "example_mod:blocks/example_sapling"
    }
}

blockのモデルファイルを引き継ぐのではなく、item/generatedで指定します。

en_us.jp
{
  "block.example_mod.example_sapling": "Example Sapling"
}
ja_jp.json
{
  "block.example_mod.example_sapling": "例の苗"
}
\loot_table\blocks\example_sapling.json
{
  "type": "minecraft:block",
  "pools": [
    {
      "rolls": 1,
      "entries": [
        {
          "type": "minecraft:item",
          "name": "example_mod:example_sapling"
        }
      ]
    }
  ]
}

ゲームを起動して確認してみましょう。
キャプチャ.PNG
苗が追加され、骨粉で木が育つことが確認できると思います。
キャプチャ.PNG
また、木の幹を除去すると、葉ブロックが自然消滅をはじめ、消える際に確率で苗をドロップすることが確認できると思います。

木の生成

ここまで来たらあと少しです。ここまでで実装した木をワールド生成時に自動で生成されるようにしましょう。
これは8. 鉱石の追加と生成とほぼ同じことをするのでそちらも参考にしてください。

\src\main\java\jp\koteko\example_mod\
   ├ blocks
   ├ items
   ├ lists
   ├ world
   │   ├ WorldGenOres.java
   │   └ WorldGenTrees.java
   └ ExampleMod.java

WorldGenTrees.javaを配置します。

WorldGenOres.java
package jp.koteko.example_mod.world;

import jp.koteko.example_mod.world.features.ExampleTreeFeature;
import net.minecraft.world.biome.Biome;
import net.minecraft.world.gen.GenerationStage;
import net.minecraft.world.gen.feature.Feature;
import net.minecraft.world.gen.feature.IFeatureConfig;
import net.minecraft.world.gen.feature.NoFeatureConfig;
import net.minecraft.world.gen.placement.AtSurfaceWithExtraConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraftforge.registries.ForgeRegistries;

public class WorldGenTrees {
    public static void setup() {
        addTreeToOverworld(new ExampleTreeFeature());
    }

    private static void addTreeToOverworld(Feature<NoFeatureConfig> featureIn) {
        for(Biome biome : ForgeRegistries.BIOMES) {
            if (!biome.getCategory().equals(Biome.Category.NETHER) && !biome.getCategory().equals(Biome.Category.THEEND)) {
                biome.addFeature(
                        GenerationStage.Decoration.VEGETAL_DECORATION,
                        Biome.createDecoratedFeature(
                                featureIn,
                                IFeatureConfig.NO_FEATURE_CONFIG,
                                Placement.COUNT_EXTRA_HEIGHTMAP,
                                new AtSurfaceWithExtraConfig(2, 0.1F, 1)
                        )
                );
            }
        }
    }
}

AtSurfaceWithExtraConfigの引数は、1チャンク当たりの抽選回数、追加抽選を行う可能性、追加抽選を行う場合の追加回数です。この例の場合、「1チャンク当たり2か所抽選を行い、10%の確率でさらに1回抽選を行う」です。

最後に、今定義したWorldGenOres.setup()をメインファイル内のsetup中で呼びます。

ExampleMod.java
//...
public class ExampleMod
{
    //...
    private void setup(final FMLCommonSetupEvent event)
    {
        WorldGenTrees.setup();
    }
    //...
}

ゲームを起動して新たにワールドを生成します。
2020-08-09_17.46.30.png
なんとなく光らせておいたので夜だと一層目立ちます。

木の追加と生成ができました!

参考

1.14.3 Tags help - Modder Support - Forge Forums
バイオーム - Minecraft Wiki
ルートテーブル - Minecraft Wiki
地図アイテムフォーマット - Minecraft Wiki

次の記事

99. Modの出力

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0