Holograph1c Attract0r

日々の数学やプログラミングに関係する話。

Minecraft Forge 1.13.2 変更点などまとめ Part2

セットアップ段階での話は既に書いたので、主にアイテムとかブロックとかの登録方法の変更について書いておこうと思います。

mikan-alpha.hatenablog.com

Forge 1.13.2の現時点での仕様

FML(Pre、Post)InitializationEventの廃止

まずmdkをEclipseで開いて少し弄ってれば分かりますが、今まで使われていたこれらのイベントが廃止されました。(ついでにEventHandlerアノテーションも廃止されたようです)
代わりとして、FMLCommonSetupEventFMLClientSetupEventInterModEnqueueEventInterModProcessEventが実装され、以下の順番で発火されます。

RegistryEvent.Register→ FMLCommonSetupEvent → FMLClientSetupEvent → InterModEnqueueEvent → InterModProcessEvent

※このように引数にfinal修飾子が必要になった

private void setup(final FMLCommonSetupEvent event)
{
    // some preinit code
}

ただ、書いておけば勝手に発火されるのではなく、メインのクラスのコンストラクタにてリスナーというやつを追加しておく必要があります。
※ModのメインクラスがExampleMod.javaの場合

public ExampleMod() {
    // Register the setup method for modloading
    FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
 
    // Register ourselves for server and other game events we are interested in
    MinecraftForge.EVENT_BUS.register(this);
}

またこれに伴ってアイテムやブロック、Entityなどの登録の仕方が若干変わりました。

アイテムなどの登録は上の4つのイベントが発火されるより前に行われ、具体的には上に書いたRegistryEvent.Register<T>のときに行われます。

@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
public static class RegistryEvents {
    @SubscribeEvent
    public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegister) {
        // register a new block here
    }
 
    @SubscribeEvent
    public static void onItemsRegistry(final RegistryEvent.Register<Item> itemRegister) {
        // register a new item here
    }
 
    @SubscribeEvent
    public static void onEntityTypesRegistry(final RegistryEvent.Register<EntityType<?>> entityRegister) {
        // register a new entity here
    }
}

メインのクラスの中にネストする形で上記のMod.EventBusSubscriberアノテーションを付けたクラスを作り、その中にレジストリのイベントをまとめておくと簡単です。

具体的なアイテム・ブロックなどの登録方法

1.10あたりからオブジェクトホルダーとかいうのを使った登録方法があるのですが、私は1.8からModdingをしている老人なので(ほぼ)昔からと同じ方法でやっています。

ModItems.java

import net.minecraft.item.Item;
import net.minecraftforge.registries.ForgeRegistries;
 
public class ModItems {
    public static Item test;
 
    public static void init() {
        test = new ItemTest();
    }
 
    public static void register() {
        ForgeRegistries.ITEMS.registerAll(test);
    }
}

このinit()とregister()を上のRegistryEvent.Register<Item>のときに実行してやればめでたく1.13の世界にアイテムが追加できます。

@SubscribeEvent
public static void onItemsRegistry(final RegistryEvent.Register<Item> itemRegister) {
    ModItems.init();
    ModItems.register();
}

あと、アイテムとブロックのレンダラーを何もしなくても登録してくれるようになりました。
assetsのいつもの場所にjsonを置いておけば勝手に読み込んでくれます。

アイテムのプロパティの設定の仕方が変わった

上のソースではItemTestクラスとしてごまかしてしまいましたが、アイテムのスタックできる数の設定とか、ブロックの硬さの設定とか、そこらへんも少し変わりました。
具体的には、ItemクラスのコンストラクタにはItem.Propertiesインスタンス、BlockクラスのコンストラクタにはBlock.Propertiesインスタンスを渡す必要があります。
どういうことか、ざっくり解説します。
まず、無機能アイテムをつくるとして、Itemを継承したクラスを作ります。

public class ItemTest extends Item {
 
    public ItemTest() {
        // このPropertiesにいろいろくっつける
        super(new Properties());
        this.setRegistryName(modid, name);
    }
 
}

すると1.12まではスーパークラスに何も渡す必要は無かったのですが、こんな感じにItem.Propertiesのインスタンスを生成してやって渡す必要があります。
で、今まではthis.setCreativeTab(...);みたいにやってたのですが、こういった情報は全てPropertiesが持つことになりました。例えばタブの設定をしたければこう書きます。

public class ItemTest extends Item {
 
    public ItemTest() {
        // このPropertiesにいろいろくっつける
        super(new Properties().group(ItemGroup.BUILDING_BLOCKS));
        this.setRegistryName(modid, name);
    }
 
}

あとUnlocalizedNameは廃止されたっぽいですね。
ブロックのPropertiesの方もアイテムと似たような感じなので省略します。

Entityの登録の仕方はかなりまるっと変わった

タイトルが全てです。結構変わってます。そもそも、Entity自体ではなくEntityTypeというやつを登録するようになりました。

ModEntities.java

import net.minecraft.entity.EntityType;
 
public class ModEntities {
    public static EntityType<EntityTest> test;
 
    public static void init() {
        test = EntityType.Builder.create(EntityTest.class, EntityTest::new).tracker(60, 5, true).build(name);
    }
 
    public static void setName() {
        test.setRegistryName(modid, name);
    }
}

build()の引数は「MODID:Entityの名前」です。examplemod:testみたいな感じ。
あとは、アイテムとかと同じでinit()とsetName()をRegistryEvent.Register<EntityType<?>>のときに実行して、下記のようにやれば登録できます。上のメソッドだけでは登録になってないので注意。

@SubscribeEvent
public static void onEntityTypesRegistry(final RegistryEvent.Register<EntityType<?>> entityRegister) {
    ModEntities.init();
    ModEntities.setName();
 
    entityRegister.getRegistry().registerAll(ModEntities.test);
}

Entityのレンダラーに関しては、FMLClientSetupEventのときにRenderingRegistry.registerEntityRenderingHandler()を使って登録してやればOKです。

あと細かいことなど

なんか無限に書けてしまいそうなので細かいやつはまとめてしまいます。
1つ目。langファイルが.jsonになりました。まあこの変更は見やすくなっていいかな、という感じです。
assetsの中のlangに今までen_us.langみたいなのを入れてましたが、これがen_us.jsonになったということです。中身はこんな感じで書きます。

{
  "itemGroup.tabExample": "Example",
  "item.examplemod.test": "Test Item"
}

普通のjsonですね。アイテムなら「item.(MODのID).(RegistryName)」の後にローカライズ後の文字列、ブロックなら「block.(MODのID).(RegistryName)」の後に、という風に書きます。まあ、習うよりやったほうが速いと思います。(暴論)
2つ目に、クリエイティブタブの作り方が少し変わりました。

public static ItemGroup tabExample = (new ItemGroup("tabExample") {
    @Override
    public ItemStack createIcon() {
        return new ItemStack(Items.IRON_PICKAXE);
    }
});

これをメインのクラスにでも宣言しておけば新規タブの作成完了です。

まあ、ざっと目立つところでの変更点はこんな感じでしょうか。
バイオームの追加とかはまだ手を付けてないので、大幅に変わっているようだったらまた書きたいと思います。では、素敵なModdingライフを。