この記事は"Minecraft Forge Universal 14.23.1.2555~"を前提MODとしています。 |
目次
旧方式を使ったBlockの作成
1.12.2現在、ItemやBlockを登録する方法は2通りあります。
- RegistryEventを利用した登録: ForgeによってEventが呼ばれて登録処理を行います。
- 自分で登録用メソッドを呼ぶ: メインクラスのpreInitで、自分でメソッドを呼びます。
どちらでも可能ですが、今回は自分で呼ぶ方式を解説します。
処理の主な流れ
ブロックとは、ワールドを構成する要素の一種で、xyz座標ごとに1つずつ設定されます。
バニラでも、石や土、原木、水源など沢山の種類があります。
このゲームでは、ItemやBlockは1種類につき1個だけインスタンスが生成され、それをForgeに登録します。以後は同じインスタンスがずっと使いまわされます。
ここまではItemの場合と同じですが、Blockの場合は、ワールドに設置されている状態(IBlockState)とドロップアイテムとして存在している状態(ItemBlock)との2種類の状態に変化します。ですので、Itemよりも登録手順が増えています。
ブロックを追加するときは、
- Blockのインスタンスを一つだけ生成する
- 登録メソッドを呼んで、Forgeに登録する
- Blockに対応したItemBlock(アイテム)のインスタンスも生成して、Forgeに登録する
- blockstate、Block用model、ItemBlock用modelの3つのjsonファイルを作成する
- ItemBlock用modelをForgeに登録する
というステップで進行します。
順番を間違えると、nullエラーなどが発生してしまうので、上記の順番で処理が進むように書きます。
ソースコード
CrowBlock.java
MODの起動に必須のメインクラスです。
Forgeはゲームの起動時に@Modアノテーションのついているクラスを探して読み取り、含まれている起動時の処理を行ってくれます。(つまり、初期設定など起動時の処理で呼んでほしいものは、必ずメインクラスの適切なところで読み取られるように書いておかなければなりません。)
MODのIDやクラスの名前などは好きなものに変えていい部分なので、自分の作りたいものに合わせて書き換えてください。
package defeatedcrow.tutorial; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.item.Item; import net.minecraft.item.ItemBlock; import net.minecraft.util.ResourceLocation; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod.EventHandler; import net.minecraftforge.fml.common.Mod.Instance; import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; import net.minecraftforge.fml.common.registry.ForgeRegistries; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @Mod(modid = CrowBlock.MOD_ID, name = CrowBlock.MOD_NAME, version = "1.0.0", dependencies = CrowBlock.MOD_DEPENDENCIES, acceptedMinecraftVersions = CrowBlock.MOD_ACCEPTED_MC_VERSIONS, useMetadata = true) public class CrowBlock { // modidはすべて小文字にする // assetsのフォルダの名前にもなるので、フォルダ名に不適な文字は含めない方がよい public static final String MOD_ID = "karasuman_block"; public static final String MOD_NAME = "CrowBlockMod"; public static final String MOD_DEPENDENCIES = ""; public static final String MOD_ACCEPTED_MC_VERSIONS = "[1.12,1.12.2]"; @Instance("karasuman_block") public static CrowBlock instance; /* 追加ブロックのインスタンス */ public static Block crowBlock; @EventHandler public void preInit(FMLPreInitializationEvent event) { // インスタンスの生成 // setRegistryNameが必須になったのが過去版との変更点 crowBlock = new BlockCrow(Material.CLOTH).setUnlocalizedName("crow_block").setRegistryName( new ResourceLocation(MOD_ID, "crow_block")); // Blockの登録。 // 過去版と異なり、ItemBlockもインスタンスの生成と登録を行う必要がある。 ForgeRegistries.BLOCKS.register(crowBlock); // ItemBlockのRegistryNameは、Blockのものと同じにする。 ForgeRegistries.ITEMS.register(new ItemBlock(crowBlock).setRegistryName(crowBlock.getRegistryName())); // モデル登録もpreInitで呼ぶ。initでは遅い // モデルなどクライアント限定の要素は、サーバー側では読み込むだけでクラッシュしてしまう // なので、イベントのSideがどちらかを判別して、条件分けをする。 if (event.getSide().isClient()) { // クライアントサイドの時だけ、モデル登録を呼ぶ。 registerModels(); } } @SideOnly(Side.CLIENT) public void registerModels() { /* * Itemのモデルのファイルは1つで良かったが、ブロックは最低3つのファイルが必要になる。 * また、ファイルの内容の一部が自動で決められてしまい、それを間違えるとモデルがおかしくなるが、 * 自動で決められた部分が確認しづらいのが初心者に厳しいポイントである。 * . * ・BlockのStateごとにモデルを割り当てるblockstate用json * ・Blockのモデルを設定するmodel用json * ・アイテムとして表示されたときのモデルをItemBlockに設定するmodel用json * この3つを用意する。 * . * このうち、blockstateのjsonは自動でファイルのパスと名前が決められる。 * デフォルトのままで良いなら、その場所にファイルを作って置けばよい。 * Blockのモデルも、blockstatejson上で設定するなら、ここでは登録がいらない。 * . * よって、ItemBlockに対して、アイテムとしてのmodel用jsonだけ登録する。 */ // フルパスで入れているのは、以下のクラスがClientOnlyのため、Serverサイドでのimport事故を防ぐ目的 net.minecraftforge.client.model.ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(crowBlock), 0, new net.minecraft.client.renderer.block.model.ModelResourceLocation(MOD_ID + ":crow_block_item_model", "inventory")); /* * いくつかの注意事項がある * 1: モデルはメタデータごとに設定しなければならない * なので、0~15までメタ値があるアイテムなら16回繰り返すことになる * . * 2: アイテムのモデルの場合はインベントリ内での表示である"inventory"だけでよい * . * 3: ModelResourceLocationに渡すStringはそのままファイルパスとファイル名になる * 上記の場合は、assets/modid名/models/item/crow_block_item_model.jsonとなる * . * 追加アイテムが多い場合など、jsonファイルをフォルダ分けしたい場合はここで設定できる * 例えばMOD_ID + ":domein/crow_block"とすれば、 * modelsフォルダ下の「domein」フォルダの中にあるモデル用jsonを読み取るようになる */ } }
BlockCrow.java
Blockは設定できる項目、カスタムしたい場合に必要な手順が多いので、個別記事で解説します。
今回は最低限の部分のみ。
package defeatedcrow.tutorial; import java.util.List; import javax.annotation.Nullable; import net.minecraft.block.Block; import net.minecraft.block.material.Material; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.item.ItemStack; import net.minecraft.util.NonNullList; import net.minecraft.world.World; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; /* * オリジナルのBlockのクラスを作る * まずはStateを持っていない、向きやメタを持たないシンプルなブロックから。 */ /* * Blockを継承(extends)させなければ登録できない<br> * Blockを継承した別のブロックのクラスでもOK * . * Item同様、ブロックのクラスもメソッドのオーバーライドでできることが沢山ある。 * こちらはStateの設定や形の設定など、項目が多いので、別のページで解説する。 */ public class BlockCrow extends Block { public BlockCrow(Material material) { super(material); // クリエイティブタブ this.setCreativeTab(CreativeTabs.DECORATIONS); // 明るさ 0.0F~1.0F this.setLightLevel(1.0F); // 採掘したときの固さ。大きいほど採掘が遅い this.setHardness(3.0F); // 爆発耐性 this.setResistance(30.0F); // デフォルトのStateを設定 this.setDefaultState(this.blockState.getBaseState()); } /** * どのクリエイティブタブにこのアイテムを表示するか。<br> * 1.12では、ここで不必要なTabに出現しないように場合分けしないと、全Tabに出てくるようになってしまった(!!!)<br> * Itemクラスと違い、BlockにはisInCreativeTabのような判定メソッドが用意されていないので、==で条件分岐する * * @param tab クリエイティブタブの種類 * @param subItems アイテムのリスト */ @Override @SideOnly(Side.CLIENT) public void getSubBlocks(CreativeTabs tab, NonNullList<ItemStack> list) { if (tab == this.getCreativeTabToDisplayOn()) { list.add(new ItemStack(this, 1, 0)); } } /** * アイテムとして表示されたときののツールチップの内容を増やす。<br> * 頭に@SideOnly(Side.CLIENT)のついているものは、サーバーサイドでは読み込めないので要注意!<br> * これを付け忘れると、マルチプレイのときだけクラッシュしてしまう、よくあるバグModを生み出す * * @param stack 対象のアイテム * @param world 現在のワールド * @param tooltip ツールチップの文字列が入ったリスト * @param flag f3+Hで拡張ツールチップに切り替えているかどうか */ @Override @SideOnly(Side.CLIENT) public void addInformation(ItemStack stack, @Nullable World world, List<String> tooltip, ITooltipFlag advanced) {} }
JsonModel
モデル用のJsonファイルの名前や置き場所はカスタム可能ですが、初心者向きではない手順のため今回は割愛します。
何も設定していなければデフォルトで指定される置き場に合わせてフォルダを作り、jsonファイルを置けば反映される仕組みです。今回はそのデフォルト設定を使用します。
blockstate用json(crow_block.json)
ファイル名とパスは、setRegistryNameで使った名前が使用されます。
assets/MODのID/blockstates/登録名.json です。
よって、今回はassets/karasuman_block/blockstates/crow_blockになります。
ここでは『BlockStateごとのモデルの設定』を行いますが、BlockStateの解説は今回はしていません。BlockStateを何も設定していない場合は"normal"のみになる、という点を覚えておけばOK
今回は、normalのモデルとして"karasuman_block:crow_block_model"を指定しました。
これは、assets/karasuman_block/blocks/crow_block_model.jsonというファイルをモデルに指定するという意味です。
{ "variants": { "normal": { "model": "karasuman_block:crow_block_model" } } }
BlockModel用json(crow_block_model.json)
blockstate用jsonで指定したファイル名とパスでモデル用のファイルを作成します。
jsonモデルは、parent(親モデル)を指定することができます。親モデルの一部を変えたい場合は、変えたい部分のみを記述すればそこだけ置き換わります。
下記の例は、"block/cube_all"(全面が同じテクスチャの立方体モデル)を指定した上で"texture"だけ指定しているので、テクスチャだけ差し替えて、そのほかは親モデルと同じ設定のモデルになります。今回は、オーク木材のテクスチャを流用しています。
もしバニラのほかのブロックのテクスチャだけを差し替えて使用したければ、parentの部分を書き換えて、テクスチャの種類(ここでは"all")の部分を親モデルに沿った記述に変えれば可能です。
{ "parent":"block/cube_all", "textures":{"all":"blocks/planks_oak"} }
ItemBlockのモデル用json(crow_block_item_model.json)
ItemBlock用のモデルも別途でファイルを作ります。設置状態のモデルとItemBlock状態のモデルは別なので、これも作らないと"ドロップするとミッシングアイコンになってしまうブロック"ができてしまいます。
ドロップ状態の外見はBlockと同じで良いのであれば、parentにブロック用に作ったモデルを指定すれば、ブロックと同じ見た目になります。
{ "parent":"karasuman_block:block/crow_block" }
jsonファイルを記述するときのパスの書き方
"modid:フォルダ名/ファイル名" -> modid/(blockstates or models)/フォルダ名/ファイル名.jsonを指定 "フォルダ名/ファイル名" -> バニラのassetsの中の(blockstates or models)/フォルダ名/ファイル名.jsonを指定