Minecraft1.21.1 Mod開発#7:基礎編⑤(独自のブロックを登録しよう!)

基礎

こんにちは!
初めましての人は初めまして。
___です。

Minecraft(マイクラ)のMODを実際に作りながら、読者の皆さんにも一緒に作ってもらえるような、実践型連載の第七弾!

これまではアイテムの登録でしたが、今回やることはこちら!

— 独自のブロックを登録しよう —

とうとうブロックの登録まで来ました!
今まで長かったような短いような・・・。

ブロックが登録できるようになると、それこそ新たな建築材料をマイクラに追加したりできるので、Modらしくなりますね!

それでは早速始めていきましょう
Let’s Minecraft Mod Life!

ブロックを登録するための準備

これからブロックを登録していきますが、まずは準備として、ブロックのモデル(JSONファイル)とテクスチャ(PNGファイル)が必要になります。

ここで、それを作っていないと独自のブロックは作れないわけですが・・・。

ここに、筆者が作ったブロック素材があります。
ブロック用のモデルとテクスチャです。
(ダウンロード・私的利用は自由です)
ダウンロードはリンクを押すと自動でダウンロードが始まります。

今回はこれを使って進めていきます!

ダウンロードしていただくと、ZIPファイルなので解凍しちゃいましょう。

中にはこんな感じで4つのファイルがあります。
「steel_block.json」と「steel_block_texture.png」、「steel_furnace.json」と「steel_furnace_texture.png」です。
今回は「鋼鉄ブロック」と「鋼鉄のかまど」の2つをまずは登録していこうと考えています。

これは一旦ここまで。
後ですぐ使います。

フォルダ階層の準備

次は、準備したモデルとテクスチャを入れるフォルダを作ってあげないといけません!
もちろん、このモデルもテクスチャもあくまで見た目やデータなので「resources」ですね。
ということでフォルダを作ります。

現在の筆者の階層構造は今こんな感じ。

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ practicemymod/
                 ├─ lang/
                 │   ├─ en_us.json
                 │   └─ ja_jp.json
                 └─ models/
                 │   └─ item/
                 │        ├─ coal_diamond.json
                 │        └─ steel.json
                 └─ textures/
                      └─ item/
                           ├─ coal_diamond_icon.png
                           └─ steel_icon.png

これを、さっきダウンロードしたデータも移動させて、以下のような階層構造にします。

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ practicemymod/
                 ├─ lang/
                 │   ├─ en_us.json
                 │   └─ ja_jp.json
                 └─ models/
                 │   ├─ block/                  ← このフォルダを作成する 
                 │   │   ├─ steel_block.json   ← ここにJSONファイルを入れる
                 │   │   └─ steel_furnace.json ← ここにJSONファイルを入れる
                 │   └─ item/
                 │        ├─ coal_diamond.json
                 │        └─ steel.json
                 └─ textures/
                      ├─ block/                         ← このフォルダを作成する 
                      │   ├─ steel_block_texture.png   ← ここにPNGファイルを入れる
                      │   └─ steel_furnace_texture.png ← ここにPNGファイルを入れる
                      └─ item/
                           ├─ coal_diamond_icon.png
                           └─ steel_icon.png

というわけでやっていきましょう。
まずは必要なフォルダをVSCodeで作成。

作ったらまずは「models」フォルダの中の「block」フォルダを右クリックして、「エクスプローラーで表示する」からフォルダを開いてしまいましょう。
そしてエクスプローラーが開いたら、「block」フォルダを開きましょう。

そしたらその中に「JSONファイル」の方(「steel_block.json」と「steel_furnace.json」)を入れましょう。

これでモデルの配置は終わりです。
次はテクスチャです。

VSCodeの方から、同じ手順で、今度は「textures」の中の「block」フォルダをエクスプローラーで開きましょう。
開いたら、その中に「PNGファイル」の方(「steel_block_texture.png」と「steel_furnace_texture.png」)を入れます。

これでテクスチャの配置も終わりです。

VSCodeの方でも入れたファイルが見えていればOKです!

モデルとテクスチャの連携

実はブロック(JSON形式の3Dモデル)を導入する場合、フォルダ階層に入れるだけではなく、1つやらなければいけないことがあります。

それがテクスチャとの連携です!

アイテムの時はしなかったのにって?
いえ、実はアイテムの時もしているんです!
ちょっとだけアイテムの時のファイル(今回は「coal_diamond.json」)を見てみましょう!

{
  "parent": "minecraft:item/generated",
  "textures": {
    "layer0": "practicemymod:item/coal_diamond_icon"
  }
}

内容を見てみると、もう言葉の通り「textures」の文字がありますね。

アイテムの時はこのJSONファイルそのものから作ったので、連携する、という認識ではないにしろ、テクスチャは登録していたのです!

ブロックの場合にはアイテムの時とは違い、JSONファイルそのものを導入しました。
なのでファイルの中身自体は作っていないので、自分の環境に合わせてテクスチャを連携してあげる必要があるのです。

まずはVSCodeで「steel_block.json」を見てみましょう!

このJSONファイルの中に、以下のような部分があると思います。

"textures": {
    "0": "block/steel_block_texture",
    "particle": "block/steel_block_texture"
},

ここが、このJSONファイルがテクスチャを読み込んでいる場所です。
一見良さそうですね。
結構ちょっとした違いなので忘れがちになる点だと思いますので、皆さんも注意してくださいね!

アイテムの時はこう書いていました。

"layer0": "practicemymod:item/coal_diamond_icon"

お気付きでしょうか。
「item/coal_diamond_icon」と書く前に「ModのID(筆者の場合には「practicemymod」)」が入っていることに。

そうです!
ブロックの時もこれを入れてあげないとうまく連携してくれないのです!

ということで、下のように(皆さんは自分のModIDを)追加しましょう。
(「particle」の方も一緒に追加しましょう)

"textures": {
    "0": "practicemymod:block/steel_block_texture",
    "particle": "practicemymod:block/steel_block_texture"
},

できたら、同じように「steel_furnace.json」の方にも追加しましょう。

これで準備は整いました。
ここからはブロックとしての登録です!

ブロックを登録する(練習)

それではまずはやっていきましょう!

ブロック自体の登録

今回必要なファイルとフォルダ構造はこんな感じ。

src/
 └─ main/
     └─ java/
         └─ com/
             └─ mypractice/
                 └─ mymod/
                     ├─ AddExistingCreativeModeTab.java
                     ├─ Config.java
                     ├─ ModBlocks.java ← 今回作るJavaファイル
                     ├─ ModCreativeModeTabs.java
                     ├─ ModItems.java
                     ├─ PracticeMyMod.java
                     └─ PracticeMyModClient.java

「ModItems.java」がアイテムを登録したのと同じように、「ModBlocks.java」はブロックを登録します。

まずはファイルを作ってしまいましょう。

作れたら、以下の内容を書きましょう。
(コピペする場合には、一番最初の「package com.mypractice.mymod」は自分のファイルがある階層に合うように書き換えてください。また途中の「PracticeMyMod」の部分も、自分のMod名に書き換えてください)

package com.mypractice.mymod;// ← ここはコピペする場合は書き換えが必要なので注意!

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;

public class ModBlocks {
    // ① ブロックを登録するためのレジストリ(DeferredRegister)を用意
    public static final DeferredRegister.Blocks BLOCKS = 
        DeferredRegister.createBlocks(PracticeMyMod.MODID);// ← ここの「PracticeMyMod」の部分も書き換えが必要なので注意!

    // =========================================================================
    // ★ ブロックの登録エリア
    // =========================================================================

    // 鋼鉄ブロック(Steel Block)
    public static final DeferredBlock<Block> STEEL_BLOCK = BLOCKS.register("steel_block",
        () -> new Block(BlockBehaviour.Properties.of()
            .strength(5.0F, 6.0F) // 硬さと爆発耐性
            .sound(SoundType.METAL) // 設置・歩行時の音(金属音)
            .requiresCorrectToolForDrops() // 適正ツール(ツルハシ等)が必要
        )
    );

    // メインクラスから呼び出すための登録メソッド
    public static void register(IEventBus eventBus) {
        BLOCKS.register(eventBus);
    }
}

これが書けたらブロックの登録は以上です!

「ModBlocks.java」の連携

さて、今作った「ModBlocks.java」ですが、これを本体である「自分のMod名.java」(筆者の場合は「PracticeMyMod.java」)に登録してあげないと、処理をしてくれません!
なので連携させていきます。

「自分のMod名.java」を開きましょう。
開いたら、「ModItems」を登録している場所までスクロールしましょう。

「ModItems」を登録している場所があったら、その下に以下を追記して、「ModBlocks」も登録しましょう。

ModBlocks.register(modEventBus);

これで「ModBlocks」も登録されましたね!

「blockstates」の登録

ブロックを世界に置くには、別の設定も必要です。
それが「blockstates」です。

これがどういう役割なのかというのは、マイクラのブロック描画の順番を軽くでも理解しておく必要があります。
簡単に書くとこんな感じ

【ワールドに置かれたブロック】
↓
① blockstates (例: steel_block.json)
「このブロックの状態(向きや条件)に応じたモデルファイルはどれ?」を案内する標識
↓
② models/block (例: steel_block.json)
「3Dの形(立方体)と、どの画像を使うか」の設計図
↓
③ textures/block (例: steel_block_texture.png)
「どのテクスチャを張るか」という実際の画像

マイクラの階段ブロックなんかが分かりやすいですね。
実際に置かれている状態がどんな状態なのか、まずはシステムが理解する必要があります。
それに合わせてモデルを選択し、テクスチャを張っている、という流れなのです。

つまり!
これが無いとマイクラは「このブロック今はどんな状態?」が分からず、どんなモデルでどんなテクスチャを張っていいのか見当がつかないということです。

ということで大事な「blockstates」を作っていきましょう。
まずフォルダ構造はこんな感じ。

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ practicemymod/
                 ├─ blockstates/            ← このフォルダを作成する
                 │   ├─ steel_block.json   ← ここにJSONファイルを作成する
                 │   └─ steel_furnace.json ← ここにJSONファイルを作成する
                 ├─ lang/
                 │   ├─ en_us.json
                 │   └─ ja_jp.json
                 └─ models/
                 │   ├─ block/
                 │   │   ├─ steel_block.json
                 │   │   └─ steel_furnace.json
                 │   └─ item/
                 │        ├─ coal_diamond.json
                 │        └─ steel.json
                 └─ textures/
                      ├─ block/ 
                      │   ├─ steel_block_texture.png
                      │   └─ steel_furnace_texture.png
                      └─ item/
                           ├─ coal_diamond_icon.png
                           └─ steel_icon.png

まずは「blockstates」フォルダを作りましょう!

その中に「steel_block.json」を作りましょう。

その内容は以下のように書きましょう。
(真ん中の「practicemymod」の部分は、自分のModのIDに変えて書きましょう)

{
  "variants": {
    "": {
      "model": "practicemymod:block/steel_block"// ← practicesmymodの部分は自分のModのIDに変更
    }
  }
}

同じように「steel_furnace.json」も作って、内容を以下のように書き換えましょう。
(これも同じく、「practicemymod」の部分は、自分のModのIDに変えて書きましょう)

{
  "variants": {
    "": {
      "model": "practicemymod:block/steel_furnace"// ← practicesmymodの部分は自分のModのIDに変更
    }
  }
}

これでブロックの登録はできました!

ブロックのアイテムとしての登録

ブロックは、ブロックとして登録したら終わり、というわけにはいかないのです。

実はマイクラの中では、「世界に置いてある立方体のブロック(Block)」と「手で持ったりインベントリに入っている平面アイテム(Item)/立体アイテム(BlockItem)」は、内部システム上、全くの別物として扱われています。
そのため、ブロックを登録しただけだと、インベントリにはアイテムのように表示させることができません!

なので別途、アイテムとしても登録してあげないといけません。

アイテムの登録のために「ModItems.java」を開きましょう。

そしたら、以下のコードを追記しましょう。
追記場所は他のアイテムを追加した後で大丈夫です。

// =========================================================================
// ★ ブロックアイテム(BlockItem)の直接登録
// =========================================================================

// 1. 鋼鉄ブロックのアイテム
public static final DeferredItem<Item> STEEL_BLOCK = ITEMS.register("steel_block",
    () -> new BlockItem(ModBlocks.STEEL_BLOCK.get(), new Item.Properties())
);

アイテムとして登録するということは、アイテムとしての情報も作らないといけません!

なので今度は「item」フォルダの中にも、新しいJSONファイルとして「steel_block.json」を作ってあげないといけません。

なので作りましょう。

さて今回も中身を書くんですが、今回は元々3Dのモデルがあるので、それをアイテムのアイコン代わりとして拝借しようと思います!
(マイクラではブロックの場合そういうこともできちゃいます)

そこ書き方がこちら。
(例のごとく「practicemymod」の部分は自分のModのIDに変更しましょう)

{
  "parent": "practicemymod:block/steel_block"
}

親(parent)が「block」フォルダのモデルだよ!という指定方法です。

これで、アイテムとしても登録できたので、手で持ったりインベントリに出したりできますね!

クリエイティブタブへの登録

さて、作ったブロックをコマンドから出してもいいのですが、せっかく前回やっているので、クリエイティブタブにも登録してしまいましょう!

今回は「既存の建築ブロック(BUILDING_BLOCKS)タグ」と「オリジナルのタグ」の両方に追加していきます。
それではまずは「既存のタグ」の方。
そのために「AddExistingCreativeModeTab.java」を開きます。

そしたら以下を追加しましょう。

// もし「建築ブロック(BUILDING_BLOCKS)」タブを開いた時だったら
if (event.getTabKey() == CreativeModeTabs.BUILDING_BLOCKS) {
    event.accept(ModItems.STEEL_BLOCK);
}

次に「オリジナルのタブ」にも追加しちゃいましょう。
「ModCreativeModeTabs.java」を開きましょう。

そしたら以下の部分を追記しましょう。
元々はこんな感じ。

.displayItems((itemDisplayParameters, output) -> {
    output.accept(ModItems.COAL_DIAMOND.get());
    output.accept(ModItems.STEEL.get());
})

この部分を下のようにします。

.displayItems((itemDisplayParameters, output) -> {
    output.accept(ModItems.COAL_DIAMOND.get());
    output.accept(ModItems.STEEL.get());
    output.accept(ModItems.STEEL_BLOCK.get());
})

ここまでできればあと一歩!

テスト起動

それでは全部保存したのを確認して、テスト起動してみましょう!
マイクラが起動したら、インベントリを開いてみましょう。

既存の建築ブロックタブとオリジナルのタブの両方に表示されていればOKです!

さらに!今回はブロックなので、ブロックを置いてみましょう!

テクスチャも貼れて、ちゃんと置けていればOKです!

表示名の変更

ここまででブロックは出せるようになりましたが、このままだと名前がIDのままでよくないですね。
なので英語表記の方から直していきましょう!

言語の設定は「lang」フォルダの「en_us.json」です。

以前登録した鋼鉄(Steel)の下に、以下を追記しましょう。
(「practicemymod」の部分は自分のModのIDに変更しましょう)

"block.practicemymod.steel_block": "Steel Block",

ここが変更できたら、テスト起動してみましょう。
ブロックの名前が変更されていればOKです。

それでは次に日本語も設定しちゃいましょう!
今度は「ja_jp.json」です。

以下のコードを追記しましょう。
(ここも「practicemymod」の部分は自分のModのIDに直しましょう)

"block.practicemymod.steel_block": "鋼鉄ブロック"

このとき、そのまま最後に貼り付けると画像のようにエラーが出ます。
実は、この全体を見た時、各内容の最後に「,」が付いています。(一番最後には逆につけてはいけません)
JSONファイルでは、この「,」で文の区切りを把握しています。
そのため今回の場合は「,」が無いせいで、前の文と繋がっているように誤判定してエラーが出ています!
そこで、今回はこんな風に直しましょう。

{
    "itemGroup.practicemymod.mymod_tab": "自作Mod用タブ",
    
    "item.practicemymod.coal_diamond": "炭製ダイヤモンド",
    "item.practicemymod.steel": "鋼鉄",// ← ここに「,」を追加
    "block.practicemymod.steel_block": "鋼鉄ブロック"
}

エラーが消えたら、保存してテスト起動してみましょう!

名前が「鋼鉄ブロック」に変わっていればOKです!

一連の流れまとめ

さて、いかがだったでしょうか。
アイテムの時に比べてかなりいろいろやることがありましたね。
ということで今回やったことをまとめようと思います。

  1. 「models」フォルダと「textures」フォルダのそれぞれの中に「block」フォルダを作る
  2. 「models」フォルダの中の「block」フォルダには3Dモデル情報のJSONファイルを、「textures」フォルダの中の「block」フォルダにはテクスチャのPNGファイルをそれぞれ入れる。
  3. モデル情報のJSONファイルの中身を修正して、テクスチャと連携させる
  4. 「ModBlocks.java」を作る
  5. 「ModBlocks.java」にブロックを登録する
  6. 「ModBlocks.java」が処理されるように、大元ファイル「PracticeMyMod.java」と連携させる
  7. 「blockstates」フォルダを作り、その中にブロックに対応するJSONファイルを作り、そこにブロックの状態によっての見え方の指示を書く
  8. ブロックをアイテムとしても登録するために「ModItems.java」に追記する
  9. ブロックをアイテムとしても表示させるために「models」フォルダの中の「item」フォルダの方にも、ブロックに対応するJSONファイルを作る
    (特に見た目の変更が無ければブロックの見た目を親要素に指定する)
  10. クリエイティブタブへ登録する
  11. 名前の英語表記を変える
  12. 名前の日本語表記を変える

・・・多いですね。
しかしこれからはもう既存のファイルやフォルダを再度作る必要は無いので、工程としては少し楽になりますね。
以下がこれ以降の登録の手順です。

  1. 「models」フォルダの中の「block」フォルダには3Dモデル情報のJSONファイルを、「textures」フォルダの中の「block」フォルダにはテクスチャのPNGファイルをそれぞれ入れる。
  2. モデル情報のJSONファイルの中身を修正して、テクスチャと連携させる
  3. 「ModBlocks.java」にブロックを登録する
  4. 「blockstates」フォルダの中にブロックに対応するJSONファイルを作り、そこにブロックの状態によっての見え方の指示を書く
  5. ブロックをアイテムとしても登録するために「ModItems.java」に追記する
  6. ブロックをアイテムとしても表示させるために「models」フォルダの中の「item」フォルダの方にも、ブロックに対応するJSONファイルを作る
    (特に見た目の変更が無ければブロックの見た目を親要素に指定する)
  7. クリエイティブタブへ登録する
  8. 名前の英語表記を変える
  9. 名前の日本語表記を変える

これで一応「鋼鉄ブロック」の登録は終わりました。
次は「鋼鉄のかまど」の登録をしていきましょう!

ブロックを登録する(実践)

今回は「鋼鉄のかまど」に必要なJSONファイルとテクスチャはすでに入れてあります。
そしてモデル情報とテクスチャの連携も終わっています。

ということで、ブロックの登録からです!

「ModBlocks.java」にブロックを登録する

まずは「ModBlocks.java」に追記しましょう。
鋼鉄ブロックの下に追記しましょう。
(今回は鋼鉄ブロックと同じ設定です)

// 鋼鉄のかまど(Steel Furnace)
public static final DeferredBlock<Block> STEEL_FURNACE = BLOCKS.register("steel_furnace",
    () -> new Block(BlockBehaviour.Properties.of()
        .strength(5.0F, 6.0F) // 硬さと爆発耐性
        .sound(SoundType.METAL) // 設置・歩行時の音(金属音)
        .requiresCorrectToolForDrops() // 適正ツール(ツルハシ等)が必要
    )
);

ブロック状態と見え方の設定(blockstates)

次に「blockstates」フォルダの中に、今回は鋼鉄のかまど(steel_furnace)に対応するJSONファイルを作りましょう。

そこにブロックの状態によっての見え方の指示を書きます。
今回は鋼鉄ブロックと同じで、どんな時も同じ見た目になるよな指示を書きます。

{
  "variants": {
    "": {
      "model": "practicemymod:block/steel_furnace"
    }
  }
}

ブロックのアイテムとしての登録

ブロックはアイテムとしても登録しないと、手に持った時やインベントリの中の状態が定義されません。
そのためにアイテムとして登録します。
アイテムの登録なので「ModItems.java」に追記しましょう。

鋼鉄ブロックを登録している部分の下に追記しちゃいましょう。

// 鋼鉄のかまどのアイテム
public static final DeferredItem<Item> STEEL_FURNACE = ITEMS.register("steel_furnace",
    () -> new BlockItem(ModBlocks.STEEL_FURNACE.get(), new Item.Properties())
);

ブロックのアイテムとしての見た目の設定

アイテムとして登録したブロックなので、アイテムとしても表示させるために「models」フォルダの中の「item」フォルダの方にも、JSONファイルを作りましょう。

ここで、特に見た目の変更が無ければブロックの見た目を親要素に指定して、そのまま流用しましょう。

{
  "parent": "practicemymod:block/steel_furnace"
}

ここまででブロックとしての登録は終わりです。

クリエイティブタブへ登録する

ブロックを出せるように、クリエイティブタブへ登録しましょう。
今回は「鋼鉄のかまど」なので、機能的ブロック(FUNCTIONAL_BLOCKS)のタブと、オリジナルのタブへ登録しましょう。

まずは既存のタブへの登録。
「AddExistingCreativeModeTab.java」を開いて中身に追記しちゃいましょう。

内容はこんな感じのを書きましょう。

// もし「機能的ブロック(FUNCTIONAL_BLOCKS)」タブを開いた時だったら
if (event.getTabKey() == CreativeModeTabs.FUNCTIONAL_BLOCKS) {
    event.accept(ModItems.STEEL_FURNACE);
}

次はオリジナルのタブへの追加です。
「ModCreativeModeTabs.java」を開きましょう。

そして追記です。

output.accept(ModItems.STEEL_FURNACE.get());

これでタブへの登録は終わりです。

名前の英語表記を変える

そして名前の表記を、そのままIDが出るんじゃなくて、ちゃんと名前が出るようにしましょう!
「en_us.json」ファイルを開きます。

そして追記しましょう。
(ここでの「practicemymod」の部分は、自分のModのIDに変えましょう)

"block.practicemymod.steel_furnace": "Steel Furnace",

ここまで出来たら、まずは英語表記でちゃんとできているか、テスト起動で確認です。

名前の日本語表記を変える

英語表記がちゃんとできていたら、次は日本語表記を作りましょう。
「ja_jp.json」に追記してしまいましょう。

(ここも「practicemymod」の場所は自分のModのIDにしましょう)

※注意※
ここで、一番最後に追記する場合には、1つ前の行の最後に「,」を入れましょう。
入れないとエラーになります。

"block.practicemymod.steel_furnace": "鋼鉄のかまど"

ここまで出来たら日本語表記になっているか、テスト起動してみましょう!

これで完了です!

ブロックの登録は以上になります。

おわりに&次回予告

さて、いかがだったでしょうか。

ブロックの登録はアイテムの登録と同じ感じでできるかと思いきや、結構やることも多くて大変です。
まぁその分、自分の作ったブロックがマイクラの中に置けたときは最高ですが。

しかし実は今回の設定だと、鋼鉄のかまどを置くと・・・

これ、画像の中央(カーソルの当たっている場所)から四方に向けて置きました。
なのに全部同じ方向を向いている・・・。

そうなんです!
これはバグでもなんでもなく、こういうふうに設定してしまっているのです。
(まさに”鋼鉄ブロックと同じ”ように、設定しましたからね)

こんなとき本当は「置いた向きに合わせて自分の方を向いて設置したい!」ですよね。
それこそがまさに「状況に応じたブロックの状態の変化」なのです!

ということで、次回はこの「置いた向きに合わせて置けるようにする」をやっていきたいと思います!

今回は結構なボリュームになってしまいました。
次回はどうかな?
ということで今回は以上になります。
ありがとうございました。
次回もお楽しみに!

コメント