Javaは分からないけどマイクラMODを作りたい #2 村人もどきをつくる

アイテムやブロックなどの追加からやるべきかと思いましたが、Mob を作っているような記事をあまり見かけなかったので、Mobを作ることに挑戦します。

1. MobじゃなくてEntityらしい

イクラにはたくさんの生物がいますね。村人や狼、モンスターからネコなど様々です。これらのことを Mob と総称していましたが、正しくは Entity というらしいです。

「Mobの作り方」ではうまく検索できません。「Entity Modding」と調べるとたくさん情報が出てきますがほぼ英語です。日本人作で有名なEntity系Modの1つにリトルメイドModがあります。メイドさんに持ち物を持たせたり武器を持たせたりして、プレイヤーと同じような動きをしてくれます。このModはGithubにて公開されているので、参考になるかと見てみましたが、複雑すぎて意味不明でした。

そこで、この方のチュートリアルを見てみることにしました。

www.youtube.com

あとソースコードです。

github.com

この人を真似ながら頑張って村人っぽいものをつくります。

2. InitとEntityディレクトリをつくる

まずは init ディレクトリを作るようですね。これは前回のFMLイベントの2つ目に書かれていたメソッドと同じですね。自作したModがこのタイミングで追加されます。なので、追加したいアイテムやアイテム、エンティティなどは init ディレクトリを作成してこの中に登録用クラス追加すれば良さそうです。

次に entity ディレクトリを作成します。ここには Entity に関するクラスを書いていくようですね。

3. 登録クラスを作る

init/EntityInit.java を作り、この中に色々書いていきます。

package jp.takunology.takunologymod.init;

import jp.takunology.takunologymod.TakunologyMod;
import jp.takunology.takunologymod.entity.HumanUnit;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.fml.common.registry.EntityRegistry;

public class EntityInit
{
    public static final int ENTITY_HUMANUNIT = 128; //たぶんID

    public static void registerEntitys()
    {
        registerEntity("HumanUnit", HumanUnit.class, ENTITY_HUMANUNIT, 512, 65535, 256);
    }

    private static void registerEntity(String name, Class<? extends Entity> entity, int id, int range, int color1, int color2)
    {
        EntityRegistry.registerModEntity(new ResourceLocation(TakunologyMod.MODID + ":" + name), 
        entity, name, id, TakunologyMod.instance, range, 1, false, color1, color2);
    }
}

クラスやメソッドの詳細は [Ctrl] + クリック で見ることができるので、じゃんじゃん活用していきましょう。例えば EntityRegidtry クラスにマウスカーソルを合わせて Ctrl 押しながらクリックすると、そのクラスの詳細が見れます。

3.1 EntityRegistry クラス

Entity を登録するためのクラスらしいです。ここにはいろんなメソッドが入っていますが、今回使用したのは registerModEntity です。

3.1.1 registerModEntity メソッド

Entityを登録するためのメソッドのようです。

f:id:takunology:20200311015644p:plain

引数 内容
entityClass 作成した Entity クラスの場所 (ResourceLocation)
entityName その Entity の名前
id Entity の固有ID(そのMod内で重複しなければ何でも良い)
mod 作成中のmod名
trackingRange Mobの探知範囲 (プレイヤーは 512)
updateFrequency 更新頻度 (tick単位)
sendsVelocityUpdates 速度情報の有無
eggPrimary スポーンエッグの色(全体)
eggSecondary スポーンエッグの色(模様部分)

3.1.2 registerEntity

このままでは引数が多すぎるので、動画ではカプセル化して registerEntity としていますね。ここで指定するのは entityName, entityClass, id, trackingRange, eggPrimary, eggSecondary です。これらはエンティティの種類が変わるごとに値を変えていくようですね。

ちなみに、new ResourceLocation メソッド内にて TakunologyMod.MODID + ":" + name と文字列連結をしているので、このエンティティ名は takunologymod:HumanUnit となります。呼び出すときはこれを固有名として使います。

3.2 ジェネリクス

Class<? extends Entity> のように <> で囲むことをジェネリクスと言うようです。これはC#と同じようですが、C#ではジェネリックと言いますね。「型は決まっていないが処理内容は決まっている」ときに活躍する子で、汎用的な使い方ができますね。

クラスを継承する際に ? extends ~ となっているのはワイルドカード扱いという意味で、型は後から合わせてくれます。今回は Class を引数にいれるときに Entity クラスを読み込むため、その型に合わせてくれるのだと思います。

4. Entity を実装する

entity/HumanUnit.java にて、いよいよEntityの機能部分を作ります。村人をベースに作りたいので EntityVillager クラスを継承してみました。動画では EntityCow でしたが、牛と同じような動作をしてしまうので、ここからアレンジです。試行錯誤していきます。

そもそも、Entityをつくるには Register, Entity, Model, Render が必要みたいです。それぞれ登録、機能、形状、描画を意味しています。個人的な予想では形状と描画がなくても村人を継承させれば村人っぽいテクスチャが読み込まれるのだろうと思いますので、Model と Render は次回に回します。

動画では継承先のクラスを調べて、そのメソッドをオーバーライドしていく形に書き換えていますね。これを良い感じに応用すればオリジナルの人間ユニットを作れそうです。

package jp.takunology.takunologymod.entity;

import net.minecraft.entity.passive.EntityVillager;
import net.minecraft.world.World;

//村人ベースエンティティの継承
public class HumanUnit extends EntityVillager {

    public HumanUnit(World worldIn) {
        super(worldIn);      
    }
}

継承すると引数に World が書かれたメソッドを生成するように言われますので、従ったら上記のコードになりました。なんですかね?これ。

4.1 World クラス

よくわかりませんでした。たぶんスポーンさせるとかそんな感じだと思います。

4.2 super

何か強そうな修飾子ですが、これは「スーパークラスへのアクセス」です。今回は EntityVillager を継承しており、同じ名前のメソッドつまりコンストラクタをもっているため、継承した瞬間にこのメソッドが呼び出されます。で合っているのか...?

4.3 superの引数について

super(worldIn, 0) となっており、スーパークラスの引数は WorldprofessionId でした。

f:id:takunology:20200311030503p:plain

professionId は職業IDみたいです。ただ、何番が何の職業に対応しているかはわかりませんでした。参照の仕方が悪かったのか、たどり着けませんでした。職業に関しては こちら で確認できます。

f:id:takunology:20200311031118p:plain

5. 自作したエンティティを初期化させる

TakunologyMod.java という本体ファイルの中の preInit メソッド内に作成したエンティティを登録するための処理を書きます。

@EventHandler
public void preInit(FMLPreInitializationEvent event)
{
    //自作したエンティティを起動時に読み込ませる
    EntityInit.registerEntitys();
}

これで起動時に自作Modのエンティティが初期化されてゲームシステムに登録されるはずです。

6. 動かしてみる

実行する際にログを見たいので --debug オプションを付けて gradle runClient --debug で実行します。

イケそうな予感。

f:id:takunology:20200311031924p:plain

と思ったけど...

[net.minecraft.init.Bootstrap:printToSYSOUT:629]: ---- Minecraft Crash Report ----
// I let you down. Sorry :(

Time: 3/11/20 3:12 AM
Description: There was a severe problem during mod loading that has caused the game to fail

net.minecraftforge.fml.common.LoaderExceptionModCrash: Caught exception from TakunologyMod (takunologymod)
Caused by: java.lang.NullPointerException

で、で、出た~~!
NullPointerException !!

ぴえん😢