5日目 Mobの追加 (1.15 ver)
前回の終わりにストラクチャーブロックを使った構造物の追加について書くと言ったのですが、いろいろ忘れそうなので少し予定を変更してMobEntityの追加を先に書きます。
Mobの追加は1.12以前と比べると特に登録の仕方が結構変わっていました。
1. ~Entity.class
2. ~Model.class
3. ~Renderer.class
の3つのクラスを作成する点は1.12以前と同様です。
これらのクラスの詳細は後述しますので、先に登録について書いておきます。
まずなにかクラスを作成し、そこで宣言および登録をします。今回はModEntitiesというクラスを作成しました。
そして、1.13以降では登録するのはEntityではなくEntityTypeなのでEntityTypeを宣言して、EntityType.Builderで作成します。
その際にEntityClassificationやサイズ等を設定します。
その後、registerEntities内で、registryNameを設定してから、登録します。この辺はアイテムやブロックの登録と同様だと思います。
続いて、Entityの場合にはレンダリングの登録が必要になります。
レンダリングはクライアントでのみ行なわれるので、登録用のメソッドはMainModクラスのclientRegistriesで呼びだします。
登録自体は比較的単純で
RenderingRegistry.registerEntityRenderingHandler(EntityType, ~~Renderer::new);
と打ち込めばOKです。
1.13および1.14では、第一引数がEntityTypeではなく~~Entity.classかもしれません (未検証)。
ModEntities.class
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) public class ModEntities { public static final EntityType<ExampleEntity> EXAMPLE_ENTITY = EntityType.Builder.<ExampleEntity>create(ExampleEntity::new,EntityClassification.CREATURE) .size(1.0f, 2.2f) .setShouldReceiveVelocityUpdates(false) .build("example_entity"); public static final EntityType<TestEntity> TEST_ENTITY = EntityType.Builder.create(TestEntity::new,EntityClassification.CREATURE) .size(1.0f, 1.5f) .setShouldReceiveVelocityUpdates(false) .build("test_entity"); @SubscribeEvent public static void registerEntities(final RegistryEvent.Register<EntityType<?>> event) { EXAMPLE_ENTITY.setRegistryName(TestMod.MODID, "example_entity"); TEST_ENTITY.setRegistryName(TestMod.MODID, "test_entity"); event.getRegistry().registerAll(EXAMPLE_ENTITY, TEST_ENTITY); } //called by clientRegistries method in MainMod class @OnlyIn(Dist.CLIENT) public static void clientInit() { RenderingRegistry.registerEntityRenderingHandler(EXAMPLE_ENTITY, ExampleRenderer::new); RenderingRegistry.registerEntityRenderingHandler(TEST_ENTITY, TestRenderer::new); } }
TestMod.class
~~~ private void clientRegistries(final FMLClientSetupEvent event) { ModEntities.clientInit(); } ~~~
以上が登録なので、続いて作成する3つのクラスについて書いていきます。
まず1つ目の~Entity.classは、そのEntityの挙動等を決めるメインとなるクラスです。
registerGoals()で基本となる行動を決めたり、registerAttributes()で体力や速度を決めます。
このクラスは他にも設定できる項目が非常にたくさんあるので、今回は牛の行動パターンをまねたシンプルなMobを追加します。
自分の追加したい挙動に近いバニラに存在するEntityのコードを参考に編集するのが良い気がします。
TestEntity.class
public class TestEntity extends AnimalEntity { protected TestEntity(EntityType<? extends AnimalEntity> type, World worldIn) { super(type, worldIn); } @Override protected void registerGoals() { this.goalSelector.addGoal(0, new SwimGoal(this)); this.goalSelector.addGoal(1, new PanicGoal(this, 2.0D)); this.goalSelector.addGoal(2, new BreedGoal(this, 1.0D)); this.goalSelector.addGoal(3, new TemptGoal(this, 1.25D, Ingredient.fromItems(Items.WHEAT), false)); this.goalSelector.addGoal(4, new FollowParentGoal(this, 1.25D)); this.goalSelector.addGoal(5, new WaterAvoidingRandomWalkingGoal(this, 1.0D)); this.goalSelector.addGoal(6, new LookAtGoal(this, PlayerEntity.class, 6.0F)); this.goalSelector.addGoal(7, new LookRandomlyGoal(this)); } protected void registerAttributes() { super.registerAttributes(); this.getAttribute(SharedMonsterAttributes.MAX_HEALTH).setBaseValue(10.0D); this.getAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.25D); } @Override public TestEntity createChild(AgeableEntity ageable) { return ModEntities.TEST_ENTITY.create(world); } }
続いて、Modelを設定します。Modelに関しては1.12以前と同様にTabulaを使って作成するのが良いと思います。
Tabulaに関する説明は長くなるので後述し、とりあえず作成したコードを乗せておきます。
TestModel.class
public class TestModel extends EntityModel<TestEntity> { public ModelRenderer body; public ModelRenderer head; public ModelRenderer arm1; public ModelRenderer arm2; //func_228301_a_ is addBox public TestModel() { this.textureHeight = 64; this.textureWidth =128; this.body = new ModelRenderer(this, 0, 0); this.body.setRotationPoint(0.0F, 3.6F, 0.0F); this.body.func_228301_a_(-5.0F, -5.0F, -5.0F, 10, 10, 10, 0f); this.arm1 = new ModelRenderer(this, 40, 0); this.arm1.setRotationPoint(-7.7F, 10.0F, 0.0F); this.arm1.func_228301_a_(-0.5F, 0.0F, -0.5F, 1, 10, 1, 0f); this.setRotationOffset(arm1, 0.0F, 0.0F, 0.3490658503988659F); this.arm2 = new ModelRenderer(this, 44, 0); this.arm2.setRotationPoint(7.7F, 10.0F, 0.0F); this.arm2.func_228301_a_(-0.5F, 0.0F, -0.5F, 1, 10, 1, 0f); this.setRotationOffset(arm2, 0.0F, 0.0F, -0.3490658503988659F); this.head = new ModelRenderer(this, 40, 0); this.head.setRotationPoint(0.0F, 16.0F, 0.0F); this.head.func_228301_a_(-7.5F, -7.5F, -7.5F, 15, 15, 15, 0f); } //This method is setRotationAngles @Override public void func_225597_a_(TestEntity p_225597_1_, float p_225597_2_, float p_225597_3_, float p_225597_4_, float p_225597_5_, float p_225597_6_) { float f = MathHelper.sin(this.swingProgress * (float)Math.PI); float f1 = MathHelper.sin((1.0F - (1.0F - this.swingProgress) * (1.0F - this.swingProgress)) * (float)Math.PI); this.arm1.rotateAngleZ = 0.0F; this.arm2.rotateAngleZ = 0.0F; this.arm1.rotateAngleY = -(0.1F - f * 0.6F); this.arm2.rotateAngleY = 0.1F - f * 0.6F; float f2 = -(float)Math.PI / 2.25F; this.arm1.rotateAngleX = f2; this.arm2.rotateAngleX = f2; this.arm1.rotateAngleX += f * 1.2F - f1 * 0.4F; this.arm2.rotateAngleX += f * 1.2F - f1 * 0.4F; this.arm1.rotateAngleZ += MathHelper.cos(p_225597_4_ * 0.09F) * 0.05F + 0.05F; this.arm2.rotateAngleZ -= MathHelper.cos(p_225597_4_ * 0.09F) * 0.05F + 0.05F; this.arm1.rotateAngleX += MathHelper.sin(p_225597_4_ * 0.067F) * 0.05F; this.arm2.rotateAngleX -= MathHelper.sin(p_225597_4_ * 0.067F) * 0.05F; } //This method is render @Override public void func_225598_a_(MatrixStack p_225598_1_, IVertexBuilder p_225598_2_, int p_225598_3_, int p_225598_4_, float p_225598_5_, float p_225598_6_, float p_225598_7_, float p_225598_8_) { body.func_228309_a_(p_225598_1_, p_225598_2_, p_225598_3_, p_225598_4_, p_225598_5_, p_225598_6_, p_225598_7_, p_225598_8_); head.func_228309_a_(p_225598_1_, p_225598_2_, p_225598_3_, p_225598_4_, p_225598_5_, p_225598_6_, p_225598_7_, p_225598_8_); arm1.func_228309_a_(p_225598_1_, p_225598_2_, p_225598_3_, p_225598_4_, p_225598_5_, p_225598_6_, p_225598_7_, p_225598_8_); arm2.func_228309_a_(p_225598_1_, p_225598_2_, p_225598_3_, p_225598_4_, p_225598_5_, p_225598_6_, p_225598_7_, p_225598_8_); } private void setRotationOffset(ModelRenderer renderer, float x, float y, float z) { renderer.rotateAngleX = x; renderer.rotateAngleY = y; renderer.rotateAngleZ = z; } }
最後にテクスチャーを設定するためのRendererクラスを作成します。
このクラスはそんなに設定することはないです。
TestRenderer.class
public class TestRenderer extends MobRenderer<TestEntity, TestModel> { public TestRenderer(EntityRendererManager p_i50961_1_) { super(p_i50961_1_, new TestModel(), 0.5f); } @Override public ResourceLocation getEntityTexture(TestEntity entity) { return new ResourceLocation(TestMod.MODID, "textures/entity/test_entity.png" ); } }
スーパーコンストラクターで第二引数のModelと第三引数の影サイズを設定し、 getEntityTextureをオーバーライドしてテクスチャのファイルを設定します。
以上の3つのクラスを作成して、登録すればMobが追加できると思います。まだスポーンエッグは作成していないので、sumommコマンドでMobを出現させました。
今回は自作のモデルとバニラのスケルトンを元に編集したモデルの2匹のMobを追加しました。
スポーンエッグや、自然スポーンについては次回以降に書こうと思います。