Minecraft1.21.1 Mod開発#4:基礎編②(独自のアイテムを登録しよう!)

基礎

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

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

今回は・・・

— 独自のアイテムを登録しよう! —

より実践的に、より体感的に、一緒に進めていただければと思います。

行きましょう!
Let’s Minecraft Mod Life!

アイテムの登録とは?

まず初めに、「そもそもアイテムの登録とは何か」をざっくり紹介。

一言でいえば、
マイクラで使えるアイテムを増やす
ことです。

マイクラは知っての通りたくさんのアイテムがあります。
それらは全てアイテムとして使えるように、マイクラ自体に登録されているのです。

その登録された中に、自分で新しくアイテムを追加してあげることで、独自のアイテムを作ることができるのです!

アイテムの登録(練習)

まずは早速ですが、とりあえずアイテムを登録してみましょう!

そのためには準備がありますので、まずはその準備から。

アイテムの登録の準備

アイテムの登録の準備には、2つの段階があります。

1つ目は「Javaでアイテムのデータがどこにあるのかをマイクラ(NeoForge)に教えてあげる」こと。
2つ目は「JSONファイルを使ってアイテムの情報(データ)を作る」こと。

この2つがありますが、今回は主に2つ目を重点的にやっていきます!
しかし1つ目もやらなければいけないことです。

そこで今回はコピペで完結させちゃいましょう!

Javaファイルの作成

まずはこのファイルを作る場所を確認しましょう。
フォルダ階層はとても重要なので間違えないように注意しましょう!

src/
 └─ main/
     └─ java/
         └─ com/
             └─ <mod package>/        ← ここはご自身の環境ですでにできているフォルダ名
                 └─ <mod package>/    ← ここもご自身の環境ですでにできているフォルダ名
                     └─ ModItems.java ← 今回作るJavaファイル

筆者の環境ではこんな名前でした。

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

作成するとこんな感じです。

次に、今作った「ModItems.java」を開いて、以下のコードを貼り付けます。

package com.example.examplemod;// ← ここに注意!フォルダの階層の名前が入ります!
// つまり筆者の場合には「package com.mypractice.mymod」になります!

import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.Item;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredItem;
import net.neoforged.neoforge.registries.DeferredRegister;

public class ModItems {
    // MODのアイテムを登録するための「レジストリ」を用意
    public static final DeferredRegister.Items ITEMS = 
        DeferredRegister.createItems(ExampleMod.MODID);// ← ここに注意!
        // この「ExampleMod」の部分は自分のModの名前にしましょう。
        // 筆者の場合には「PracticeMyMod」です。

    // ① アイテムのセッティング:coal_diamond
    public static final DeferredItem<Item> COAL_DIAMOND = 
        ITEMS.register("coal_diamond", () -> new Item(new Item.Properties()));

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

これをコピペしたらファイルを上書き保存しましょう。
注意書きの通りに直して何事もなければ、エラーの赤い波線が出ることなく保存できるはずです。
(一番エラーになりやすいのは一番頭の部分と真ん中あたりです。注意書きの通りに直してください)

次に既存のファイルを変更します。
変更場所を間違えないようにしてください。

既存のファイルの中で「<Modの名前>.java」というファイルを開きます。
(筆者の場合は「PracticeMyMod.java」です)

この中で下のような表記の場所の下に、改行して、以下のコードを貼り付けます。

ModItems.register(modEventBus);

貼り付けるとこんな感じです。
できたら保存しましょう。

アイテムの登録(JSONファイルの作成)

ここからがアイテムの登録になります。
練習として、「coal_diamond」(炭からできたダイヤモンド)を登録していきます!

まずは下の通りにフォルダ階層とファイルを作成しましょう!

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ <modid>/                    ← 自分のMOD ID
                 └─ models/
                      └─ item/              ← アイテムの見た目(3D/2Dモデル定義JSON)
                           └─ coal_diamond.json ★作成!

筆者の場合には以下のようになりました。

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ practicemymod/
                 ├─ lang/
                 │   └─ en_us.json
                 └─ models/
                      └─ item/
                           └─ coal_diamond.json

このフォルダ階層と「coal_diamond.json」というファイルができたら、次はそのファイルの内容に以下をコピペしましょう。

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

短いですね。
でもここはこれだけで登録できちゃうんです!

これも保存してください。

ここまで出来たら、動作テストしてみましょう!

GradleからrunClientを起動し、NeoForgeを起動させてください。
そうしたら、前回すでに作っているワールドでいいので入りましょう。

ワールドに入れたら、「T」キーでチャットを出して、以下のコマンドを実行してください。
(<modid>の部分は自分のModのIDにしましょう。筆者は「/give @s practicemymod:coal_diamond」というコマンドになりました)

/give @s <modid>:coal_diamond

すると・・・?

さっき登録したcoal_diamondが取得できました!

いかがでしょうか?

まずはコピペで作りましたが、ここから内容について詳しく見つつ、新しいアイテムを登録していきましょう!

アイテムの登録(実践)

さて、先ほどやったことを振り返ってみましょう。

  • Javaファイルを作成して準備する
  • 既存のJavaファイルに追記する
  • アイテムを登録するためのJSONファイルを作成する

こんな感じでした。

この中で「既存のJavaファイルに追記する」のは、実はもうしなくていいのです!
これは一度やれば変更しなくていいのです!(特段新しいことが増えない限りは)

今度は「Steel」を登録しながら、あと2つの処理を詳しく見ていきましょう!

Javaファイルの準備

さてここで、もう一度Javaファイルを作って準備して・・・とやっていくのでしょうか?

今回はNoです。
その理由を、先ほど作ったJavaファイルの中身を見ながら解説していきます。

ModItems.javaの解説

package com.example.examplemod;// ← ここに注意!フォルダの階層の名前が入ります!
// つまり筆者の場合には「package com.mypractice.mymod」になります!

import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.Item;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredItem;
import net.neoforged.neoforge.registries.DeferredRegister;

public class ModItems {
    // MODのアイテムを登録するための「レジストリ」を用意
    public static final DeferredRegister.Items ITEMS = 
        DeferredRegister.createItems(ExampleMod.MODID);// ← ここに注意!
        // この「ExampleMod」の部分は自分のModの名前にしましょう。
        // 筆者の場合には「PracticeMyMod」です。

    // ① アイテムのセッティング:coal_diamond
    public static final DeferredItem<Item> COAL_DIAMOND = 
        ITEMS.register("coal_diamond", () -> new Item(new Item.Properties()));

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

まずは頭の部分。

package com.example.examplemod;// ← ここに注意!フォルダの階層の名前が入ります!
// つまり筆者の場合には「package com.mypractice.mymod」になります!

この部分は「package」から始まるもので、「このファイル自身がどこにいるのか」という住所(フォルダ階層)を定義する役割があります。

これはJava言語の特徴で、最初にファイルが「どのフォルダに入っているか」を一番上の行に書くルールがあるんです。
筆者の場合は「com.mypractice.mymod」です。
(これ、実はNeoForgeのダウンロードの時に決めたものですね)
もし自分が作成したプロジェクトのフォルダ(パッケージ)構成が違うのであれば、それに合わせて書き換える必要があります。
ここが間違っているとJavaの方で「ファイルが見つからない!」と赤い波線(エラー)が出てしまうので注意です!

import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.Item;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.neoforge.registries.DeferredItem;
import net.neoforged.neoforge.registries.DeferredRegister;

この辺はインポート文とも呼ばれたりするもので、各一文ごとにそれぞれ、マイクラやNeoForgeが用意してくれている「便利な道具(機能)」の呼び出しをしています。

Javaは最初からすべての機能が使えるわけではありません。
「これからマイクラの『Item』という仕組みや、NeoForgeの『DeferredRegister』という登録機能を使いたい!」と事前に宣言(import)しています。

この「便利な道具(機能)」のことをライブラリと呼んだりもします。
他のファイルで書いた処理をこのファイルでも使えるように借りてきているイメージでしょうか。

public class ModItems {

   ...中略...

}

ここでの書き方がJavaの中では重要です。

「public」から文章が始まっていますね。
これは何かを定義するときの書き方です。
今回の場合は「ModItems」という名前の「アイテム登録専用の部屋(クラス)」を作る枠組みを定義しています。
この定義をするときの先頭が「public」だと、「Public(公共)」の言葉通り、外からでも使える、という意味になります。

つまり、この処理のまとまり(クラス)は、別のJavaファイルから読み込まれたら、その別のファイル上でも使うことができる、というものになっているのです。

そして「class(クラス)」とは、関連するプログラムをひとまとめにする「箱」や「部屋」のようなものです。
今回は「Modのアイテム(Items)をまとめる場所」なので 「ModItems」という名前をつけています。
このとき、 { と } の間に、これから作成するアイテムのデータや設定を書いていきます。

ではそんなプログラムのクラスの中では何をしているのか。

// Modのアイテムを登録するための「レジストリ」を用意
public static final DeferredRegister.Items ITEMS = 
    DeferredRegister.createItems(ExampleMod.MODID);// ← ここに注意!
    // この「ExampleMod」の部分は自分のModの名前にしましょう。
    // 筆者の場合には「PracticeMyMod」です。

「Modのアイテムを登録するための「レジストリ」を用意」とは、マイクラの世界にModの「アイテム」という情報を届けるための「専用受付窓口(レジストリ)」を開設するというイメージです。

そもそもマイクラに新しいアイテムを認識させるには、ゲームが起動するタイミングで「こんなアイテムを追加してね!」と受付してもらう窓口が必要です。
この窓口のことを専門用語でレジストリ(Registry)と呼びます。

「ExampleMod.MODID」の部分は、いったいどのMod用のアイテム窓口かを指定している場所です。
なので、メインのクラス名(筆者の場合はPracticeMyMod.MODID)に合わせて書き換える必要があるのです。

// ① アイテムのセッティング:coal_diamond
public static final DeferredItem<Item> COAL_DIAMOND = 
    ITEMS.register("coal_diamond", () -> new Item(new Item.Properties()));

【最重要】
実はここが最重要!
ここで、一つ前の命令で作ったレジストリに、新たに「coal_diamond」というID名の新しいアイテムを追加したい!という申請を出している命令です。

命令の中身。
COAL_DIAMOND:Javaプログラムの中で使う変数名(大文字で書くのが慣例です)
(変数は、=の後に書いた部分を、=の前に書いた名前で保存しておく機能、というイメージです)
“coal_diamond”:マイクラゲーム内で使われる内部IDです。
new Item.Properties():今回は何も書いていないので、勝手に「スタック数は64個」「耐久値なし」といった標準的なアイテムの基本プロパティ(属性)が付与されます。もし()の中にいろいろ追加で書くと、「スタック数は16個」「耐久値あり」といったアイテムのプロパティ(属性)をセットすることもできます。

ということは・・・
先に作ったこのJavaファイルは、全体が1つのアイテムの登録のために書かれたわけではなく、実はこの部分だけが、アイテムの登録のために書かれた部分だったということです!

なので、アイテムを追加するときは毎回Javaファイルを作るのではなく、このModItems.javaファイルのこの数行をコピーして変更すればOKなのです!

そして最後の部分。

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

この部分は、準備したレジストリを、マイクラが起動するシステム本体へ繋ぐ「スイッチ」を作る処理です。

実は、どれだけクラスの中でアイテム用のレジストリ(ITEMSのような受付窓口)を用意しても、マイクラ本体の起動処理と接続してあげないと動いてくれません。
これは受付窓口はあるのに会社本部と繋がっていないようなものです。
この「register」は、メインのプログラム(筆者の場合はPracticeMyMod.java)から呼び出してもらうことで、MOD起動時にアイテム登録処理を正しく実行させるための「接続用スイッチ」の役割を果たしています。

ModItems.javaのアイテムを追加するときの変更場所

つまりまとめると・・・

ModItems.javaの中でも

public static final DeferredItem<Item> COAL_DIAMOND = 
    ITEMS.register("coal_diamond", () -> new Item(new Item.Properties()));

この部分で、
Javaファイル内で使う名前(変数名):COAL_DIAMOND
マイクラ内で管理されるアイテムID:coal_diamond
アイテムのプロパティ(属性):new Item.Properties() ← これは標準仕様の場合
という内容でアイテムを登録しています。

なので、もし新しく「new_item」を登録したければ、これのすぐ後に

public static final DeferredItem<Item> NEW_ITEM = 
    ITEMS.register("new_item", () -> new Item(new Item.Properties()));

と追加してあげるだけでOKなのです!

ModItems.javaへの追記

今回は新たに「Steel」というアイテムを追加したいです。
ということは、その準備のために、ModItems.javaに追記してあげればできそうですね!

早速やっていきましょう!

まずはModItems.javaに新しいアイテム追加の内容を追記しましょう。

// Modでの追加アイテム「Steel」を追加
public static final DeferredItem<Item> STEEL = 
    ITEMS.register("steel", () -> new Item(new Item.Properties()));

Javaファイル内で使う名前(変数名):STEEL
マイクラ内で管理されるアイテムID:steel
アイテムのプロパティ(属性):new Item.Properties()

という内容ですね。

これでModItems.javaへの登録は完了です!

JSONファイルの準備

次はJSONファイルについてです。

先ほどのcoal_diamondの時に作ったJSONファイルは下のようなフォルダ構造の中に「coal_diamond.json」を作ってコードをコピペしましたね。

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ <modid>/                    ← 自分のMOD ID
                 └─ models/
                      └─ item/              ← アイテムの見た目(3D/2Dモデル定義JSON)
                           └─ coal_diamond.json ★作成!
{
  "parent": "minecraft:item/generated",
  "textures": {
    "layer0": "minecraft:item/diamond"
  }
}

ここで、まずはフォルダ階層について見ていきましょう。
マイクラModではフォルダ階層がデータ読み込みでは欠かせませんからね!

フォルダ「resources」と中の階層構造

先ほどまでは「src」>「java」より中の階層でJavaファイルを作っていました。
ですがここからは「src」>「resources」の中の階層でファイルを作っていきます。

中身に「models」だったり「item」だったり似た名前があって混乱する!
でもここには非常に重要な区別があるので、それを見ていきましょう!

先ほどまで扱っていた「java」フォルダ内は、プログラムを置く場所です。
一方「resources」フォルダ内は、プログラム以外のデータ(画像・音・設定・テキスト)を置く場所です。
なので言い換えれば「java」フォルダは「Modの頭脳」であり、「resources」フォルダは「Modの装飾・道具箱」です。
プログラムの中に直接画像や文字を書くことはできないため、見た目やデータに関するファイルはすべてこの「resources」フォルダの中に格納します。

つまり、先ほどまでは「java」の中の階層に「ModItems.java」を作りましたが、これは「こういう処理をしてね!」というプログラムであり、脳の処理です。
それに対してこれから作っていくのは「このアイテムはこんな見た目で、こんな設定だよ!」というデータであり、装飾・道具なのです。

2大フォルダの比較:assets と data

そんな「装飾・道具箱」の「resources」フォルダですが、その中には、主に2つの重要なフォルダがあります。

フォルダ名役割扱うデータの例
assets (今回主役!)見た目・音・テキスト(主にプレイヤーの画面用)テクスチャ(画像)、モデル(形状)、言語ファイル、効果音
data (前回使用!)ゲームの仕組み・データ(主にサーバー/内部処理用)進捗(Advancement)、クラフトレシピ、ドロップルール(Loot Table)

つまり「assets」が「装飾箱」であり、「data」が「道具箱」ですね。

前回作った「進捗」は、ゲームの仕組みとしてマイクラの内部で処理されるものです。
そのため「data」フォルダ内の階層にあります。

一方今回作る「アイテムの見た目」は言葉通り「見た目」であり、画面の描画に関わっている部分です。
そのため「assets」フォルダ内の階層に作ります。

「assets」フォルダの中:「models」 と「item」は何を分類している?

「assets」>「modid」の中には、役割ごとにフォルダが細かく分かれています。
各階層ごとに見ていきましょう。

assets/
 └─ <modid>/
      ├─ blockstates/
      ├─ font/
      ├─ lang/
      ├─ models/
      ├─ sounds/
      └─ textures/

まずは「modid」の直下のフォルダたちです。

  • blockstates/:ブロックの「状態(向き・色・アクティブ化など)に応じた見た目の切り替え」を指定する場所
  • font/:独自テキストや記号で使う「カスタムフォント・文字グラフィック」を定義する場所
  • lang/:アイテムやブロックの「多言語対応の表示名(日本語・英語など)」をまとめる場所
  • models/:アイテムやブロックの「形状・見た目の設計図(3D/2Dの骨組み)」を置く場所
  • sounds/:Mod独自の「効果音やBGMのデータ(.ogg)」と再生設定を置く場所
  • textures/:設計図(models)に貼り付ける「ピクセルアートの画像ファイル(.png)」を置く場所

さらに、今回使う「models」の中はこんな感じです。

assets/
 └─ <modid>/
      ├─ (他は省略)
      └─ models/
           ├─ block/  ← ブロックの3D形状(立方体や階段の形など)(.json)
           └─ item/   ← 手で持つアイテムの見た目の設定(.json)

この「block」と「item」は、それぞれ下のような違いがあります。

  • block/: 世界に設置される「ブロック」の立体的な形。
  • item/: インベントリに入るアイテム全般。

このように、フォルダにはそれぞれ意味があって作られています。

今回のリソースで言うと「新しく定義したアイテムの見た目をどうするかという情報を書く」ので、
情報を書く:「resources」フォルダ内
見た目:「assets」フォルダ内
どうするか → 設計図:「models」フォルダ内
アイテム:「item」フォルダ内
という感じです。

その中に、名前を合わせて「steel.json」を作りましょう!

JSONファイルの内容

今度はJSONファイル自体の内容についてです。

最初にコピペで作ったcoal_diamond.jsonを見てみましょう。

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

これを分解して見てみましょう!

"parent": "minecraft:item/generated",

これは「どんな骨組みを使うか」を指定するものです。
今回はマイクラ標準の2Dアイテムの形を借りています。

JSONファイルでは、「””」に囲まれた名前が「:」の右と左にあります。
左側の「””」の中には、「何を今決めようとしているか」という決められた名前が入ります。
(今回の場合は「parent」です)
右側の「””」の中には、「決めようとしている内容」が入ります。
(今回の場合は「minecraft:item/generated」です)

この時右側の「””」の中にも、さらに「:」がありますね。
これは左と右を分けている「:」とは役割が違います。
この時の「:」は、「””の中で、:の左側に書いてあるデータ群の中にある右側のデータ」という意味になります。
(今回の場合は「minecraft」というデータ群の中にある「item/generated」です)
(「item/generated」は、マイクラの中の標準のアイテム用の形のデータです)

"textures": { ... 中略 ...}

この書き方も先ほどと同じですね。
左側の「””」の中には、「何を今決めようとしているか」という決められた名前が入ります。
(今回の場合は「textures」です)
右側の「””」の中には、「決めようとしている内容」が入ります。

ただし先ほどと違うのは、右側が「{}」で囲まれた内容になっているということ。
(この「{}」で囲まれたデータを辞書型データ、なんて呼んだりもします)
これは「1つのデータだけではなくて複数のデータが使えるよ!」ということを表しています。

ではその中身を見てみましょう

"layer0": "minecraft:item/diamond"

これも「:」の左側と右側の法則は変わりません。
つまり「layer0」を「minecraft:item/diamond」という内容にしたい、という意味です。

そしてさらに「minecraft:item/diamond」は、「minecraft」というデータ群の中にある「item/diamond」という意味になります。

まとめると・・・

このJSONファイルは、

  • マイクラ標準の2D用の形を使って、
  • マイクラ標準のダイアモンドの見た目を割り当てる

という内容が記載されているということです!

JSONファイルの作成

では実際に新しいアイテム「steel」用のJSONファイルを作ってみましょう!

フォルダ階層は、「coal_diamond」と同じ場所です。

src/
 └─ main/
     └─ resources/
         └─ assets/
             └─ <modid>/
                 └─ models/
                      └─ item/
                           ├─ coal_diamond.json
                           └─ steel.json  ★作成!

新しく作った「steel.json」の内容を以下のようにしてみましょう!

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

これは

  • マイクラ標準の2D用の形を使って、
  • マイクラ標準の鉄インゴットの見た目を割り当てる

ということです。

ここまでできれば、新たなアイテム「steel」の登録は完成です!

確認

それではテスト起動してみましょう。

GradleからrunClientを起動し、NeoForgeを起動させてください。
そうしたら、前回すでに作っているワールドでいいので入りましょう。

ワールドに入れたら、「T」キーでチャットを出して、以下のコマンドを実行してください。
(<modid>の部分は自分のModのIDにしましょう。筆者は「/give @s practicemymod:steel」というコマンドになりました)

/give @s <modid>:steel

すると・・・?

さっき登録したsteelが取得できました!
今回は見た目が「鉄インゴット」になっていると思います!

いかがでしょうか?

おわりに&次回予告

今回は以上になります。

アイテムの登録ができたことで、自分のオリジナルのアイテムがマイクラ内に導入できるようになりましたね!
これでいっぱいアイテムが追加できそう・・・!

しかし気になるところもいろいろありますね。
例えば、
「アイテムを取得したときのアイテム名が「item.~」というIDのまま表示されている」
「マイクラにあるテクスチャ(見た目)と同じものしか使っていない」
などがありますね。

次回はそこを直してみます!
そうすればちゃんと「炭製ダイアモンド」とか「鋼鉄」とかの名前が付けられるし、見た目もオリジナルの物に変えられます!

それでは今回はありがとうございました。
次回もお楽しみに!

コメント