fabric 編 part2 とりまブロックを追加してみる

冷静にリファレンスを読めば良かったものを...

note

作業環境

  • 日付: 2025/08/16
  • 言語: kotlin
  • バージョン: 1.21.3
minecraft_version=1.21.3
yarn_mappings=1.21.3+build.2
loader_version=0.17.2
kotlin_loader_version=1.13.4+kotlin.2.2.0
# Mod Properties
mod_version=1.0-SNAPSHOT
maven_group=org.adw39
archives_base_name=MyFabricMods
# Dependencies
# check this on https://modmuss50.me/fabric.html
fabric_version=0.114.1+1.21.3

今日のひとこと

一回聞いてすぐ好きになったので共有したくなった。

続きをみる

もうしゅお好き。

MYAAMAI!! な花梨ちゃんすこ

今日のふたこと

ちょっとお試しで、劇場風な感じに説明してみたいと思います。(決して罰ゲームではない)
E さんと I さんによる掛け合い的な説明にします。

マジでしょうもない設定

ちなみにネームスペースで言うところのこんな感じ

mod world {
    struct Human();
    let nikki = Human::new();
    mod nikki_world {
        struct People();
        let E_san = People::new();
        let I_san = People::new();
    }
}

セキュリティ的に(?)外部参照できない感じなので、E さんも I さんも日記と言う存在を認知できないです。

あと、前回クラス名やパッケージ名が不思議だったため、手作業修正しました

変更前変更後補足
org.adw39.myfabricmodsorg.adw39.my_fabric_mods-区切りの方が正しいらしい...
MyfabricmodsMyFabricMods

ブロック追加してみる

E: プログラム書く E ちゃんやで。
I: 隣で見守る I です。

E: 前回のおさらいでアイテムの追加と、クリエイティブモードのインベントリに追加するところまでやったで
I: 今回は、実際に置けるブロックを作るところまでやるの?
E: せやで。 リファレンスそのままコピペでなんとかなるからすぐ終わるはずや!
I: ほんとかな 心配だな

kotlin を最大限有効活用する

E: あそうだ、その前にやりたいことがあるねん
I: なに?

E: ModInitializer を実装してる MyFabricModsクラスから kotlin オブジェクトに変更するで
I: 何が変わるの?
E: 正直そこまで変わらないで1
I: そうなの? 調べてみたら、シングルトンを簡単に作れるって書いてあったけど
E: さすが A ちゃんやな!
I: I です。
E: さすが I ちゃんやな! すぐ調べるのは大切や!
I: 一つのインスタンスを使い回すようだね。
E: どれだけ新しくインスタンスを立てても、結局は全て同じインスタンスになるんやで。 インスタンスがごちゃ混ぜにならないから、システムの核心的なところでよくみる
I: お姉ちゃんは mod の中心部を一つにインスタンスにしたいわけだ
E: せやな。 しかし、Fabric の場合はただの初期化関数に近い気がするから、あまり意味ないかなって思う1

(変更前)

class MyFabricMods: ModInitializer {
    const val MOD_ID = "my_fabric_mods"
    val logger: Logger = LoggerFactory.getLogger(MOD_ID)

    override fun onInitialize() {
        MyBlocks.initialize()
        logger.info("Initialized {}", MOD_ID)
    }
}

(変更後)

object MyFabricMods: ModInitializer {
    const val MOD_ID = "my_fabric_mods"
    val logger: Logger = LoggerFactory.getLogger(MOD_ID)

    override fun onInitialize() {
        MyBlocks.initialize()
        logger.info("Initialized {}", MOD_ID)
    }
}

I: ほんとだ。 object に変えるだけでいいんだ。
E: これくらいの変更だったら余裕やで。でもちょっと心配だから起動してみよか。
I: 早くブロック追加しよ。

Caused by: net.fabricmc.loader.api.EntrypointException: Exception while loading entries for entrypoint 'main' provided by 'my_fabric_mods'
Caused by: net.fabricmc.loader.api.LanguageAdapterException: java.lang.IllegalAccessException: class net.fabricmc.loader.impl.util.DefaultLanguageAdapter cannot access a member of class org.adw39.my_fabric_mods.MyFabricMods with modifiers "private"

E: うせやろ
I: えー。
E: Private...なんでやろうな?

数分後

I: あの、早くブロック追加しないの?
E: でも気になるから仕方ないんやで
I: そういえば、マインクラフトってjavaで動いてなかったっけ? Kotlin だと都合悪いんじゃない?
E: kotlin と java は互換性あるんやで。だから不思議ちゃんやで
I: ほんとに? fabric-language-kotlinと言うのを内部で使ってるって書かれてるけど
E: プロジェクト作る時に kotlin って選べたから、そのままなんやで
I: fabric.mod.jsonを変更しろって書かれてる気がする、ちょっとみてみよ

...
  "entrypoints": {
    "fabric-datagen": [
      "org.adw39.my_fabric_mods.client.MyFabricModsDataGenerator"
    ],
    "client": [
      "org.adw39.my_fabric_mods.client.MyFabricModsClient"
    ],
    "main": [
      "org.adw39.my_fabric_mods.MyFabricMods"
    ]
  }
...

I: ほらっ、main のところ。 こうしてって書かれてる気がする

"main": [
    {
    "adapter": "kotlin",
    "value": "org.adw39.my_fabric_mods.MyFabricMods"
    }
]

E: ほな、きどうしてみよか

(エラーなく起動)

E: まじか。 さすが A ちゃ、I ちゃんやね
I: 褒めても何も出ないよ

note

以降この設定に変更してます。
2025/08/16、Idea の Minecraft Development プラグインのバージョン2025.2-1.8.6でプロジェクト生成した場合です。

ブロック追加してみる

E: やっとブロック追加するで
I: もう、どうでもいいところでトラブル起こすんだから E: ごめんて

ブロックの追加の仕方(簡易)

  1. Identifier.of()で id を作成します
  2. RegistryKey.of(RegistryKeys.BLOCK, Identifier)でブロックの RegistryKey を作成します
  3. RegistryKey.of(RegistryKeys.ITEM, Identifier)でアイテムの RegistryKey を作成します
    ブロックとアイテムは別の存在で、セットで使うようにこの後設定します。
  4. Blockインスタンスを作成します
  5. BlockItemインスタンスを作成します
  6. Registry.register()でアイテムとブロックをそれぞれ追加します

マインクラフトを起動すると、/give @p <MOD_ID>:<ITEM_NAME>でブロックが手に入るはずです。
左クリックで設置してみてください。

E: なんや意外と簡単そうやな
I: もうなんかだめそう

(とりあえず書いてみた)

package org.adw39.my_fabric_mods

import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents
import net.minecraft.advancement.criterion.ConsumeItemCriterion.Conditions.item
import net.minecraft.block.AbstractBlock
import net.minecraft.block.Block
import net.minecraft.item.BlockItem
import net.minecraft.item.Item
import net.minecraft.item.ItemGroups
import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier

object MyBlocks {

    val tatami = run {
        val id = Identifier.of(MyFabricMods.MOD_ID, "tatami")
        val blockKey = RegistryKey.of(RegistryKeys.BLOCK, id)
        val itemKey = RegistryKey.of(RegistryKeys.ITEM, id)

        val blockSetting = AbstractBlock.Settings.create()
            .registryKey(blockKey)
            .sounds(BlockSoundGroup.GRASS)

        val block = Block(blockSetting)
        val item = BlockItem(block, Item.Settings().registryKey(itemKey))

        Registry.register(Registries.BLOCK, blockKey, block)
        Registry.register(Registries.ITEM, itemKey, item)

        block
    }

    fun initialize() {
        ItemGroupEvents.modifyEntriesEvent(ItemGroups.BUILDING_BLOCKS).register {
            it.add { tatami.asItem() }
        }
    }
}

E: ファイルは別に切り分けたで
I: クリエイティブインベントリにも追加済みです。
E: テクスチャついてないけど、起動確認するで

pic1

E: お、普通に起動した
I: あっさり起動したね。 でも、一回間違えて BlockItemItemで初期化してたの覚えてるよ
E: 余計なこと言わんくてええで

テクスチャを張ってみる

I: 私テクスチャ作ったことあるよ
E: そか。 なら簡単に追加できそうやね
I: そなの? バニラなら指定された部分の画像ファイルを変えたり、json ファイルを少しいじるだけだったよ

note

バニラのテクスチャの基礎知識記事を書きました。
テクスチャ編 part1

(実際に張ってみた) pic1

I: なんで画像使いまわしてるの
E: 通信費圧縮のためやで。 いや、実際一ミリも変わってないで
I: 嘘でしょ
I: ...あれ、これ。 modID が item になってない?

pic2

E: ほんまや、コーディングミスったかな
I: テクスチャの場所間違えたかな...?

(調べてみる)

テクスチャを置く場所
src/main/resources/assets/<MOD_ID>より中です

画像ファイルは
正方形で、src/main/resources/assets/<MOD_ID>/textureの中です。 blockitemとサブディレクトリが続きます。

言語ファイル設定しましたか?
正方形で、src/main/resources/assets/<MOD_ID>/langの中です。 英語はen_us.json、日本語はja_jp.jsonです。

1.21.x 以降の Registry について
AbstractBlock.Settings や、Item.Settings の中にRegistryKeyを埋め込む必要があります。

...

(数時間後)

E: ぜんっぜんわからへん!
I: いっそjava で描き直そうよ...
E: いやや!うちは kotlin って決めたんや!

blockstates 登録しましたか?
ここに json ファイルおかないと表示されへんよ

E: BlockState ってあれやろ? ブロックの状態を示すやつ
I: すごい、まるでそのままだ。
E: そう思って無視してたけど
I: json ファイルを設置しないと表示されないみたいだね。

E: ...
I: ...

src/main/resources/assets/my_fabric_mods/lang/ja_jp.json

{
  "item.my_fabric_mods.tatami": "畳"
}

tip

docs だと block だったけど、item にしたら適用されたよ

src/main/resources/assets/my_fabric_mods/blockstates/tatami.json

{
  "variants": {
    "": {
      "model": "my_fabric_mods:block/tatami"
    }
  }
}

tip

モデルとテクスチャはバニラとあまり変化なかったので省略します

pic3

I: あの E: はい
I: 表示されてるんですが
E: すまんな
I: すまんなじゃないでしょ! やったね!
E: 今日は寝れそうやな

まとめ

E: ドキュメントは、ちゃんと最後まできっちり読みましょうっちゅうことや
I: そうだね。
I: 途中からタイポも多かったね
E: めんどくなってコピペに頼ってたで
I: 頻繁にコミットしてたから、やり直しが簡単だったのは良かったね。
E: せやなぁー E: I ちゃんも、ネームスペースをminecraft:って書いててミスしまくってたで
I: バニラのモデルも使わないと行けなかったから、ちょっと混合してた。

E: ほな、次回はクラフトできるようにしたいで
I: そうだね。サバイバルモードでも使えるようにしたいよね。

注釈とか

1

日記さんの考えです。言わせてすみません。

日記さんまとめ

どうしてもやりたかった。(読みづらい記事になったことを)反省してる。

動画化?そのうちね。(口癖)