2025年1月26日日曜日

forge MOD でカスタムブロックを作成する

forge MOD でカスタムブロックを作成する

概要

これまでは Mod のメインのコードに直接カスタムブロックを定義してきました
その場合は特にクラスを定義せず直接 Block クラスを使いました
今回はカスタムブロック用のクラスを作成し Block クラスを継承することでカスタムブロックを作成してみます

環境

  • macOS 15.2
  • Java 21.0.5
  • forrge MDK 1.20.6-50.1.32
  • minecraft 1.20.6

カスタムブロックを管理するクラスの作成

過去に Mod のメイン側で実装したカスタムブロックのように削除はできないが隣接すると削除されるブロックを実装します
Block クラスの onPlace と onRemove をオーバライドすることで挙動を実装します

  • vimv src/main/java/com/example/examplemod/CustomBlock.java
package com.example.examplemod;

import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.material.MapColor;

public class CustomBlock extends Block {
    private boolean suppressRemove = false; // 無限ループを防ぐためのフラグ

    public CustomBlock() {
        super(BlockBehaviour.Properties.of().mapColor(MapColor.STONE).strength(3.0F, 3.0F));
    }

    @Override
    public void onRemove(
            BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) {
        if (!suppressRemove) { // フラグが有効な場合にのみ処理を行う
            if (!world.isClientSide && state.getBlock() != newState.getBlock()) {
                System.out.println("CustomBlock at " + pos + " is being removed.");
                world.setBlock(pos, state, 3);
            }
            super.onRemove(state, world, pos, newState, isMoving);
        }
    }

    @Override
    public void onPlace(
            BlockState state, Level world, BlockPos pos, BlockState oldState, boolean isMoving) {
        super.onPlace(state, world, pos, oldState, isMoving);

        if (!world.isClientSide) {
            // 周囲のブロックをチェック、上下左右と上下を順番にチェック
            for (BlockPos neighborPos :
                    new BlockPos[] {
                        pos.north(), pos.south(), pos.east(), pos.west(), pos.above(), pos.below()
                    }) {
                BlockState neighborState = world.getBlockState(neighborPos);
                if (neighborState.getBlock() instanceof CustomBlock) {
                    System.out.println(
                            "CustomBlock found at " + neighborPos + ", breaking both blocks.");

                    // 一時的にフラグを設定して破壊を制御
                    // onRemove でブロックを破壊しない定義と矛盾するため隣接時には破壊できるようにする
                    suppressRemove = true;
                    world.destroyBlock(neighborPos, false); // 隣接ブロックを破壊
                    world.destroyBlock(pos, false); // 現在のブロックを破壊
                    suppressRemove = false;
                    break;
                }
            }
        }
    }
}

カスタムブロックを Mod に登録する

作成した CustomBlock を Mod に登録します
長いので主要な部分のみ紹介します
コード全体は前回紹介しているのでそちらを参照してください

  • vim src/main/java/com/example/examplemod/ExampleMod.java
@Mod(ExampleMod.MODID)
public class ExampleMod {
    // カスタムブロックの登録
    public static final RegistryObject<Block> CUSTOM_BLOCK =
            BLOCKS.register("custom_block", CustomBlock::new);

    // カスタムブロックアイテムの登録
    public static final RegistryObject<Item> CUSTOM_BLOCK_ITEM =
            ITEMS.register(
                    "custom_block", () -> new BlockItem(CUSTOM_BLOCK.get(), new Item.Properties()));

    private void addCreative(BuildCreativeModeTabContentsEvent event) {
        // カスタムブロックを建築ブロックタブに追加
        if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) event.accept(CUSTOM_BLOCK_ITEM);
    }
}

動作確認

  • ./gradlew build && ./gradlew runClient

で確認しクリエイティブモードのワールドでインベントリを開き建築ブロックの一覧の最後にMODブロックが追加されていることを確認しましょう
ブロックは左クリックでは破壊できませんが同じ Mod ブロックを設置させると破壊することができるのが確認できます

最後に

カスタムブロックを Block クラスを継承して作成する方法を紹介しました
ブロックのイベントハンドリングが MOD 用のブロックのみで収まるのであればカスタムクラスを使った方法のほうがイベントハンドラ用のメソッドもカスタムクラスに収めることができるのできれいに書けます

逆にメインの ExampleMod 側の BlockEvent.BreakEvent などは Mod 用のブロックだけでなくワールドに存在するすべてのブロックに対するイベントハンドリングを記載すればいいのでメイン側に書いていた Mod 用のブロックの処理は削除することができてコードもきれいになります

参考サイト

0 件のコメント:

コメントを投稿