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

（この記事は一連の解説記事の一つになります）

先頭記事：[入門編](https://qiita.com/koteko/items/7afadf8261eac49bacad)
前の記事：[7. 進捗の追加](https://qiita.com/koteko/items/43c02b25b39e6a2a061d)
次の記事：[9. 木の追加と生成](https://qiita.com/koteko/items/aebfc47cf73d7e49baa6)

# 鉱石の追加
いよいよ少し本格的な部分に入っていきます。Modの固有素材を作った場合その入手手段を用意しなければなりません。それが一次資源であった場合には、ワールド内に生成しておく必要があるでしょう。今回は鉱石の追加を学びます。

まず、既に[2. ブロックの追加](https://qiita.com/koteko/items/c904b3bfdd90f0b9a98a)で説明したように、鉱石ブロックを追加しましょう。全く同じ工程なので解説は省略しますが、`BlockList.ExampleOre`を追加しました。

```java:BlockList.java
//...
public class BlockList {
    public static Block ExampleOre = new Block(
            Block.Properties.create(Material.ROCK)
                    .hardnessAndResistance(3.0F, 3.0F)
                    .lightValue(15))
            .setRegistryName(new ResourceLocation(ExampleMod.MOD_ID, "example_ore"));

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

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

**※追記**
採掘時の経験値ドロップを設定するために`Block`クラスでなく、`Block`クラスを継承した独自の`BlockExampleOre`クラスを定義したほうが良かったです。

```java:BlockExampleOre.java
package jp.koteko.example_mod.blocks;

import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;

public class BlockExampleOre extends Block {
    public BlockExampleOre(Block.Properties properties) {
        super(properties);
    }

    @Override
    public int getExpDrop(BlockState state, net.minecraft.world.IWorldReader reader, BlockPos pos, int fortune, int silktouch) {
        return silktouch == 0 ? MathHelper.nextInt(RANDOM, 3, 7) : 0;
    }
}

```
___

```
\src\main\resources
   ├ assets
   │  └ example_mod
   │     ├ blockstates
   │     │  └ example_ore.json
   │     ├ lang
   │     │  └ en_us.json
   │     │  └ ja_jp.json
   │     ├ models
   │     │  ├ block
   │     │  │  └ example_ore.json
   │     │  └ item
   │     │     └ example_ore.json
   │     └ textures
   │        ├ blocks
   │        │  └ example_ore.png
   │        └ items
   └ data
      └ example_mod
         └ loot_tables
            └ blocks
               └ example_ore.json
```
[レシピなども適宜追加](https://qiita.com/koteko/items/4e6f4e25db43907770d9)しましょう。

# 鉱石の生成
さてこれを生成させるようコードを追加していきます。

```
\src\main\java\jp\koteko\example_mod\
   ├ items
   ├ lists
   ├ world
   │   └ WorldGenOres.java
   └ ExampleMod.java
```
`WorldGenOres.java`を配置します。

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

import jp.koteko.example_mod.lists.BlockList;
import net.minecraft.block.Block;
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.OreFeatureConfig;
import net.minecraft.world.gen.placement.CountRangeConfig;
import net.minecraft.world.gen.placement.Placement;
import net.minecraftforge.registries.ForgeRegistries;

public class WorldGenOres {
    public static void setup() {
        addOreToOverworld(
                BlockList.ExampleOre,
                17,
                new CountRangeConfig(20, 0, 0, 128)
        );
    }

    private static void addOreToOverworld(Block blockIn, int size, CountRangeConfig countRangeConfigIn) {
        for(Biome biome : ForgeRegistries.BIOMES) {
            if(!biome.getCategory().equals(Biome.Category.NETHER) && !biome.getCategory().equals(Biome.Category.THEEND)) {
                biome.addFeature(
                        GenerationStage.Decoration.UNDERGROUND_ORES,
                        Biome.createDecoratedFeature(
                                Feature.ORE,
                                new OreFeatureConfig(
                                        OreFeatureConfig.FillerBlockType.NATURAL_STONE,
                                        blockIn.getDefaultState(),
                                        size
                                ),
                                Placement.COUNT_RANGE,
                                countRangeConfigIn
                        )
                );
            }
        }
    }
}

```
書き方はあくまで一例なのでもっとシンプルに書いてももっと抽象度高めに書いてもよいです。~~何が最適か考えれば考えるほどわからん。~~
コアとなる部分は`biome.addFeature()`で、`Biome`クラスのインスタンスに`Feature`すなわち特徴を追加するメソッドです。これによってバイオーム生成時に特徴が生成されます。

```java:解説
// net\minecraft\world\biome\DefaultBiomeFeatures.javaに例が沢山ある
biome.addFeature(
        // 追加する特徴のタイプ
        // net\minecraft\gen\GenerationStage.javaを見ていい感じのものを選ぶとよい
        GenerationStage.Decoration.UNDERGROUND_ORES,
        // 構成した特徴のインスタンス
        Biome.createDecoratedFeature(
                // 特徴の種類　次の引数に対応するものをここで渡すらしい
                Feature.ORE,
                // 特徴のコンフィグ
                new OreFeatureConfig(
                        // 置き換えを行う対象となるブロックの種類
                        // NATURAL_STONE は STONE,GRANITE,DIORITE,ANDESITE
                        OreFeatureConfig.FillerBlockType.NATURAL_STONE,
                        // 生成する鉱石のblockstate
                        BlockList.ExampleOre.getDefaultState(),
                        // 1か所に生成される最大の数
                        17
                ),
                // Placement(範囲を扱うオブジェクト？)の種類　次の引数に対応するものをここで渡すらしい
                Placement.COUNT_RANGE,
                // Placementのコンフィグ
                // (count, bottomOffset, topOffset, maximum)
                // 詳細が正しくつかめているかはあやしいが、
                // 0からmaximum-topOffsetまでの範囲でランダムに選んだ数に
                // bottomOffsetを足した整数値をcount回抽選するような動きをしているっぽい
                // 要するに、1チャンクあたりcount箇所の鉱石生成するy座標を決定しているらしい
                new CountRangeConfig(20, 0, 0, 128)
        )
)
```


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

```java:ExampleMod.java
//...
public class ExampleMod
{
    //...
    private void setup(final FMLCommonSetupEvent event)
    {
        WorldGenOres.setup();
    }
    //...
}
```
ゲームを起動します。
![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/256627/3b07f453-4f1a-acd8-37b0-2dc6e68e2b64.png)
**追加鉱石が生成されました。**

# 発展
Q. ネザー・エンドに生成したい
Q. 特定のバイオームにだけ生成したい
**A. `biome.getCategory().equals()`で真偽判定しましょう。**
今回例示したのは地上のすべてのバイオーム中に生成していました。そのコードは以下の部分です。

```java:
if(!biome.getCategory().equals(Biome.Category.NETHER) && !biome.getCategory().equals(Biome.Category.THEEND))
```
forで回して全てのバイオームのうちネザーでないかつエンドでないものすべてに`addFeature()`していました。この部分を適宜変えた新しいメソッドを用意するといいんじゃないかな、と思ってこの書き方をしています。あるいはいっそ引数にバイオームの配列を受け取るようにしてしまうのもありかと思います。

# 参考
[Minecraft 1.14.4 Forge Modの作成 その8 【鉱石の追加と生成】](https://qiita.com/Hiroya_W/items/fc6093ef9990d90b521e)

# 次の記事
[9. 木の追加と生成](https://qiita.com/koteko/items/aebfc47cf73d7e49baa6)