アイテムやブロックなどの追加からやるべきかと思いましたが、Mob を作っているような記事をあまり見かけなかったので、Mobを作ることに挑戦します。
1. MobじゃなくてEntityらしい
マイクラにはたくさんの生物がいますね。村人や狼、モンスターからネコなど様々です。これらのことを Mob と総称していましたが、正しくは Entity というらしいです。
「Mobの作り方」ではうまく検索できません。「Entity Modding」と調べるとたくさん情報が出てきますがほぼ英語です。日本人作で有名なEntity系Modの1つにリトルメイドModがあります。メイドさんに持ち物を持たせたり武器を持たせたりして、プレイヤーと同じような動きをしてくれます。このModはGithubにて公開されているので、参考になるかと見てみましたが、複雑すぎて意味不明でした。
そこで、この方のチュートリアルを見てみることにしました。
あとソースコードです。
この人を真似ながら頑張って村人っぽいものをつくります。
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を登録するためのメソッドのようです。
引数 | 内容 |
---|---|
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)
となっており、スーパークラスの引数は World
と professionId
でした。
professionId
は職業IDみたいです。ただ、何番が何の職業に対応しているかはわかりませんでした。参照の仕方が悪かったのか、たどり着けませんでした。職業に関しては こちら で確認できます。
5. 自作したエンティティを初期化させる
TakunologyMod.java
という本体ファイルの中の preInit
メソッド内に作成したエンティティを登録するための処理を書きます。
@EventHandler public void preInit(FMLPreInitializationEvent event) { //自作したエンティティを起動時に読み込ませる EntityInit.registerEntitys(); }
これで起動時に自作Modのエンティティが初期化されてゲームシステムに登録されるはずです。
6. 動かしてみる
実行する際にログを見たいので --debug
オプションを付けて gradle runClient --debug
で実行します。
イケそうな予感。
と思ったけど...
[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 !!
ぴえん😢