MOD製作チュートリアル > 作物の追加

概要

新しく種、作物を追加する。
テクスチャは、
  • 作物(ブロック)が成長段階によって"textures/blocks/cropAluminium_stage_0.png"から"blocks/cropAluminium_stage_7.png"。
  • 種が"textures/items/seedsAluminium.png"、作物が"textures/items/wheatAluminium.png"。

ソースコード

  • AluminiumMod.java
package tutorial.aluminiummod;
 
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
 
@Mod(modid = AluminiumMod.MOD_ID, name = AluminiumMod.MOD_NAME, version = AluminiumMod.MOD_VERSION)
public class AluminiumMod {
	public static final String MOD_ID = "AluminiumMod";
	public static final String MOD_NAME = "Aluminium Mod";
	public static final String MOD_VERSION = "1.0.0";
	public static Block cropAluminium;
	public static Item seedsAluminium;
	public static Item wheatAluminium;
 
	@EventHandler
	public void perInit(FMLPreInitializationEvent event) {
		// 作物ブロックのインスタンス生成。
		cropAluminium = new BlockAluminiumCrop().setBlockName("cropAluminium").setBlockTextureName("cropAluminium");
		GameRegistry.registerBlock(cropAluminium, "aluminium_crop");
		// 種のインスタンス生成。
		seedsAluminium = new ItemAluminiumSeeds().setUnlocalizedName("seedsAluminium").setTextureName("seedsAluminium");
		GameRegistry.registerItem(seedsAluminium, "aluminium_seeds");
		// 小麦のインスタンス生成。
		wheatAluminium = new Item().setUnlocalizedName("wheatAluminium").setTextureName("wheatAluminium").setCreativeTab(CreativeTabs.tabMaterials);
		GameRegistry.registerItem(wheatAluminium, "aluminium_wheat");
	}
}
 

  • BlockAluminiumCrop.java
+長いので囲みます
package tutorial.aluminiummod;
 
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.BlockCrops;
import net.minecraft.block.IGrowable;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.IIcon;
import net.minecraft.util.MathHelper;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.EnumPlantType;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.ForgeDirection;
 
import java.util.ArrayList;
import java.util.Random;
 
public class BlockAluminiumCrop extends BlockCrops implements IPlantable, IGrowable {
	private IIcon[] iIcons;
 
	public BlockAluminiumCrop() {
		// BlockBushのコンストラクタでMaterialはMaterial.plantsを指定されている。
		super();
		// updateTickがランダムに呼ばれるようにする。
		this.setTickRandomly(true);
		// ブロックの大きさを指定する。あたり判定やカーソルがあった時の枠の大きさに使われる。
		float f = 0.5F;
		this.setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, 0.25F, 0.5F + f);
		// クリエイティブタブに表示されないようにする。
		this.setCreativeTab(null);
		// 一瞬で破壊できるようにする。ツールを持っていても耐久値は消費しない。
		this.setHardness(0.0F);
		// 設置時や歩行時の音の種類を指定する。
		this.setStepSound(soundTypeGrass);
		// 統計にカウントされないようにする。(?)
		this.disableStats();
	}
 
	/** その座標に設置できるか。 */
	@Override
	public boolean canPlaceBlockAt(World world, int x, int y, int z) {
		return world.getBlock(x, y, z).isReplaceable(world, x, y, z) && this.canBlockStay(world, x, y, z);
		// 以下はBlockBushでの実装。superを呼び出すと二重に判定されてしまうため変更した。
		//		return super.canPlaceBlockAt(world, x, y, z) && this.canBlockStay(world, x, y, z);
	}
 
	/** そのブロックの上に設置できるか。 */
	@Override
	protected boolean canPlaceBlockOn(Block block) {
		// 耕地の上のみ。
		return block == Blocks.farmland;
	}
 
	/** 隣接ブロックが更新された時の処理。 */
	@Override
	public void onNeighborBlockChange(World world, int x, int y, int z, Block block) {
		// 二重判定回避のためコメントアウト。実際、Blockクラスでは何もしていない。
		//		super.onNeighborBlockChange(world, x, y, z, block);
		this.checkAndDropBlock(world, x, y, z);
	}
 
	/** Tick更新時の処理。 */
	@Override
	public void updateTick(World world, int x, int y, int z, Random random) {
		// ランダムに呼ばれる。
		this.checkAndDropBlock(world, x, y, z);
		// 一つ上のブロックの光源レベルが9以上の時。
		if (world.getBlockLightValue(x, y + 1, z) >= 9) {
			// メタデータを取得。
			int l = world.getBlockMetadata(x, y, z);
			// 成長限界に達していない時。
			if (l < 7) {
				// 成長しやすさを取得。
				float f = this.func_149864_n(world, x, y, z);
				// 成長させるかを判定する。
				if (random.nextInt((int) (25.0F / f) + 1) == 0) {
					// 一段階成長させる。
					++l;
					world.setBlockMetadataWithNotify(x, y, z, l, 2);
				}
			}
		}
	}
 
	/** 設置状態を維持できるかを確認し、維持できなければドロップする。 */
	@Override
	protected void checkAndDropBlock(World world, int x, int y, int z) {
		// 維持できない時。
		if (!this.canBlockStay(world, x, y, z)) {
			// ドロップする。
			this.dropBlockAsItem(world, x, y, z, world.getBlockMetadata(x, y, z), 0);
			// 空気に上書きする。
			world.setBlock(x, y, z, getBlockById(0), 0, 2);
		}
	}
 
	/** その座標で維持できるか。 */
	@Override
	public boolean canBlockStay(World world, int x, int y, int z) {
		// 下のブロックが耕地かどうかを判定する。
		return world.getBlock(x, y - 1, z).canSustainPlant(world, x, y - 1, z, ForgeDirection.UP, this);
	}
 
	/** 作物の種別を返す。 */
	@Override
	public EnumPlantType getPlantType(IBlockAccess world, int x, int y, int z) {
		// IPlantableの実装。作物。耕地の上に設置する。
		return EnumPlantType.Crop;
	}
 
	/** 作物ブロックのインスタンスを返す。 */
	@Override
	public Block getPlant(IBlockAccess world, int x, int y, int z) {
		// IPlantableの実装。
		return this;
	}
 
	/** 作物のメタデータを返す。 */
	@Override
	public int getPlantMetadata(IBlockAccess world, int x, int y, int z) {
		// IPlantableの実装。
		return world.getBlockMetadata(x, y, z);
	}
 
	/** 成長しやすさの数値を返す。 */
	private float func_149864_n(World world, int x, int y, int z) {
		// 周囲の耕地、作物の状況を判定し、成長しやすさを算出する。
		float f = 1.0F;
		Block block = world.getBlock(x, y, z - 1);
		Block block1 = world.getBlock(x, y, z + 1);
		Block block2 = world.getBlock(x - 1, y, z);
		Block block3 = world.getBlock(x + 1, y, z);
		Block block4 = world.getBlock(x - 1, y, z - 1);
		Block block5 = world.getBlock(x + 1, y, z - 1);
		Block block6 = world.getBlock(x + 1, y, z + 1);
		Block block7 = world.getBlock(x - 1, y, z + 1);
		boolean flag = block2 == this || block3 == this;
		boolean flag1 = block == this || block1 == this;
		boolean flag2 = block4 == this || block5 == this || block6 == this || block7 == this;
		for (int l = x - 1; l <= x + 1; ++l) {
			for (int i1 = z - 1; i1 <= z + 1; ++i1) {
				float f1 = 0.0F;
				if (world.getBlock(l, y - 1, i1).canSustainPlant(world, l, y - 1, i1, ForgeDirection.UP, this)) {
					f1 = 1.0F;
					if (world.getBlock(l, y - 1, i1).isFertile(world, l, y - 1, i1)) {
						f1 = 3.0F;
					}
				}
				if (l != x || i1 != z) {
					f1 /= 4.0F;
				}
				f += f1;
			}
		}
		if (flag2 || flag && flag1) {
			f /= 2.0F;
		}
		return f;
	}
 
	/** あたり判定を返す。 */
	@Override
	public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z) {
		// あたり判定をなくす。
		return null;
	}
 
	/** 不透明なブロックか。 */
	@Override
	public boolean isOpaqueCube() {
		// 透明なブロックなのでfalseを返す。
		return false;
	}
 
	/** 通常と同様に描画するか。 */
	@Override
	public boolean renderAsNormalBlock() {
		return false;
	}
 
	/** 描画の種別を返す。 */
	@Override
	public int getRenderType() {
		// 小麦などと同じ。四枚の板が上から見て「井」の形になるように配置され、そこにテクスチャが表示される。
		return 6;
	}
 
	/** 種のアイテムを返す。 */
	@Override
	protected Item func_149866_i() {
		return AluminiumMod.seedsAluminium;
	}
 
	/** 作物のアイテムを返す。 */
	@Override
	protected Item func_149865_P() {
		return AluminiumMod.wheatAluminium;
	}
 
	/** ブロックをドロップさせる。 */
	@Override
	public void dropBlockAsItemWithChance(World world, int x, int y, int z, int meta, float dropChance, int fortune) {
		super.dropBlockAsItemWithChance(world, x, y, z, meta, dropChance, fortune);
		// BlockCropで以下のようにオーバーライドされている。幸運レベルを0に固定。
		//		super.dropBlockAsItemWithChance(world, x, y, z, meta, dropChance, 0);
	}
 
	/** ドロップアイテムを返す。 */
	@Override
	public Item getItemDropped(int meta, Random random, int fortune) {
		// 基本的に種を返すが、完全成長していたら作物を返す。
		return meta == 7 ? this.func_149865_P() : this.func_149866_i();
	}
 
	/** ドロップ数を返す。 */
	@Override
	public int quantityDropped(Random random) {
		return 1;
	}
 
	/** ドロップアイテムのリストを返す。 */
	@Override
	public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int metadata, int fortune) {
		// 未成長なら種を、完全成長していたら作物が追加される。
		ArrayList<ItemStack> ret = new ArrayList<>();
		int count = quantityDropped(metadata, fortune, world.rand);
		for (int i = 0; i < count; i++) {
			Item item = getItemDropped(metadata, world.rand, fortune);
			if (item != null) {
				ret.add(new ItemStack(item, 1, damageDropped(metadata)));
			}
		}
		// 以上はBlockでの実装。以下はBlockCrops出の実装。重複処理回避のため変更した。
		//		ArrayList<ItemStack> ret = super.getDrops(world, x, y, z, metadata, fortune);
		//
		// 完全成長の時。
		if (metadata >= 7) {
			// 幸運レベルにより判定回数が増加する。デフォルトは3回。
			for (int i = 0; i < 3 + fortune; ++i) {
				// 0~14 <= 7 より、1/2の確率。
				if (world.rand.nextInt(15) <= metadata) {
					// 種を追加する。
					ret.add(new ItemStack(this.func_149866_i(), 1, 0));
				}
			}
		}
		return ret;
	}
 
	/** 対応するアイテムを返す。 */
	@Override
	@SideOnly(Side.CLIENT)
	public Item getItem(World world, int x, int y, int z) {
		// 種を返す。
		return this.func_149866_i();
	}
 
	/** ブロックのテクスチャを返す。 */
	@Override
	@SideOnly(Side.CLIENT)
	public IIcon getIcon(int side, int meta) {
		// メタデータの数値が異常だったら成長限界の値を使う。
		if (meta < 0 || meta > 7) {
			meta = 7;
		}
		return this.iIcons[meta];
	}
 
	/** ブロックのテクスチャを登録する。 */
	@Override
	@SideOnly(Side.CLIENT)
	public void registerBlockIcons(IIconRegister register) {
		this.iIcons = new IIcon[8];
		for (int i = 0; i < this.iIcons.length; ++i) {
			this.iIcons[i] = register.registerIcon(this.getTextureName() + "_stage_" + i);
		}
	}
 
	/** 骨粉を使用できるか。 */
	@Override
	public boolean func_149851_a(World world, int x, int y, int z, boolean isRemote) {
		// IGrowableの実装。完全成長していたらfalse。
		return world.getBlockMetadata(x, y, z) != 7;
	}
 
	/** 骨粉を適用するか。 */
	@Override
	public boolean func_149852_a(World world, Random random, int x, int y, int z) {
		// IGrowableの実装。
		return true;
	}
 
	/** 骨粉を適用する。 */
	@Override
	public void func_149853_b(World world, Random random, int x, int y, int z) {
		// IGrowableの実装。
		this.func_149863_m(world, x, y, z);
	}
 
	/** 骨粉を使用した時の成長させる処理。 */
	@Override
	public void func_149863_m(World world, int x, int y, int z) {
		// 成長段階を2以上5以下上昇させる。
		int l = world.getBlockMetadata(x, y, z) + MathHelper.getRandomIntegerInRange(world.rand, 2, 5);
		// 成長限界を超えていたら抑える。
		if (l > 7) {
			l = 7;
		}
		// メタデータを設定する。
		world.setBlockMetadataWithNotify(x, y, z, l, 2);
	}
}
 

  • ItemAluminiumSeeds.java
package tutorial.aluminiummod;
 
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemSeeds;
import net.minecraft.item.ItemStack;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.EnumPlantType;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.ForgeDirection;
 
public class ItemAluminiumSeeds extends ItemSeeds implements IPlantable {
	private Block field_150925_a;
	// 土台となるブロックのインスタンス。使われていない。
	//	private Block soilBlockID;
 
	public ItemAluminiumSeeds() {
		super(AluminiumMod.cropAluminium, Blocks.farmland);
		// 以下はItemSeedsのコンストラクタ。
		this.field_150925_a = AluminiumMod.cropAluminium;
		//		this.soilBlockID = Blocks.farmland;
		//		this.setCreativeTab(CreativeTabs.tabMaterials);
	}
 
	/** アイテムを使用した時の処理。 */
	@Override
	public boolean onItemUse(ItemStack itemStack, EntityPlayer player, World world, int x, int y, int z, int side, float hitX, float hitY, float hitZ) {
		if (side != 1) {
			return false;
		} else if (player.canPlayerEdit(x, y, z, side, itemStack) && player.canPlayerEdit(x, y + 1, z, side, itemStack)) {
			// 上からの使用で、プレイヤーが編集可能で、右クリックしたブロックが耕地であり、その上が空気の時。
			if (world.getBlock(x, y, z).canSustainPlant(world, x, y, z, ForgeDirection.UP, this) && world.isAirBlock(x, y + 1, z)) {
				// 作物を設置する。
				world.setBlock(x, y + 1, z, this.field_150925_a);
				// スタック数を減らす。
				--itemStack.stackSize;
				return true;
			} else {
				return false;
			}
		} else {
			return false;
		}
	}
 
	/** 作物の種別を返す。 */
	@Override
	public EnumPlantType getPlantType(IBlockAccess world, int x, int y, int z) {
		// IPlantableの実装。作物。耕地の上に設置する。
		return EnumPlantType.Crop;
	}
 
	/** 作物ブロックのインスタンスを返す。 */
	@Override
	public Block getPlant(IBlockAccess world, int x, int y, int z) {
		// IPlantableの実装。
		return field_150925_a;
	}
 
	/** 作物のメタデータを返す。 */
	@Override
	public int getPlantMetadata(IBlockAccess world, int x, int y, int z) {
		// IPlantableの実装。
		return 0;
	}
}
 

解説

Block

Block setTickRandomly(boolean needsRandomTick)

trueにすると、updateTickがランダムなタイミングで呼ばれるようになる。

void setBlockBounds(float minX, float minY, float minZ, float maxX, float maxY, float maxZ)

ブロックの大きさを設定する。
あたり判定やカーソルがあった時の枠の大きさに使われる。

Block disableStats()

統計にカウントされないようにする。(?)
正確な挙動を把握できていません。ご存知の方、ご教授いただければ幸いです。

boolean canPlaceBlockAt(World world, int x, int y, int z)

その座標に設置できるか。

void onNeighborBlockChange(World world, int x, int y, int z, Block block)

隣接ブロックが更新された時の処理。

void updateTick(World world, int x, int y, int z, Random random)

Tick更新時の処理。

boolean canBlockStay(World world, int x, int y, int z)

その座標で維持できるか。

AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z)

あたり判定を返す。
BlockのデフォルトではsetBlockBoundsの設定が適用される。

boolean isOpaqueCube()

不透明なブロックか。

boolean renderAsNormalBlock()

通常と同様に描画するか。

int getRenderType()

描画の種別を返す。

void dropBlockAsItemWithChance(World world, int x, int y, int z, int meta, float dropChance, int fortune)

ブロックをドロップさせる。

Item getItemDropped(int meta, Random random, int fortune)

ドロップアイテムを返す。

int quantityDropped(Random random)

ドロップ数を返す。

ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int metadata, int fortune)

ドロップアイテムのリストを返す。

Item getItem(World world, int x, int y, int z)

対応するアイテムを返す。
Clientのみ。第三ボタンでのピックアップに使っている。

IIcon getIcon(int side, int meta)

ブロックのテクスチャを返す。

void registerBlockIcons(IIconRegister register)

ブロックのテクスチャを登録する。

BlockBush

植物系ブロックの親クラス。

boolean canPlaceBlockOn(Block block)

そのブロックの上に設置できるか。

void checkAndDropBlock(World world, int x, int y, int z)

設置状態を維持できるかを確認し、維持できなければドロップする。

BlockCrops

小麦のクラス。
人参やジャガイモの親クラスでもある。

float func_149864_n(World world, int x, int y, int z)

成長しやすさの数値を返す。
周囲の耕地、作物の状況を判定し、成長しやすさを算出する。
privateメソッドなので、隠蔽して実装しなおしている。

Item func_149866_i()

種のアイテムを返す。

Item func_149865_P()

作物のアイテムを返す。

void func_149863_m(World world, int x, int y, int z)

骨粉を使用した時の成長させる処理。

IPlantable

植物用のインターフェース。
下のブロックの判定などに使う。
ここでは、種と作物(ブロック)が実装している。

EnumPlantType getPlantType(IBlockAccess world, int x, int y, int z)

作物の種別を返す。

Block getPlant(IBlockAccess world, int x, int y, int z)

作物ブロックのインスタンスを返す。

int getPlantMetadata(IBlockAccess world, int x, int y, int z)

作物のメタデータを返す。

IGrowable

骨粉を使用できるブロックのためのインターフェース。

boolean func_149851_a(World world, int x, int y, int z, boolean isRemote)

骨粉を使用できるか。

boolean func_149852_a(World world, Random random, int x, int y, int z)

骨粉を適用するか。

void func_149853_b(World world, Random random, int x, int y, int z)

骨粉を適用する。

使用例

オファレンMOD-[1.7.10]2.0.0で実装予定のオファレン草。
ただし、作物はオファレンの欠片で、成長段階数、成長判定、骨粉判定などは小麦と異なる予定。

コメント

この項目に関する質問などをどうぞ。
名前: