1.7.10の開発講座を修正中です。このページには誤りや古い情報が含まれる可能性があります。
MOBを追加する。
今回は、匠型のMOBを追加する。
目次
ソースコード
package tutorial.aluminiummod;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.EnumCreatureType;
import net.minecraft.item.Item;
import net.minecraft.world.biome.BiomeGenBase;
import cpw.mods.fml.client.registry.RenderingRegistry;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.EntityRegistry;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@Mod(modid = "AluminiumMod", name = "Aluminium Mod", version = "1.0.0")
public class AluminiumMod {
//追加するアイテムの宣言
public static Item aluminiumEgg;
@EventHandler
public void perInit(FMLPreInitializationEvent event) {
//Itemのインスタンスを生成し、代入する
aluminiumEgg = new ItemAluminiumEgg()
//クリエイティブタブの登録
.setCreativeTab(CreativeTabs.tabMaterials)
//システム名の登録
.setUnlocalizedName("aluminiumEgg")
//テクスチャ名の登録
.setTextureName("aluminiummod:aluminiumEgg");
//GameRegistryへの登録
GameRegistry.registerItem(aluminiumEgg, "aluminiumEgg");
}
@EventHandler
public void init(FMLInitializationEvent event) {
//Entityを登録する
EntityRegistry.registerModEntity(EntityAluminiumCreeper.class, "AluminiumCreeper", 0, this, 250, 1, false);
//Entityの自然スポーンを登録する
EntityRegistry.addSpawn(EntityAluminiumCreeper.class, 20, 1, 4, EnumCreatureType.creature, BiomeGenBase.plains);
/*EntityのRenderを登録する
*Client側でのみ登録するため、今回はif文で処理をする。*/
if(FMLCommonHandler.instance().getSide() == Side.CLIENT) {
this.render();
}
}
@SideOnly(Side.CLIENT)
public void render()
{
RenderingRegistry.registerEntityRenderingHandler(EntityAluminiumCreeper.class, new RenderAluminiumCreeper());
}
}
package tutorial.aluminiummod;
import net.minecraft.block.material.Material;
import net.minecraft.entity.EnumCreatureAttribute;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.EntityAIAttackOnCollide;
import net.minecraft.entity.ai.EntityAIAvoidEntity;
import net.minecraft.entity.ai.EntityAIHurtByTarget;
import net.minecraft.entity.ai.EntityAILookIdle;
import net.minecraft.entity.ai.EntityAINearestAttackableTarget;
import net.minecraft.entity.ai.EntityAISwimming;
import net.minecraft.entity.ai.EntityAIWander;
import net.minecraft.entity.ai.EntityAIWatchClosest;
import net.minecraft.entity.monster.EntityMob;
import net.minecraft.entity.passive.EntityOcelot;
import net.minecraft.entity.passive.EntityPig;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.stats.AchievementList;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.DamageSource;
import net.minecraft.world.World;
public class EntityAluminiumCreeper extends EntityMob
{
public EntityAluminiumCreeper(World world) {
super(world);
/*EntiyのAIを登録する*/
this.tasks.addTask(1, new EntityAISwimming(this));
this.tasks.addTask(2, new EntityAIAttackOnCollide(this, EntityPig.class, 1.0D, false));
this.tasks.addTask(2, new EntityAIAvoidEntity(this, EntityOcelot.class, 6.0F, 1.0D, 1.2D));
this.tasks.addTask(3, new EntityAIAttackOnCollide(this, EntityPlayer.class, 1.0D, false));
this.tasks.addTask(4, new EntityAIWander(this, 0.8D));
this.tasks.addTask(5, new EntityAIWatchClosest(this, EntityPlayer.class, 8.0F));
this.tasks.addTask(6, new EntityAILookIdle(this));
this.targetTasks.addTask(1, new EntityAINearestAttackableTarget(this, EntityPig.class, 1,false));
this.targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, EntityPlayer.class, 1, true));
this.targetTasks.addTask(2, new EntityAIHurtByTarget(this, false));
}
/**MOBの速度やHPを変更するメソッド*/
@Override
protected void applyEntityAttributes()
{
super.applyEntityAttributes();
this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.5D);
this.getEntityAttribute(SharedMonsterAttributes.followRange).setBaseValue(128D);
this.getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValue(100D);
this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(1.5D);
}
/**MOBの属性を返すメソッド*/
@Override
public EnumCreatureAttribute getCreatureAttribute() {
return EnumCreatureAttribute.UNDEFINED;
}
/**MOBのドロップアイテムを返すメソッド*/
@Override
public Item getDropItem() {
return Item.getItemFromBlock(Blocks.wool) ;
}
/**MOBのドロップアイテムをドロップさせるメソッド*/
@Override
protected void dropFewItems(boolean isCanDropRare, int fortuneLv){
Item item = this.getDropItem();
int amount = 1;
if(isCanDropRare && this.rand.nextBoolean())
{
item = Item.getItemFromBlock(Blocks.stained_glass);
}
if(fortuneLv > 0)
{
amount += fortuneLv;
}
int damage = this.rand.nextInt(16);
for(int i = 0; i < amount; i++)
{
this.entityDropItem(new ItemStack(item, amount, damage), 0.0F);
}
}
/**Tickごとに呼ばれるメソッド*/
@Override
public void onUpdate()
{
int x = (int) this.posX;
int y = (int) this.posY;
int z = (int) this.posZ;
if(this.worldObj.getBlock(x, y - 1, z).getMaterial() == Material.iron)
{
this.worldObj.createExplosion(this, x, y, z, 3F, true);
}
super.onUpdate();
}
/**MOB死亡時に呼ばれるメソッド*/
public void onDeath(DamageSource source)
{
super.onDeath(source);
if(source.getSourceOfDamage() != null && source.getSourceOfDamage() instanceof EntityPlayer)
{
EntityPlayer player = (EntityPlayer) source.getSourceOfDamage();
if(!this.worldObj.isRemote)
{
player.addChatMessage(new ChatComponentText("You slayed."));
player.triggerAchievement(AchievementList.mineWood);
}
}
}
/**ダメージを食らうか否かを判定するメソッド*/
@Override
public boolean attackEntityFrom(DamageSource source, float damage)
{
if(source.isExplosion())
{
return false;
}
else if(source.isFireDamage())
{
return super.attackEntityFrom(source, damage * 2);
}
else
{
return super.attackEntityFrom(source, damage);
}
}
}
package tutorial.aluminiummod;
import net.minecraft.client.renderer.entity.RenderLiving;
import net.minecraft.entity.Entity;
import net.minecraft.util.ResourceLocation;
public class RenderAluminiumCreeper extends RenderLiving
{
public RenderAluminiumCreeper() {
super(new ModelAluminiumCreeper() , 0.5F);
}
/**テクスチャを登録するメソッド*/
@Override
protected ResourceLocation getEntityTexture(Entity entity) {
return new ResourceLocation("aluminiummod:textures/mobs/aluminium_creeper.png");
}
}
package tutorial.aluminiummod;
import net.minecraft.client.model.ModelBase;
import net.minecraft.client.model.ModelRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.util.MathHelper;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
@SideOnly(Side.CLIENT)
public class ModelAluminiumCreeper extends ModelBase
{
public ModelRenderer head;
public ModelRenderer head2;
public ModelRenderer body;
public ModelRenderer leg1;
public ModelRenderer leg2;
public ModelRenderer leg3;
public ModelRenderer leg4;
public ModelAluminiumCreeper()
{
this(0.0F);
}
/**Modelを登録するメソッド*/
public ModelAluminiumCreeper(float size)
{
byte b0 = 4;
this.head = new ModelRenderer(this, 0, 0);
this.head.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, size);
this.head.setRotationPoint(0.0F, (float)b0, 0.0F);
this.head2 = new ModelRenderer(this, 32, 0);
this.head2.addBox(-4.0F, -8.0F, -4.0F, 8, 8, 8, size + 0.5F);
this.head2.setRotationPoint(0.0F, (float)b0, 0.0F);
this.body = new ModelRenderer(this, 16, 16);
this.body.addBox(-4.0F, 0.0F, -2.0F, 8, 12, 4, size);
this.body.setRotationPoint(0.0F, (float)b0, 0.0F);
this.leg1 = new ModelRenderer(this, 0, 16);
this.leg1.addBox(-2.0F, 0.0F, -2.0F, 4, 6, 4, size);
this.leg1.setRotationPoint(-2.0F, (float)(12 + b0), 4.0F);
this.leg2 = new ModelRenderer(this, 0, 16);
this.leg2.addBox(-2.0F, 0.0F, -2.0F, 4, 6, 4, size);
this.leg2.setRotationPoint(2.0F, (float)(12 + b0), 4.0F);
this.leg3 = new ModelRenderer(this, 0, 16);
this.leg3.addBox(-2.0F, 0.0F, -2.0F, 4, 6, 4, size);
this.leg3.setRotationPoint(-2.0F, (float)(12 + b0), -4.0F);
this.leg4 = new ModelRenderer(this, 0, 16);
this.leg4.addBox(-2.0F, 0.0F, -2.0F, 4, 6, 4, size);
this.leg4.setRotationPoint(2.0F, (float)(12 + b0), -4.0F);
}
/**Modelをレンダリングするメソッド*/
public void render(Entity entity, float f1, float f2, float f3, float yaw, float pitch, float size)
{
this.setRotationAngles(f1, f2, f3, yaw, pitch, size, entity);
this.head.render(size);
this.body.render(size);
this.leg1.render(size);
this.leg2.render(size);
this.leg3.render(size);
this.leg4.render(size);
}
/**頭、足、腕などの角度を設定するメソッド*/
public void setRotationAngles(float f1, float f2, float f3, float yaw, float pitch, float size, Entity entity)
{
this.head.rotateAngleY = yaw / (180F / (float)Math.PI);
this.head.rotateAngleX = pitch / (180F / (float)Math.PI);
this.leg1.rotateAngleX = MathHelper.cos(f1 * 0.6662F) * 1.4F * f2;
this.leg2.rotateAngleX = MathHelper.cos(f1 * 0.6662F + (float)Math.PI) * 1.4F * f2;
this.leg3.rotateAngleX = MathHelper.cos(f1 * 0.6662F + (float)Math.PI) * 1.4F * f2;
this.leg4.rotateAngleX = MathHelper.cos(f1 * 0.6662F) * 1.4F * f2;
}
}
package tutorial.aluminiummod;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLiquid;
import net.minecraft.entity.Entity;
import net.minecraft.entity.IEntityLivingData;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Facing;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.world.World;
/**このクラスは、net.minecraft.Item.ItemMonsterPlacerをもとに作成しています。*/
public class ItemAluminiumEgg extends Item
{
/**アイテムでブロックを右クリックしたのメソッド。ItemMonsterPlacer参照。*/
@Override
public boolean onItemUse(ItemStack itemStack, EntityPlayer player, World world, int x, int y, int z, int side, float posX, float posY, float posZ)
{
//サーバー側の場合は処理をスキップする
if (world.isRemote)
{
return true;
}
else
{
Block block = world.getBlock(x, y, z);
x += Facing.offsetsXForSide[side];
y += Facing.offsetsYForSide[side];
z += Facing.offsetsZForSide[side];
double height = 0.0D;
if (side == 1 && block.getRenderType() == 11)
{
height = 0.5D;
}
Entity entity = spawnEntity(world, (double)x + 0.5D, (double)y + height, (double)z + 0.5D);
if (entity != null)
{
if (!player.capabilities.isCreativeMode)
{
--itemStack.stackSize;
}
}
return true;
}
}
/**アイテムを使ったときのメソッド。ItemMonsterPlacer参照。*/
@Override
public ItemStack onItemRightClick(ItemStack itemStack, World world, EntityPlayer player)
{
//サーバー側の場合は処理をスキップする
if (world.isRemote)
{
return itemStack;
}
else
{
MovingObjectPosition movingobjectposition = this.getMovingObjectPositionFromPlayer(world, player, true);
if (movingobjectposition == null)
{
return itemStack;
}
else
{
if (movingobjectposition.typeOfHit == MovingObjectPosition.MovingObjectType.BLOCK)
{
int x = movingobjectposition.blockX;
int y = movingobjectposition.blockY;
int z = movingobjectposition.blockZ;
if (!world.canMineBlock(player, x, y, z))
{
return itemStack;
}
if (!player.canPlayerEdit(x, y, z, movingobjectposition.sideHit, itemStack))
{
return itemStack;
}
if (world.getBlock(x, y, z) instanceof BlockLiquid)
{
Entity entity = spawnEntity(world, (double)x, (double)y, (double)z);
if (entity != null)
{
if (!player.capabilities.isCreativeMode)
{
--itemStack.stackSize;
}
}
}
}
return itemStack;
}
}
}
/**Mobをスポーンさせるメソッド*/
public Entity spawnEntity(World world, double x, double y, double z)
{
EntityAluminiumCreeper entityliving = new EntityAluminiumCreeper(world);
entityliving.setLocationAndAngles(x, y, z, MathHelper.wrapAngleTo180_float(world.rand.nextFloat() * 360.0F), 0.0F);
entityliving.rotationYawHead = entityliving.rotationYaw;
entityliving.renderYawOffset = entityliving.rotationYaw;
entityliving.onSpawnWithEgg((IEntityLivingData)null);
world.spawnEntityInWorld(entityliving);
entityliving.playLivingSound();
return entityliving;
}
}
解説
AluminiumMod
void registerModEntity(Class<? extends Entity> entityClass, String entityName, int id, Object mod, int trackingRange, int updateFrequency, boolean sendsVelocityUpdates)
Mod用のMOBを追加するメソッド。
引数は、以下の通り。
引数 | 内容 |
---|---|
entityClass | 追加するEntityをClass型で渡す。 |
entityName | 追加するEntityの名称。langファイルで、entity.[MODIDで登録した名称].[entityNameで登録した名称].name=[ローカル名]でローカル名を登録できる。 |
id | 追加するEntityのID。同一MOD内で被るとエラーを吐く。 |
mod | MODを渡す。thisで渡せばOK。 |
trackingRange | MOBの更新範囲。 |
updateFrequency | MOBの更新頻度。何tickごとにMOBを更新するかの値。基本的に2以下の値を渡す。 |
sendsVelocityUpdates | MOBの更新時に加速度の情報を更新させるか否か。MOBや動物の場合は基本的にtrueを渡す。 |
void addSpawn(Class<? extends EntityLiving> entityClass, int weightedProb, int min, int max, EnumCreatureType typeOfCreature, BiomeGenBase... biomes)
Entityの自然スポーンを追加するメソッド。
引数は、以下の通り。
引数 | 内容 |
---|---|
entityClass | スポーンを追加させるEntityをClass型で渡す。 |
weightProb | Entityのスポーンがどれくらいの頻度で抽選されるかの値。値が大きいほうがスポーンしやすい。 |
min | Entityがスポーンする際の最低数。 |
max | Entityがスポーンする際の最高数。 |
typeOfCreature | Entityのスポーンタイプ。 |
EnumCreatureType一覧
- monster...敵性MOBの属性。夜にスポーンする。
- creature...友好MOB(とオオカミ、ヤマネコ)の属性。昼にスポーンする。
- ambient...コウモリの属性。
- waterCreature...イカの属性。これのみ、スポーン場所が空気中でなく水中になる。
- biomes Entityがスポーンするバイオーム。複数バイオームを渡す場合は、先にfinal定数でリストを作ってからそのリストをそのまま引数に渡した方がいい。
void registerEntityRenderingHandler(Class<? extends Entity> entityClass, Render renderer)
Entityのレンダーを追加するメソッド。
Client限定の処理であり、Serverで処理してしまうとクラッシュの原因になるため、必ずif文とSideOnlyアノテーションを付けたメソッドで処理をする。
もしくは、proxyで分化させる手もある。
第一引数には登録EntityをClass型で、第二引数は登録Renderをインスタンス型で渡す。
EntityAluminiumCreeper
tasks.addTask(int p_75776_1_, EntityAIBase p_75776_2_)
Entityに攻撃、移動AIを追加するメソッド。
ターゲットAIは後述のtargetTasks.addTaskを用いる。
第一引数はAIの順位(小さいほうが先に行動される。)、第二引数がAI。
主なAI
EntityAISwimming(EntityLiving p_i1624_1_)
水を泳ぐAI。
第一引数にはthisを渡す。
EntityAIAttackOnCollide(EntityCreature p_i1635_1_, Class p_i1635_2_, double p_i1635_3_, boolean p_i1635_5_)
特定のEntityを襲うAI。
第一引数にはthis、第二引数には襲う対象のEntityをクラス型で、第三引数には自分の通常移動速度に対する倍率、第四引数には追い続けるか否かの判定を渡す。
第三引数が1より大きいと、通常の移動スピードより高速で襲ってくることになる。
EntityAIAvoidEntity(EntityCreature p_i1616_1_, Class p_i1616_2_, float p_i1616_3_, double p_i1616_4_, double p_i1616_6_)
特定のEntityから逃げるAI。
第一引数にはthis、第二引数には逃げる対象のEntityをクラス型で、第三引数には対象からどれだけ逃げるかの距離、第四引数には対象と十分離れたときの逃げる速度の倍率、第五引数には対象と近いときの逃げる速度の倍率を渡す。
第四引数 < 第五引数となれば、対象と近いときはより急いで逃げるようになる。
EntityAIWander(EntityCreature p_i1648_1_, double p_i1648_2_)
うろうろするAI。
第一引数にはthis、第二引数には速度の倍率を渡す。
第二引数は、基本的に1より小さい値を渡す。
EntityAIWatchClosest(EntityLiving p_i1631_1_, Class p_i1631_2_, float p_i1631_3_)
特定のEntityを睨むAI。
第一引数にはthis、第二引数には睨む対象のEntityをクラス型で、第三引数は対象との距離を渡す。
第三引数が大きいと、対象が離れていても視認する。
EntityAILookIdle(EntityLiving p_i1647_1_)
きょろきょろするAI。thisを渡す。
targetTasks.addTask(int p_75776_1_, EntityAIBase p_75776_2_)
EntityにターゲットAIを追加するメソッド。
第一引数はAIの順位(小さいほうが先に行動される。)、第二引数がAI。
主なAI
EntityAINearestAttackableTarget(EntityCreature p_i1663_1_, Class p_i1663_2_, int p_i1663_3_, boolean p_i1663_4_)
特定のEntityをターゲッティングするAI。
第一引数にはthis、第二引数には対象のEntityをクラス型で、第三引数には行動しない確率(0だと攻撃できる場所に入れば必ずターゲッティングされる)、第四引数にはブロックに囲まれていてもターゲッティングするか否かを渡す。
第四引数をtrueにすると、ゾンビのように対象がブロックに囲まれていても、視認できれば攻撃しようとする。
EntityAIHurtByTarget(EntityCreature p_i1660_1_, boolean p_i1660_2_)
攻撃したEntityに反撃するAI。
第一引数にはthis、第二引数には反撃する際周囲の同種のEntityに助けを求めるか否かを渡す。
第二引数をtrueにすると、第一引数のEntityを攻撃したEntityは、第一引数と同種の周囲のEntityにも狙われる。ゾンビやゾンビピッグマンのような行動をする。
EntityLivingBase.getEntityAttribute(IAttribute p_110148_1_).setBaseValue(double p_111128_1_)
Entityに対してHPやスピード等の数値を追加するメソッド。
getEntityAttributeに追加する数値の種類、setBaseValueにその値を渡す。
引数一覧 すべてSharedMonsterAttributesのstatic定数である。
定数名 | 説明 | 既定値 |
---|---|---|
movementSpeed | 移動速度 | 0.7(ただし、バニラのMOBは基本的に0.25) |
followRange | 追跡距離 | 32 |
knockbackResistance | ノックバック耐性 | 0 |
maxHealth | 体力 | 20 |
RenderAluminiumCreeper
ResourceLocation(String p_i1293_1_)
Entityなどでテクスチャを指定する際につかうコンストラクター。
"aluminiummod:textures/mobs/aluminium_creeper.png"のように、ファイルと修飾子まで入れる必要がある。
また、テクスチャを実装する場合は、今回のものはモデルをクリーパーのものを流用したため、クリーパーのテクスチャをaluminiummod/mobs/に放り込んでaluminium_creeperと名前を付ければよい。
ItemAluminiumEgg
spawnEntityInWorld(Entity p_72838_1_)
Entityをワールドにスポーンさせるメソッド。
ただし、setLocationAndAnglesなどで先にスポーン位置を指定する必要がある。
Tips
クリーパーに因んでEntityを光らせる方法と爆発を発生させる方法を説明する。
Entityを光らせる
以下のコードを追加する。
@SideOnly(Side.CLIENT)
public int getBrightnessForRender(float p_70070_1_) {
return 15728880;
}
public float getBrightness(float p_70013_1_) {
return 50.0F;
}
getBrightnessForRenderが描画側、getBrightnessが計算側に明るさを渡す。数値はこれらが最大値である。
爆発を発生させる
爆発を発生させる方法は二種類ある。どちらもWorldクラスに属する。
createExplosion(Entity entity, double x, double y, double z, float power, boolean destroyBlocks)
引数は発生Entity(このEntityは爆発によるダメージ、処理から除外されることになる)、座標(double三つ)、威力(大体土ブロックでの爆発半径に等しい。威力15以上は何故か正方形状に削れてしまう上範囲がほぼ広がらないため別処理の実装が望ましい。匠CraftのEntityGiantCreeper参照。)、ブロックを破壊するか否か、となっている。
newExplosion(Entity entity, double x, double y, double z, float power, boolean destroyBlocks, boolean spreadFire)
最終引数以外上記と一緒。最終引数は炎を撒き散らすかどうか。ファイアボールのような処理にしか使わない。なお、上のcreateExplosionはこの関数にfalseを渡しているだけである。
また、この二つの関数はExplosion型を返すため、イベントの追加にある爆発のフック処理をこの爆発限定で行うことも出来る。
#実際のコード
匠Craft内で実際にMOBを実装している。
匠Craft
あ、自己解決しました。
getEquipmentInSlot(int p_71124_1_)...
MOBに剣等を持たせるメソッドってどんな感じですかね?
ドロップアイテムの種類ってどうやって増やすんですかね?