マイクラMOD制作学習#4 レシピ追加+鉱石追加【ver1.21、1.21.1】【NeoForge】

はじめに

レシピの追加です。JSONファイルの自動生成ができるようにしていきます。
前回の記事の続きとなります。以下のリンクから是非ご覧ください!

参考

レシピのJSONデータ自動生成

tutorialmodフォルダーの下に以下のファイル構成を追加する。

tutorialmod/
 └ datagen/
   └ server/
     └ ModRecipeProvider.java

ModRecipeProvider.java

package com.chimugame.tutorialmod.datagen.server;

import com.chimugame.tutorialmod.block.ModBlocks;
import com.chimugame.tutorialmod.item.ModItems;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.RecipeCategory;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.RecipeProvider;
import net.minecraft.data.recipes.ShapedRecipeBuilder;

import java.util.concurrent.CompletableFuture;

public class ModRecipeProvider extends RecipeProvider
{
    public ModRecipeProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries)
    {
        super(output, registries);
    }

    @Override
    protected void buildRecipes(RecipeOutput recipeOutput)
    {
        // レインボーダイヤとレインボーダイヤブロックのレシピを追加
        nineBlockStorageRecipes(recipeOutput, RecipeCategory.MISC, ModItems.RAINBOW_DIAMOND, RecipeCategory.BUILDING_BLOCKS, ModBlocks.RAINBOW_DIAMOND_BLOCK);
    }
}

DataGenerators.java

@EventBusSubscriber(modid = TutorialMod.MOD_ID, bus = EventBusSubscriber.Bus.MOD)
public class DataGenerators
{
    @SubscribeEvent
    public static void gatherData(GatherDataEvent event)
    {
        DataGenerator generator = event.getGenerator();
        PackOutput packOutput = generator.getPackOutput();
        ExistingFileHelper existingFileHelper = event.getExistingFileHelper();
        CompletableFuture<HolderLookup.Provider> lookupProvider = event.getLookupProvider();

        // ブロックタグ
        ModBlockTagProvider blockTagProvider = new ModBlockTagProvider(packOutput, lookupProvider, existingFileHelper);
        generator.addProvider(event.includeServer(), blockTagProvider);

        // ルートテーブル
        generator.addProvider(event.includeServer(), new LootTableProvider(packOutput, Collections.emptySet(),
                List.of(new LootTableProvider.SubProviderEntry(ModBlockLootTableProvider::new, LootContextParamSets.BLOCK)), lookupProvider));

        // レシピ
        generator.addProvider(event.includeServer(), new ModRecipeProvider(packOutput, lookupProvider));

        // アイテムモデル
        generator.addProvider(event.includeClient(), new ModItemModelProvider(packOutput, existingFileHelper));
        // ブロックモデル
        generator.addProvider(event.includeClient(), new ModBlockStateProvider(packOutput, existingFileHelper));

        // 言語ファイル
        generator.addProvider(event.includeClient(), new ModJAJPLanguageProvider(packOutput));
        generator.addProvider(event.includeClient(), new ModENUSLanguageProvider(packOutput));
    }
}

レシピ動作確認

いつもの通り、JSONデータを生成してからゲームを起動します。
追加したレインボーダイヤブロックが作業台で作れることを確認できました!

あまりにあっさり終わってしまいましたので、ついでに鉱石ブロックと精錬レシピを追加します。

鉱石ブロック追加

ModBlocks.javaへブロック追加を定義

// -----追加するブロック群-----
public static final DeferredBlock<Block> RAINBOW_DIAMOND_BLOCK =
        registerBlock("rainbow_diamond_block",
                () -> new Block(BlockBehaviour.Properties.of()
                        .strength(4.f)                  // ブロックの硬さ
                        .requiresCorrectToolForDrops()  // 適正ツールでなければアイテムがドロップしない
                        .sound(SoundType.AMETHYST)      // 設置音
                ));
public static final DeferredBlock<Block> RAINBOW_DIAMOND_ORE =
        registerBlock("rainbow_diamond_ore",
                () -> new DropExperienceBlock(      // 経験値をドロップする経験値
                        UniformInt.of(2,4),         // 2~4の経験値をランダムにドロップ
                        BlockBehaviour.Properties.ofFullCopy(Blocks.DIAMOND_ORE)));     // ダイヤモンド鉱石の特徴をコピー
public static final DeferredBlock<Block> RAINBOW_DIAMOND_DEEPSLATE_ORE =
        registerBlock("rainbow_diamond_deepslate_ore",
                () -> new DropExperienceBlock(      // 経験値をドロップする経験値
                        UniformInt.of(3,6),         // 3~6の経験値をランダムにドロップ
                        BlockBehaviour.Properties.ofFullCopy(Blocks.DEEPSLATE_DIAMOND_ORE)));     // 深層ダイヤモンド鉱石の特徴をコピー
// -------------------------

ModBlockStateProvider.javaへブロックモデルのJSON生成を定義

@Override
protected void registerStatesAndModels()
{
    simpleBlockWithItem(ModBlocks.RAINBOW_DIAMOND_BLOCK);
    simpleBlockWithItem(ModBlocks.RAINBOW_DIAMOND_ORE);
    simpleBlockWithItem(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE);
}

ModJAJPLanguageProvider.javaへ日本語のJSON生成を定義(英語も)

@Override
protected void addTranslations()
{
    addItem(ModItems.RAINBOW_DIAMOND, "レインボーダイヤ");

    addBlock(ModBlocks.RAINBOW_DIAMOND_BLOCK, "レインボーダイヤブロック");
    addBlock(ModBlocks.RAINBOW_DIAMOND_ORE, "レインボーダイヤ鉱石");
    addBlock(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE, "深層レインボーダイヤ鉱石");

    add("creativetabs.tutorial_tab", "練習");
}

ModBlockLootTableProvider.javaへ鉱石からのドロップを定義

public class ModBlockLootTableProvider extends BlockLootSubProvider
{
    public ModBlockLootTableProvider(HolderLookup.Provider registries)
    {
        super(Set.of(), FeatureFlags.REGISTRY.allFlags(), registries);
    }

    @Override
    protected void generate()
    {
        // そのままドロップするブロック
        dropSelf(ModBlocks.RAINBOW_DIAMOND_BLOCK.get());

        // 鉱石ブロック(シルクタッチでそのままドロップ、幸運エンチャントで複数ドロップ)
        add(ModBlocks.RAINBOW_DIAMOND_ORE.get(),
                block -> createOreDrop(ModBlocks.RAINBOW_DIAMOND_ORE.get(), ModItems.RAINBOW_DIAMOND.get()));

        // 鉱石ブロック+通常でも複数個ドロップ(レッドストーンのような)
        add(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE.get(),
                block -> createMultipleOreDrops(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE.get(), ModItems.RAINBOW_DIAMOND.get(), 2, 5));
    }

    /**
     * 複数個ドロップするブロックを定義する関数
     * @param pBlock 定義するブロック
     * @param item {@code pBlock} からドロップするアイテム
     * @param minDrops 最小ドロップ数
     * @param maxDrops 最大ドロップ数
     * @return ルートテーブル
     */
    protected LootTable.Builder createMultipleOreDrops(Block pBlock, Item item, float minDrops, float maxDrops){
        HolderLookup.RegistryLookup<Enchantment> registryLookup = this.registries.lookupOrThrow(Registries.ENCHANTMENT);
        return this.createSilkTouchDispatchTable(
                pBlock,
                this.applyExplosionDecay(pBlock, LootItem.lootTableItem(item)
                        .apply(SetItemCountFunction.setCount(UniformGenerator.between(minDrops, maxDrops)))
                        .apply(ApplyBonusCount.addOreBonusCount(registryLookup.getOrThrow(Enchantments.FORTUNE)))));
    }

    @Override
    protected Iterable<Block> getKnownBlocks()
    {
        return ModBlocks.BLOCKS.getEntries().stream().map(e -> (Block) e.value()).toList();
    }
}

ModBlockTagProvider.javaへ追加する鉱石ブロックの最適ツールを定義

public class ModBlockTagProvider extends BlockTagsProvider
{
    public ModBlockTagProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, @Nullable ExistingFileHelper existingFileHelper)
    {
        super(output, lookupProvider, TutorialMod.MOD_ID, existingFileHelper);
    }

    @Override
    protected void addTags(HolderLookup.Provider provider)
    {
        // ツルハシが最適ツールのタグにブロック追加
        tag(BlockTags.MINEABLE_WITH_PICKAXE)
                .add(ModBlocks.RAINBOW_DIAMOND_BLOCK.get())
                .add(ModBlocks.RAINBOW_DIAMOND_ORE.get())
                .add(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE.get());

        // 鉄以上のツールが必要なタグにブロック追加
        tag(BlockTags.NEEDS_IRON_TOOL)
                .add(ModBlocks.RAINBOW_DIAMOND_BLOCK.get())
                .add(ModBlocks.RAINBOW_DIAMOND_ORE.get())
                .add(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE.get());
    }
}

ModCreativeModeTabs.javaへクリエイティブタブのブロック追加を定義

public class ModCreativeModeTabs
{
public static final DeferredRegister<CreativeModeTab> CREATIVE_MODE_TAB =
DeferredRegister.create(Registries.CREATIVE_MODE_TAB, TutorialMod.MOD_ID);

public static final Supplier<CreativeModeTab> TUTORIAL_ITEM_TAB = CREATIVE_MODE_TAB.register("tutorial_tab",
() -> CreativeModeTab.builder()
.icon(() -> new ItemStack(ModItems.RAINBOW_DIAMOND.get()))
.title(Component.translatable("creativetabs.tutorial_tab"))
.displayItems((itemdisplayParameters, output) -> {
output.accept(ModItems.RAINBOW_DIAMOND);
output.accept(ModBlocks.RAINBOW_DIAMOND_BLOCK);
output.accept(ModBlocks.RAINBOW_DIAMOND_ORE);
output.accept(ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE);

})
.build());

public static void register(IEventBus eventBus)
{
CREATIVE_MODE_TAB.register(eventBus);
}
}

テクスチャの追加

よければ利用してください~(配布などはご遠慮を)

精錬レシピの追加

ModRecipeProvider.javaへ精錬レシピのJSON生成を定義。

public class ModRecipeProvider extends RecipeProvider
{
    public ModRecipeProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries)
    {
        super(output, registries);
    }

    @Override
    protected void buildRecipes(RecipeOutput recipeOutput)
    {
        // レインボーダイヤとレインボーダイヤブロックのレシピを追加
        nineBlockStorageRecipes(recipeOutput, RecipeCategory.MISC, ModItems.RAINBOW_DIAMOND, RecipeCategory.BUILDING_BLOCKS, ModBlocks.RAINBOW_DIAMOND_BLOCK);

        // レインボーダイヤが精錬できるブロック、アイテムのリスト
        List<ItemLike> RAINBOW_DIAMOND = List.of(
                ModBlocks.RAINBOW_DIAMOND_ORE,
                ModBlocks.RAINBOW_DIAMOND_DEEPSLATE_ORE);
        // かまど精錬レシピの追加
        oreSmelting(recipeOutput, RAINBOW_DIAMOND, RecipeCategory.MISC, ModItems.RAINBOW_DIAMOND.get(), .25f, 200, "rainbow_diamond");
        // 溶鉱炉精錬レシピの追加
        oreBlasting(recipeOutput, RAINBOW_DIAMOND, RecipeCategory.MISC, ModItems.RAINBOW_DIAMOND.get(), .25f, 100, "rainbow_diamond");
    }

    // RecipeProvider.oreSmeltingをそのままコピー
    protected static void oreSmelting(
            RecipeOutput recipeOutput, List<ItemLike> ingredients, RecipeCategory category, ItemLike result, float experience, int cookingTime, String group)
    {
        oreCooking(recipeOutput, RecipeSerializer.SMELTING_RECIPE, SmeltingRecipe::new, ingredients, category, result,
                experience, cookingTime, group, "_from_smelting"
        );
    }

    // RecipeProvider.oreBlastingをそのままコピー
    protected static void oreBlasting(
            RecipeOutput recipeOutput, List<ItemLike> ingredients, RecipeCategory category, ItemLike result, float experience, int cookingTime, String group)
    {
        oreCooking(recipeOutput, RecipeSerializer.BLASTING_RECIPE, BlastingRecipe::new, ingredients, category, result,
                experience, cookingTime, group, "_from_blasting"
        );
    }
    
    // RecipeProvider.oreCookingをコピーし、tutorialmodフォルダー下に生成されるようにidの部分に「TutorialMod.MOD_ID:」を接頭語として書き加える
    protected static <T extends AbstractCookingRecipe> void oreCooking(RecipeOutput recipeOutput, RecipeSerializer<T> serializer, AbstractCookingRecipe.Factory<T> recipeFactory,
                                                                       List<ItemLike> ingredients, RecipeCategory category, ItemLike result, float experience, int cookingTime, String group, String suffix)
    {
        for (ItemLike itemlike : ingredients)
        {
            SimpleCookingRecipeBuilder.generic(Ingredient.of(itemlike), category, result, experience, cookingTime, serializer, recipeFactory)
                    .group(group)
                    .unlockedBy(getHasName(itemlike), has(itemlike))
                    .save(recipeOutput, TutorialMod.MOD_ID + ":" + getItemName(result) + suffix + "_" + getItemName(itemlike));
        }
    }
}

鉱石ブロックと精錬レシピの動作確認

追加した鉱石ブロックを鉄のツルハシ以上で掘れるか、経験値とアイテムがドロップするかなど設定通りの動作をするか確認します。深層鉱石のほうは複数ドロップすることが確認できました!

各鉱石がかまどと溶鉱炉で精錬可能です!

さいごに

基礎はなんとなく理解しましたので、学習備忘録の記事の更新はこれで一旦停止としようと思います。
これからは実際のMOD制作に際して必要なものを学習していき、気まぐれで更新するかもしれません。

ここまで読んでいただいてありがとうございました!!

タイトルとURLをコピーしました