マイクラMOD制作学習#3 ブロック追加【ver1.21、1.21.1】【NeoForge】

はじめに

ブロックの追加です。JSONファイルの自動生成も併せて対応していきます。
前回の記事の続きとなります。以下のリンクから是非ご覧ください!

参考

ブロッククラスの追加

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

tutorialmod/
 └ block/
   └ ModBlocks.java

ModBlocks.java

package com.chimugame.tutorialmod.block;

import com.chimugame.tutorialmod.TutorialMod;
import com.chimugame.tutorialmod.item.ModItems;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredBlock;
import net.neoforged.neoforge.registries.DeferredRegister;

import java.util.function.Supplier;

public class ModBlocks
{
// 追加するブロックの登録簿
public static final DeferredRegister.Blocks BLOCKS =
DeferredRegister.createBlocks(TutorialMod.MOD_ID);

// -----追加するブロック群-----
public static final DeferredBlock<Block> RAINBOW_DIAMOND_BLOCK =
registerBlock("rainbow_diamond_block",
() -> new Block(BlockBehaviour.Properties.of()
.strength(4.f) // ブロックの硬さ
.requiresCorrectToolForDrops() // 適正ツールでなければアイテムがドロップしない
.sound(SoundType.AMETHYST) // 設置音
));
// -------------------------

/// 登録簿へブロックを追加する
/// @param name ブロックID
/// @param block 登録時に呼び出され、{@link T} 型のブロックを生成する {@link Supplier}
/// @return 登録した {@link DeferredBlock}
/// @param <T> 登録するブロックの型({@link Block} またはそのサブクラス)
private static <T extends Block> DeferredBlock<T> registerBlock(String name, Supplier<T> block){
// ブロックとしての登録
DeferredBlock<T> toReturn = BLOCKS.register(name, block);
// アイテムとしての登録も必要
registerBlockItem(name, toReturn);
return toReturn;
}

/// アイテムの登録簿へアイテムを追加する
/// @param name アイテムID
/// @param block 登録するアイテムに対応するブロック
/// @param <T> {@code block} のブロック型({@link Block} またはそのサブクラス)
private static <T extends Block> void registerBlockItem(String name, DeferredBlock<T> block){
// 特に能力のないただのアイテムとして追加
ModItems.ITEMS.register(name, () -> new BlockItem(block.get(), new Item.Properties()));
}

/// ブロックの登録
/// @param eventBus イベントバス
public static void register(IEventBus eventBus){
BLOCKS.register(eventBus);
}
}

TutorialMod.javaの修正

  • ブロックの登録
  • クリエイティブタブへの追加
    public TutorialMod(IEventBus modEventBus, ModContainer modContainer)
{
// Register the commonSetup method for modloading
modEventBus.addListener(this::commonSetup);

// Register ourselves for server and other game events we are interested in.
// Note that this is necessary if and only if we want *this* class (ExampleMod) to respond directly to events.
// Do not add this line if there are no @SubscribeEvent-annotated functions in this class, like onServerStarting() below.
NeoForge.EVENT_BUS.register(this);

// クリエイティブモードタブの登録
ModCreativeModeTabs.register(modEventBus);

// アイテムの登録
ModItems.register(modEventBus);
// ブロックの登録
ModBlocks.register(modEventBus);


// Register the item to a creative tab
modEventBus.addListener(this::addCreative);

// Register our mod's ModConfigSpec so that FML can create and load the config file for us
modContainer.registerConfig(ModConfig.Type.COMMON, Config.SPEC);
}

クリエイティブタブにブロックを追加
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);
})
.build());

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

JSONファイルの自動生成

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

tutorialmod/
 ├ client/
 │ └ ModBlockStateProvider.java
 └ server/
   ├ ModBlockTagProvider.java
   └ ModBlockLootTableProvider.java

ModBlockStateProvider.java

package com.chimugame.tutorialmod.datagen.client;

import com.chimugame.tutorialmod.TutorialMod;
import com.chimugame.tutorialmod.block.ModBlocks;
import net.minecraft.data.PackOutput;
import net.neoforged.neoforge.client.model.generators.BlockStateProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.registries.DeferredBlock;

public class ModBlockStateProvider extends BlockStateProvider
{
    public ModBlockStateProvider(PackOutput output, ExistingFileHelper exFileHelper)
    {
        super(output, TutorialMod.MOD_ID, exFileHelper);
    }

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

    /// [#simpleBlockWithItem]を実行するUtil関数
    /// @param deferredBlock 登録するブロック
    private void simpleBlockWithItem(DeferredBlock<?> deferredBlock){
        simpleBlockWithItem(deferredBlock.get(), cubeAll(deferredBlock.get()));
    }
}

ModBlockTagProvider.java

package com.chimugame.tutorialmod.datagen.server;

import com.chimugame.tutorialmod.TutorialMod;
import com.chimugame.tutorialmod.block.ModBlocks;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.tags.BlockTags;
import net.neoforged.neoforge.common.data.BlockTagsProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper;
import org.jetbrains.annotations.Nullable;

import java.util.concurrent.CompletableFuture;

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());

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

ModBlockLootTableProvider.java

package com.chimugame.tutorialmod.datagen.server;

import com.chimugame.tutorialmod.block.ModBlocks;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.loot.BlockLootSubProvider;
import net.minecraft.world.flag.FeatureFlagSet;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.enchantment.Enchantment;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount;
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;

import java.util.Set;

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

/**
* 複数個ドロップするブロックを定義する関数
* @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();
}
}

以下のクラスにプロバイダーを追加する。
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.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));
}
}

日本語名を追加する。
ModJAJPLanguageProvider.java

public class ModJAJPLanguageProvider extends LanguageProvider
{
public ModJAJPLanguageProvider(PackOutput output)
{
super(output, TutorialMod.MOD_ID, Locale.JAPAN.toString().toLowerCase());
}

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

addBlock(ModBlocks.RAINBOW_DIAMOND_BLOCK, "レインボーダイヤブロック");

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

同じように英語のほうにも追加する。
ModENUSLanguageProvider.java

public class ModENUSLanguageProvider extends LanguageProvider
{
public ModENUSLanguageProvider(PackOutput output)
{
super(output, TutorialMod.MOD_ID, Locale.US.toString().toLowerCase());
}

@Override
protected void addTranslations()
{
addItem(ModItems.RAINBOW_DIAMOND, "Rainbow Diamond");

addBlock(ModBlocks.RAINBOW_DIAMOND_BLOCK, "Rainbow Diamond Block");

add("creativetabs.tutorial_tab", "Practice");
}
}

テクスチャの追加

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

resources/assets/
 └ tutorialmod/
   └ textures/
     └ block/
       └ rainbow_diamond_block.png

テキトーに作ったものですが、よければ利用してください。(配布などはご遠慮ください)

動作確認

JSONファイルを生成し、ゲームを起動します。
クリエイティブタブに追加されていることを確認。

ブロックが設定通りに動作するか確認します。※サバイバルモード(F3+F4で切替)で確認。

  • まず設置できること。
  • 設置時にアメジストと同じ音が鳴ること。
  • 素手など適正ツールでないとアイテムがドロップしないこと。
  • 鉄のツルハシ以上で破壊でき、アイテムがドロップすること。
タイトルとURLをコピーしました