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

概要

新しく苗木・原木・葉を追加し、苗木が育ったら木が生成されるようにする。
今回は一つのIDで一種類の木しか追加していないが、バニラの木のようにメタデータを利用して何種類かの木を追加することも可能。
※5/22更新
葉のテクスチャの指定ができていなかったため、追記・修正しました。
テクスチャは、sapling_aluminium,log_aluminium_top,log_aluminium_side,leaves_aluminium,leaves_aluminium_opaqueでそれぞれ指定できます。

ソースコード

  • 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.minecraftforge.oredict.OreDictionary;
 
@Mod(modid = AluminiumMod.MODID, name = AluminiumMod.MODNAME, version = AluminiumMod.VERSION)
public class AluminiumMod {
 
	public static final String MODID = "AluminiumMod";
	public static final String MODNAME = "Aluminium Mod";
	public static final String VERSION = "1.0.0";
 
	public static Block saplingAluminium;
	public static Block logAluminium;
	public static Block leavesAluminium;
 
	@EventHandler
	public void perInit(FMLPreInitializationEvent event) {
		saplingAluminium = new BlockAluminiumSapling()
				.setBlockName("saplingAluminium")
				.setBlockTextureName("aluminiummod:sapling_aluminium");
		GameRegistry.registerBlock(saplingAluminium, "saplingAluminium");
		OreDictionary.registerOre("saplingAluminium", saplingAluminium);
 
		logAluminium = new BlockAluminiumLog()
				.setBlockName("logAluminium")
				.setBlockTextureName("aluminiummod:log_aluminium");
		GameRegistry.registerBlock(logAluminium, "logAluminium");
		OreDictionary.registerOre("logAluminium", logAluminium);
 
		leavesAluminium = new BlockAluminiumLeaves()
				.setBlockName("leavesAluminium")
				.setBlockTextureName("aluminiummod:leaves_aluminium");
		GameRegistry.registerBlock(leavesAluminium, ItemAluminiumLeaves.class, "leavesAluminium");
		OreDictionary.registerOre("leavesAluminium", leavesAluminium);
	}
 
}
 

  • BlockAluminiumSapling.java
+ 長いので囲みます
package tutorial.aluminiummod;
 
import static net.minecraftforge.common.EnumPlantType.*;
 
import java.util.List;
import java.util.Random;
 
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.BlockSapling;
import net.minecraft.block.IGrowable;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
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.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenerator;
import net.minecraftforge.common.EnumPlantType;
import net.minecraftforge.common.IPlantable;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.terraingen.TerrainGen;
 
public class BlockAluminiumSapling extends BlockSapling implements IPlantable, IGrowable {
 
	public BlockAluminiumSapling() {
		super();
		this.setCreativeTab(CreativeTabs.tabDecorations);
		this.setTickRandomly(true);
		this.setHardness(0F);
		this.setStepSound(soundTypeGrass);
		float f = 0.4F;
		this.setBlockBounds(0.5F - f, 0.0F, 0.5F - f, 0.5F + f, f * 2.0F, 0.5F + f);
	}
 
	@Override
	public boolean canPlaceBlockAt(World world, int x, int y, int z) {
		// Blockで上書き可能なブロックかどうかの判定をしているが、BlockBushでcanBlockStayの判定を追加している。
		return world.getBlock(x, y, z).isReplaceable(world, x, y, z) && this.canBlockStay(world, x, y, z);
	}
 
	@Override
	protected boolean canPlaceBlockOn(Block block) {
		// 草、土、耕された土ならtrueを返す。
		return block == Blocks.grass || block == Blocks.dirt || block == Blocks.farmland;
	}
 
	@Override
	public void onNeighborBlockChange(World world, int x, int y, int z, Block block) {
		this.checkAndDropBlock(world, x, y, z);
	}
 
	@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.setBlockToAir(x, y, z);
		}
	}
 
	@Override
	public boolean canBlockStay(World world, int x, int y, int z) {
		// 下のブロックのcanSustainPlantで判定している。
		return world.getBlock(x, y - 1, z).canSustainPlant(world, x, y - 1, z, ForgeDirection.UP, this);
	}
 
	@Override
	public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int x, int y, int z) {
		// 当たり判定を消すため、nullを返している。
		return null;
	}
 
	@Override
	public boolean isOpaqueCube() {
		return false;
	}
 
	@Override
	public boolean renderAsNormalBlock() {
		return false;
	}
 
	@Override
	public int getRenderType() {
		return 1;
	}
 
	@Override
	public EnumPlantType getPlantType(IBlockAccess world, int x, int y, int z) {
		return Plains;
	}
 
	@Override
	public Block getPlant(IBlockAccess world, int x, int y, int z) {
		return this;
	}
 
	@Override
	public int getPlantMetadata(IBlockAccess world, int x, int y, int z) {
		return world.getBlockMetadata(x, y, z);
	}
 
	@Override
	public void updateTick(World world, int x, int y, int z, Random random) {
		// サーバー側で、checkAndDropBlock・明るさの判定を行い、条件を満たしていれば1/7の確率で成長する。
		if (!world.isRemote) {
			this.checkAndDropBlock(world, x, y, z);
			if (world.getBlockLightValue(x, y + 1, z) >= 9 && random.nextInt(7) == 0) {
				this.func_149879_c(world, x, y, z, random);
			}
		}
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public IIcon getIcon(int side, int meta) {
		// Blockと同様。
		return blockIcon;
	}
 
	@Override
	public void func_149879_c(World world, int x, int y, int z, Random random) {
		// 一度も成長していないなら一段階成長させ、二段階目なら木を生成する。
		// 一段階目の成長が終わったかどうかは、メタデータの二進数四桁目で判断する。
		int meta = world.getBlockMetadata(x, y, z);
		if ((meta & 8) == 0) {
			world.setBlockMetadataWithNotify(x, y, z, meta | 8, 4);
		} else {
			this.func_149878_d(world, x, y, z, random);
		}
	}
 
	@Override
	public void func_149878_d(World world, int x, int y, int z, Random random) {
		// Eventを呼び出しているが、他MODでキャンセルされたくなければこの部分は削除してよい。
		if (!TerrainGen.saplingGrowTree(world, random, x, y, z))
			return;
		// メタデータを利用して複数種類の木を追加したい場合はバニラの苗木を参考にするとよい。
		int meta = 0;
		// 大木を生成したい場合はこの部分を参考にするとよい。
		// Object object = random.nextInt(10) == 0 ? new WorldGenBigTree(true) : new WorldGenTrees(true);
		Object object = new WorldGenAluminiumTrees(true);
		world.setBlock(x, y, z, Blocks.air, 0, 4);
		if (!((WorldGenerator) object).generate(world, random, x, y, z)) {
			world.setBlock(x, y, z, this, meta, 4);
		}
	}
 
	@Override
	public boolean func_149880_a(World world, int x, int y, int z, int type) {
		return world.getBlock(x, y, z) == this && (world.getBlockMetadata(x, y, z) & 7) == type;
	}
 
	@Override
	public int damageDropped(int meta) {
		// Blockと同様。
		return 0;
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public void getSubBlocks(Item item, CreativeTabs tab, List list) {
		// Blockと同様。
		list.add(new ItemStack(item, 1, 0));
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public void registerBlockIcons(IIconRegister register) {
		// Blockと同様。
		blockIcon = register.registerIcon(this.getTextureName());
	}
 
	@Override
	public boolean func_149851_a(World world, int x, int y, int z, boolean isRemote) {
		return true;
	}
 
	@Override
	public boolean func_149852_a(World world, Random random, int x, int y, int z) {
		return world.rand.nextFloat() < 0.45D;
	}
 
	@Override
	public void func_149853_b(World world, Random random, int x, int y, int z) {
		this.func_149879_c(world, x, y, z, random);
	}
}
 

  • WorldGenAluminiumTrees.java
+ 長いので囲みます
package tutorial.aluminiummod;
 
import java.util.Random;
 
import net.minecraft.block.Block;
import net.minecraft.block.BlockSapling;
import net.minecraft.block.material.Material;
import net.minecraft.init.Blocks;
import net.minecraft.util.Direction;
import net.minecraft.world.World;
import net.minecraft.world.gen.feature.WorldGenAbstractTree;
import net.minecraftforge.common.util.ForgeDirection;
 
public class WorldGenAluminiumTrees extends WorldGenAbstractTree {
 
	private final int minTreeHeight;
	private final boolean vinesGrow;
	private final int metaWood;
	private final int metaLeaves;
 
	public WorldGenAluminiumTrees(boolean doBlockNotify) {
		this(doBlockNotify, 4, 0, 0, false);
	}
 
	public WorldGenAluminiumTrees(boolean doBlockNotify, int minTreeHeight, int metaWood, int metaLeaves, boolean vinesGrow) {
		super(doBlockNotify);
		this.minTreeHeight = minTreeHeight;
		this.metaWood = metaWood;
		this.metaLeaves = metaLeaves;
		this.vinesGrow = vinesGrow;
	}
 
	@Override
	public boolean generate(World world, Random random, int x, int y, int z) {
		int l = random.nextInt(3) + minTreeHeight;
 
		if (y < 1 || y + l + 1 > 256)
			return false;
		byte b0;
		int k1;
		Block block;
 
		for (int iy = y; iy <= y + 1 + l; ++iy) {
			b0 = 1;
 
			if (iy == y) {
				b0 = 0;
			}
 
			if (iy >= y + 1 + l - 2) {
				b0 = 2;
			}
 
			for (int ix = x - b0; ix <= x + b0; ++ix) {
				for (int iz = z - b0; iz <= z + b0; ++iz) {
					if (iy < 0 || iy >= 256)
						return false;
					block = world.getBlock(ix, iy, iz);
					if (!this.isReplaceable(world, ix, iy, iz))
						return false;
				}
			}
		}
 
		Block block2 = world.getBlock(x, y - 1, z);
 
		boolean isSoil = block2.canSustainPlant(world, x, y - 1, z, ForgeDirection.UP, (BlockSapling) AluminiumMod.saplingAluminium);
		if (!isSoil || y >= 256 - l - 1)
			return false;
		block2.onPlantGrow(world, x, y - 1, z, x, y, z);
		b0 = 3;
		byte b1 = 0;
		int l1;
		int i2;
		int j2;
		int i3;
 
		for (k1 = y - b0 + l; k1 <= y + l; ++k1) {
			i3 = k1 - (y + l);
			l1 = b1 + 1 - i3 / 2;
 
			for (i2 = x - l1; i2 <= x + l1; ++i2) {
				j2 = i2 - x;
 
				for (int k2 = z - l1; k2 <= z + l1; ++k2) {
					int l2 = k2 - z;
 
					if (Math.abs(j2) != l1 || Math.abs(l2) != l1 || random.nextInt(2) != 0 && i3 != 0) {
						Block block1 = world.getBlock(i2, k1, k2);
 
						if (block1.isAir(world, i2, k1, k2) || block1.isLeaves(world, i2, k1, k2)) {
							this.setBlockAndNotifyAdequately(world, i2, k1, k2, AluminiumMod.leavesAluminium, metaLeaves);
						}
					}
				}
			}
		}
 
		for (k1 = 0; k1 < l; ++k1) {
			block = world.getBlock(x, y + k1, z);
 
			if (block.isAir(world, x, y + k1, z) || block.isLeaves(world, x, y + k1, z)) {
				setBlockAndNotifyAdequately(world, x, y + k1, z, AluminiumMod.logAluminium, metaWood);
 
				if (vinesGrow && k1 > 0) {
					if (random.nextInt(3) > 0 && world.isAirBlock(x - 1, y + k1, z)) {
						this.setBlockAndNotifyAdequately(world, x - 1, y + k1, z, Blocks.vine, 8);
					}
 
					if (random.nextInt(3) > 0 && world.isAirBlock(x + 1, y + k1, z)) {
						this.setBlockAndNotifyAdequately(world, x + 1, y + k1, z, Blocks.vine, 2);
					}
 
					if (random.nextInt(3) > 0 && world.isAirBlock(x, y + k1, z - 1)) {
						this.setBlockAndNotifyAdequately(world, x, y + k1, z - 1, Blocks.vine, 1);
					}
 
					if (random.nextInt(3) > 0 && world.isAirBlock(x, y + k1, z + 1)) {
						this.setBlockAndNotifyAdequately(world, x, y + k1, z + 1, Blocks.vine, 4);
					}
				}
			}
		}
 
		if (vinesGrow) {
			for (k1 = y - 3 + l; k1 <= y + l; ++k1) {
				i3 = k1 - (y + l);
				l1 = 2 - i3 / 2;
 
				for (i2 = x - l1; i2 <= x + l1; ++i2) {
					for (j2 = z - l1; j2 <= z + l1; ++j2) {
						if (world.getBlock(i2, k1, j2).isLeaves(world, i2, k1, j2)) {
							if (random.nextInt(4) == 0 && world.getBlock(i2 - 1, k1, j2).isAir(world, i2 - 1, k1, j2)) {
								this.growVines(world, i2 - 1, k1, j2, 8);
							}
 
							if (random.nextInt(4) == 0 && world.getBlock(i2 + 1, k1, j2).isAir(world, i2 + 1, k1, j2)) {
								this.growVines(world, i2 + 1, k1, j2, 2);
							}
 
							if (random.nextInt(4) == 0 && world.getBlock(i2, k1, j2 - 1).isAir(world, i2, k1, j2 - 1)) {
								this.growVines(world, i2, k1, j2 - 1, 1);
							}
 
							if (random.nextInt(4) == 0 && world.getBlock(i2, k1, j2 + 1).isAir(world, i2, k1, j2 + 1)) {
								this.growVines(world, i2, k1, j2 + 1, 4);
							}
						}
					}
				}
			}
 
			if (random.nextInt(5) == 0 && l > 5) {
				for (k1 = 0; k1 < 2; ++k1) {
					for (i3 = 0; i3 < 4; ++i3) {
						if (random.nextInt(4 - k1) == 0) {
							l1 = random.nextInt(3);
							this.setBlockAndNotifyAdequately(world, x + Direction.offsetX[Direction.rotateOpposite[i3]], y + l - 5 + k1, z + Direction.offsetZ[Direction.rotateOpposite[i3]], Blocks.cocoa, l1 << 2 | i3);
						}
					}
				}
			}
		}
 
		return true;
	}
 
	@Override
	protected boolean isReplaceable(World world, int x, int y, int z) {
		Block block = world.getBlock(x, y, z);
		return block.isAir(world, x, y, z) || block.isLeaves(world, x, y, z) || block.isWood(world, x, y, z) || this.func_150523_a(block);
	}
 
	@Override
	protected boolean func_150523_a(Block block) {
		return block.getMaterial() == Material.air || block.getMaterial() == Material.leaves || block == Blocks.grass || block == Blocks.dirt || block == Blocks.log || block == Blocks.log2 || block == Blocks.sapling || block == Blocks.vine;
	}
 
	private void growVines(World world, int x, int y, int z, int length) {
		this.setBlockAndNotifyAdequately(world, x, y, z, Blocks.vine, length);
		int i1 = 4;
 
		while (true) {
			--y;
 
			if (!world.getBlock(x, y, z).isAir(world, x, y, z) || i1 <= 0) {
				return;
			}
 
			this.setBlockAndNotifyAdequately(world, x, y, z, Blocks.vine, length);
			--i1;
		}
	}
 
}
 

  • BlockAluminiumLog.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.BlockLog;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
 
public class BlockAluminiumLog extends BlockLog {
 
	private IIcon[] iicon = new IIcon[2];
 
	public BlockAluminiumLog() {
		super();
		this.setCreativeTab(CreativeTabs.tabBlock);
		this.setHardness(2.0F);
		this.setStepSound(soundTypeWood);
	}
 
	@Override
	public int getRenderType() {
		return 31;
	}
 
	@Override
	public int onBlockPlaced(World world, int x, int y, int z, int side, float posX, float posY, float posZ, int meta) {
		// 設置された方向に応じてメタデータを設定する。
		int metaType = meta & 3;
		byte direction = 0;
 
		switch (side) {
		case 0:
		case 1:
			direction = 0;
			break;
		case 2:
		case 3:
			direction = 8;
			break;
		case 4:
		case 5:
			direction = 4;
		}
 
		return metaType | direction;
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public IIcon getIcon(int side, int meta) {
		int k = meta & 12;
		int l = meta & 3;
		return k == 0 && (side == 1 || side == 0) ? this.getTopIcon(l) : (k == 4 && (side == 5 || side == 4) ? this.getTopIcon(l) : (k == 8 && (side == 2 || side == 3) ? this.getTopIcon(l) : this.getSideIcon(l)));
	}
 
	@Override
	public int damageDropped(int meta) {
		return meta & 3;
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	protected IIcon getSideIcon(int meta) {
		return this.iicon[1];
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	protected IIcon getTopIcon(int meta) {
		return this.iicon[0];
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public void registerBlockIcons(IIconRegister iicon) {
		this.iicon[0] = iicon.registerIcon(this.getTextureName() + "_top");
		this.iicon[1] = iicon.registerIcon(this.getTextureName() + "_side");
	}
 
	@Override
	protected ItemStack createStackedBlock(int meta) {
		return new ItemStack(Item.getItemFromBlock(this), 1, meta & 3);
	}
 
	@Override
	public void breakBlock(World world, int x, int y, int z, Block block, int meta) {
		// 周囲の葉ブロックの消滅を始める。
		byte b0 = 4;
		int i1 = b0 + 1;
 
		if (!world.checkChunksExist(x - i1, y - i1, z - i1, x + i1, y + i1, z + i1))
			return;
		for (int ix = -b0; ix <= b0; ++ix) {
			for (int iy = -b0; iy <= b0; ++iy) {
				for (int iz = -b0; iz <= b0; ++iz) {
					Block block1 = world.getBlock(x + ix, y + iy, z + iz);
					if (block1.isLeaves(world, x + ix, y + iy, z + iz)) {
						block1.beginLeavesDecay(world, x + ix, y + iy, z + iz);
					}
				}
			}
		}
	}
 
	@Override
	public boolean canSustainLeaves(IBlockAccess world, int x, int y, int z) {
		return true;
	}
 
	@Override
	public boolean isWood(IBlockAccess world, int x, int y, int z) {
		return true;
	}
}
 

  • BlockAluminiumLeaves.java
+ 長いので囲みます
package tutorial.aluminiummod;
 
import java.util.ArrayList;
import java.util.Random;
 
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.BlockLeaves;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
import net.minecraft.world.ColorizerFoliage;
import net.minecraft.world.IBlockAccess;
import net.minecraft.world.World;
import net.minecraftforge.common.IShearable;
 
public class BlockAluminiumLeaves extends BlockLeaves implements IShearable {
 
	protected boolean isFancy;
	protected IIcon[] iicon = new IIcon[2];
	int[] array;
 
	public BlockAluminiumLeaves() {
		super();
		this.setTickRandomly(true);
		this.setCreativeTab(CreativeTabs.tabDecorations);
		this.setHardness(0.2F);
		this.setLightOpacity(1);
		this.setStepSound(soundTypeGrass);
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public int getBlockColor() {
		double d0 = 0.5D;
		double d1 = 1.0D;
		return ColorizerFoliage.getFoliageColor(d0, d1);
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public int getRenderColor(int color) {
		return ColorizerFoliage.getFoliageColorBasic();
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public int colorMultiplier(IBlockAccess iBlockAccess, int x, int y, int z) {
		// バイオームの境目である程度スムーズになるよう、周囲9ブロックのバイオームから平均を求めている。
		int ir = 0;
		int ig = 0;
		int ib = 0;
 
		for (int iz = -1; iz <= 1; ++iz) {
			for (int ix = -1; ix <= 1; ++ix) {
				int color = iBlockAccess.getBiomeGenForCoords(x + ix, z + iz).getBiomeFoliageColor(x + ix, y, z + iz);
				ir += (color & 16711680) >> 16;
				ig += (color & 65280) >> 8;
				ib += color & 255;
			}
		}
 
		return (ir / 9 & 255) << 16 | (ig / 9 & 255) << 8 | ib / 9 & 255;
	}
 
	@Override
	public void breakBlock(World world, int x, int y, int z, Block block, int meta) {
		// 周囲の葉ブロックの消滅を始める。
		byte b0 = 1;
		int i1 = b0 + 1;
 
		if (world.checkChunksExist(x - i1, y - i1, z - i1, x + i1, y + i1, z + i1)) {
			for (int j1 = -b0; j1 <= b0; ++j1) {
				for (int k1 = -b0; k1 <= b0; ++k1) {
					for (int l1 = -b0; l1 <= b0; ++l1) {
						Block block1 = world.getBlock(x + j1, y + k1, z + l1);
						if (block1.isLeaves(world, x + j1, y + k1, z + l1)) {
							block1.beginLeavesDecay(world, x + j1, y + k1, z + l1);
						}
					}
				}
			}
		}
	}
 
	@Override
	public void updateTick(World world, int x, int y, int z, Random random) {
		if (world.isRemote)
			return;
		int meta = world.getBlockMetadata(x, y, z);
		// メタデータの二進数四桁目が0(周囲で原木/葉が破壊されていない)か、
		// 三桁目が0以外(プレイヤーに設置されたもの)なら消滅させない。
		if ((meta & 8) == 0 || (meta & 4) != 0)
			return;
		// 周囲のブロックを調査し、葉の消滅を抑えるブロック(原木)がなければ消滅させる。
		byte b4 = 4;
		int i5 = b4 + 1;
		byte b32 = 32;
		int i1024 = b32 * b32;
		int i16 = b32 / 2;
 
		if (array == null) {
			array = new int[b32 * b32 * b32];
		}
 
		int l1;
 
		if (world.checkChunksExist(x - i5, y - i5, z - i5, x + i5, y + i5, z + i5)) {
 
			for (int ix = -b4; ix <= b4; ++ix) {
				for (int iy = -b4; iy <= b4; ++iy) {
					for (int iz = -b4; iz <= b4; ++iz) {
						Block block = world.getBlock(x + ix, y + iy, z + iz);
 
						if (!block.canSustainLeaves(world, x + ix, y + iy, z + iz)) {
							if (block.isLeaves(world, x + ix, y + iy, z + iz)) {
								array[(ix + i16) * i1024 + (iy + i16) * b32 + iz + i16] = -2;
							} else {
								array[(ix + i16) * i1024 + (iy + i16) * b32 + iz + i16] = -1;
							}
						} else {
							array[(ix + i16) * i1024 + (iy + i16) * b32 + iz + i16] = 0;
						}
					}
				}
			}
 
			for (l1 = 1; l1 <= 4; ++l1) {
				for (int ix = -b4; ix <= b4; ++ix) {
					for (int iy = -b4; iy <= b4; ++iy) {
						for (int iz = -b4; iz <= b4; ++iz) {
							if (array[(ix + i16) * i1024 + (iy + i16) * b32 + iz + i16] == l1 - 1) {
								if (array[(ix + i16 - 1) * i1024 + (iy + i16) * b32 + iz + i16] == -2) {
									array[(ix + i16 - 1) * i1024 + (iy + i16) * b32 + iz + i16] = l1;
								}
 
								if (array[(ix + i16 + 1) * i1024 + (iy + i16) * b32 + iz + i16] == -2) {
									array[(ix + i16 + 1) * i1024 + (iy + i16) * b32 + iz + i16] = l1;
								}
 
								if (array[(ix + i16) * i1024 + (iy + i16 - 1) * b32 + iz + i16] == -2) {
									array[(ix + i16) * i1024 + (iy + i16 - 1) * b32 + iz + i16] = l1;
								}
 
								if (array[(ix + i16) * i1024 + (iy + i16 + 1) * b32 + iz + i16] == -2) {
									array[(ix + i16) * i1024 + (iy + i16 + 1) * b32 + iz + i16] = l1;
								}
 
								if (array[(ix + i16) * i1024 + (iy + i16) * b32 + (iz + i16 - 1)] == -2) {
									array[(ix + i16) * i1024 + (iy + i16) * b32 + (iz + i16 - 1)] = l1;
								}
 
								if (array[(ix + i16) * i1024 + (iy + i16) * b32 + iz + i16 + 1] == -2) {
									array[(ix + i16) * i1024 + (iy + i16) * b32 + iz + i16 + 1] = l1;
								}
							}
						}
					}
				}
			}
		}
 
		l1 = array[i16 * i1024 + i16 * b32 + i16];
 
		if (l1 >= 0) {
			world.setBlockMetadataWithNotify(x, y, z, meta & 7, 4);
		} else {
			this.removeLeaves(world, x, y, z);
		}
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public void randomDisplayTick(World world, int x, int y, int z, Random random) {
		// 水を滴らせる?
		if (world.canLightningStrikeAt(x, y + 1, z) && !World.doesBlockHaveSolidTopSurface(world, x, y - 1, z) && random.nextInt(15) == 1) {
			double dx = x + random.nextFloat();
			double dy = y - 0.05D;
			double dz = z + random.nextFloat();
			world.spawnParticle("dripWater", dx, dy, dz, 0.0D, 0.0D, 0.0D);
		}
	}
 
	private void removeLeaves(World world, int x, int y, int z) {
		// ドロップさせ、空気ブロックに置き換える。
		this.dropBlockAsItem(world, x, y, z, world.getBlockMetadata(x, y, z), 0);
		world.setBlockToAir(x, y, z);
	}
 
	@Override
	public int quantityDropped(Random random) {
		return random.nextInt(20) == 0 ? 1 : 0;
	}
 
	@Override
	public Item getItemDropped(int meta, Random random, int fortune) {
		return Item.getItemFromBlock(AluminiumMod.saplingAluminium);
	}
 
	@Override
	public void dropBlockAsItemWithChance(World world, int x, int y, int z, int meta, float chance, int par7) {
		super.dropBlockAsItemWithChance(world, x, y, z, meta, 1.0f, par7);
	}
 
	@Override
	protected void func_150124_c(World world, int x, int y, int z, int meta, int chance) {}
 
	@Override
	protected int func_150123_b(int meta) {
		return 20;
	}
 
	@Override
	public boolean isOpaqueCube() {
		return !isFancy;
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public IIcon getIcon(int side, int meta) {
		return iicon[isFancy ? 0 : 1];
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public void setGraphicsLevel(boolean isFancy) {
		this.isFancy = isFancy;
	}
 
	@Override
	protected ItemStack createStackedBlock(int meta) {
		return new ItemStack(Item.getItemFromBlock(this), 1, 0);
	}
 
	@Override
	public String[] func_150125_e() {
		return null;
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public boolean shouldSideBeRendered(IBlockAccess iBlockAccess, int x, int y, int z, int side) {
		Block block = iBlockAccess.getBlock(x, y, z);
		// 処理優先で隣が同じブロックだったらfalse。
		return !isFancy && block == this ? false : super.shouldSideBeRendered(iBlockAccess, x, y, z, side);
	}
 
	@Override
	public boolean isShearable(ItemStack item, IBlockAccess world, int x, int y, int z) {
		return true;
	}
 
	@Override
	public ArrayList<ItemStack> onSheared(ItemStack item, IBlockAccess world, int x, int y, int z, int fortune) {
		ArrayList<ItemStack> ret = new ArrayList<ItemStack>();
		ret.add(new ItemStack(this, 1, world.getBlockMetadata(x, y, z) & 3));
		return ret;
	}
 
	@Override
	public void beginLeavesDecay(World world, int x, int y, int z) {
		int meta = world.getBlockMetadata(x, y, z);
 
		if ((meta & 8) == 0) {
			world.setBlockMetadataWithNotify(x, y, z, meta | 8, 4);
		}
		world.setBlockMetadataWithNotify(x, y, z, world.getBlockMetadata(x, y, z) | 8, 4);
	}
 
	@Override
	public boolean isLeaves(IBlockAccess world, int x, int y, int z) {
		return true;
	}
 
	@Override
	public ArrayList<ItemStack> getDrops(World world, int x, int y, int z, int meta, int fortune) {
		ArrayList<ItemStack> ret = new ArrayList<ItemStack>();
		int chance = this.func_150123_b(meta);
 
		if (fortune > 0) {
			chance -= 2 << fortune;
			if (chance < 10)
				chance = 10;
		}
 
		if (world.rand.nextInt(chance) == 0)
			ret.add(new ItemStack(this.getItemDropped(meta, world.rand, fortune), 1, this.damageDropped(meta)));
 
		chance = 200;
		if (fortune > 0) {
			chance -= 10 << fortune;
			if (chance < 40)
				chance = 40;
		}
 
		this.captureDrops(true);
		this.func_150124_c(world, x, y, z, meta, chance);
		ret.addAll(this.captureDrops(false));
		return ret;
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public void registerBlockIcons(IIconRegister register) {
		iicon[0] = register.registerIcon(this.getTextureName());
		iicon[1] = register.registerIcon(this.getTextureName() + "_opaque");
	}
 
}
 

  • ItemAluminiumLeaves.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.BlockLeaves;
import net.minecraft.item.ItemLeaves;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IIcon;
 
public class ItemAluminiumLeaves extends ItemLeaves {
 
	protected final Block leaves;
 
	public ItemAluminiumLeaves(Block block) {
		super((BlockLeaves) block);
		this.leaves = block;
		this.setMaxDamage(0);
		this.setHasSubtypes(true);
	}
 
	@Override
	public int getMetadata(int meta) {
		// 設置時は二進数三桁目が1になる。
		return meta | 4;
	}
 
	@Override
	public String getUnlocalizedName(ItemStack itemStack) {
		return leaves.getUnlocalizedName();
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public IIcon getIconFromDamage(int meta) {
		return leaves.getIcon(0, meta);
	}
 
	@Override
	@SideOnly(Side.CLIENT)
	public int getColorFromItemStack(ItemStack itemStack, int pass) {
		return leaves.getRenderColor(itemStack.getItemDamage());
	}
 
}
 

解説

Block

+ 長いので囲みます

Block setTickRandomly(boolean needsRandomTick)

trueに設定すると、ランダムでupdateTickが呼ばれるようになる。
植物の成長判定や葉の消滅などに使う。

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

ブロックの大きさを設定する。
カーソルがあったかどうかの判定や、合わせたときの枠線の描画に使われる。

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

ブロックを設置できるかを判定する。

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

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

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

ブロックがとどまれるかどうか。

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

当たり判定を返す。
BlockではAxisAlignedBB.getBoundingBox(x + minX, y + minY, z + minZ, x + maxX, y + maxY, z + maxZ)を返している。
このメソッドをオーバーライドすれば当たり判定を変更できる。

boolean isOpaqueCube()

不透明なブロックかどうかを返す。

boolean renderAsNormalBlock()

通常のブロックのように描画するかどうかを返す。
柵や壁がつながるかどうかの判定にも使われる。

int getRenderType()

レンダ―タイプを返す。
通常は0。
苗木や花、キノコなどは1。
原木、干草の俵などは31。
  • 1を返すとTileEntityの設定などが適用される。

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

ブロックのアップデート時の処理。
needsRandomTickがtrueの時はランダムに呼ばれる。
falseだと何tick後に呼び出すなどの処理が可能。

int onBlockPlaced(World world, int x, int y, int z, int side, float posX, float posY, float posZ, int meta)

ブロックが設置された時の処理。

ItemStack createStackedBlock(int meta)

メタデータを反映してItemStackを生成し返す。

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

ブロックが破壊された時の処理。

boolean canSustainLeaves(IBlockAccess world, int x, int y, int z)

葉を維持させられるブロックかどうかを返す。

boolean isWood(IBlockAccess world, int x, int y, int z)

原木かどうかを返す。
木の生成時の判定に使う。

Block setLightOpacity(int lightOpacity)

光の透過度を設定する。
0で不透過。1で完全に透過。

int getBlockColor()

ブロックの色を返す。

int getRenderColor(int color)

描画するときの色を返す。
インベントリ内での描画などに使う。

int colorMultiplier(IBlockAccess iBlockAccess, int x, int y, int z)

ブロックを描画するときの色の係数を返す。

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

クライアントで描画されるときにランダムで呼ばれる。

int quantityDropped(Random random)

ドロップ数を返す。

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

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

void dropBlockAsItemWithChance(World world, int x, int y, int z, int meta, float chance, int par7)

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

boolean shouldSideBeRendered(IBlockAccess iBlockAccess, int x, int y, int z, int side)

引数の面を描画するかどうか。
引数の座標はメソッドを呼ばれたブロックではなく、その隣にあるブロック。
Blockでは、ブロックの大きさが通常より小さいでないか、引数の座標のブロックが不透明でないならtrueを返す。

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

葉の消滅を始める。

boolean isLeaves(IBlockAccess world, int x, int y, int z)

葉かどうかを返す。

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

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

BlockBush

boolean canPlaceBlockOn(Block block)

引数のブロックの上に設置が可能かどうか。

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

設置されている状況が保てるかどうか判定し、保てないならドロップさせる。

BlockSapling

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

苗木を成長させる処理。

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

木を生成する処理。

boolean func_149880_a(World world, int x, int y, int z, int type)

引数の座標のブロックが同じ種類の苗木かどうか。
ダークオーク・松などの生成に使っている。

EnumPlantType

植物の植え方を表すenum。

Plains

苗木や草、花など。
草・土・耕された土に植えられる。

ForgeDirection

方向を表すenum。

UP

上方向。

AxisAlignedBB

ブロックの当たり判定などを保持するクラス。

TerrainGen

生成に関するEventを呼び出すクラス。

saplingGrowTree

SaplingGrowTreeEventを呼び出す。

IPlantable

植物のインターフェース。

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

植物の植え方によってenumを返す。

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

引数の座標にある植物を返す。
基本的にはthisでよい。

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)

成長させる処理。

BlockRotatedPillar

IIcon getSideIcon(int meta)

横の面のアイコンを返す。

IIcon getTopIcon(int meta)

上下面のアイコンを返す。

ColorizerFoliage

葉や草などの色を扱うクラス。

int getFoliageColor(double temperature, double humidity)

気温・湿度から色を生成して返す。

int getFoliageColorBasic()

基本の色を返す。
4764952(0x48A518, RGB:(72,181,24))。

BlockLeaves

void func_150124_c(World world, int x, int y, int z, int meta, int chance)

リンゴのドロップに使うメソッド。

int func_150123_b(int meta)

苗木のドロップ確率を返す。

void setGraphicsLevel(boolean isFancy)

描画優先/処理優先の設定を反映する。

String[] func_150125_e()

木の種類名の配列を返す。

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

引数の座標にあるブロックをドロップして破壊する。
privateなのでBlockAluminiumLeavesでは新しく実装していることになる。

IShearable

ハサミで回収できるブロックのインターフェース。

boolean isShearable(ItemStack item, IBlockAccess world, int x, int y, int z)

ハサミで回収できるかどうかを返す。

ArrayList<ItemStack> onSheared(ItemStack item, IBlockAccess world, int x, int y, int z, int fortune)

ハサミで回収された時の処理。
ハサミで回収した時のドロップアイテムのリストを返す。

使用例

星の樹MODは木のカスタマイズ機能の影響で非常にわかりにくくなっているため、使用例は載せません。
Beta 1.0.0以前のコードは参考になるかもしれません。

コメント

この項目に関する質問などをどうぞ。
  • 左のメニューでTileEntityの追加を押すとこのページに来てしまいます。 - 名無しさん 2016-01-09 22:38:31
    • 申し訳ありません。訂正いたしました。 - Tom Kate 2016-01-10 08:41:18
  • 1.7.10で丸ごとコピーしてみたのですが、preInitのLeavesのところで落ちてしまいます。 - 名無しさん 2016-04-30 05:28:57
    • ご報告ありがとうございます。修正しました。 - 赤砂蛇凪浜 2016-04-30 16:13:34
  • 葉のテクスチャは新しい文で指定する必要があるのでしょうか - 名無しさん 2016-05-20 16:43:05
    • テクスチャの指定に関する部分はいくつかあるため、どこについてなのかもう少し詳しくお願いします。 - 赤砂蛇凪浜 2016-05-20 17:28:58
      • 葉のブロックのテクスチャ(描画設定で切り替わったり)です。例えばここのコードを丸ごと持ってきた場合、画像ファイルの名前や位置次第でテクスチャは表示されうるのでしょうか。すみません枝間違えました - ポン酢 2016-05-20 20:44:34
        • 入力ミスのようですので、コメントは片方削除させていただきます。
          チュートリアルに不備があり、葉のテクスチャの指定がうまくできていなかったので修正しました。
          AluminiumMod.javaおよびBlockAluminiumLeaves.javaに変更がありますのでご確認ください。
          ご迷惑をおかけして申し訳ありませんでした。解説がわかりにくければ、またご質問いただけるとありがたいです。 - 赤砂蛇凪浜 2016-05-22 13:42:04
  • 同じIDでいくつかの原木ブロックを追加するには、どのようにすれば良いのでしょうか。 - 名無しさん 2016-06-17 23:31:47
    • 「メタデータを持つブロックの追加」に記載している方法で、getSubBlocksなどをオーバーライドしてテクスチャの指定を少し変更すればできます。
      ただし、原木の場合は向きをメタデータで記録しているので、一つのIDで追加できるのは4種類までです。
      バニラのコードや、(カスタム機能の影響で少しわかりにくくなっていますが)星の樹MODのソースコードも見てみてください。
      返信が遅くなってしまい、申し訳ありません。 - 赤砂蛇凪浜 2016-06-25 10:48:37
  • ありがとうございます! - 名無しさん 2016-06-25 21:53:03
    • 返信するとこ間違えました... - 名無しさん 2016-06-25 21:53:43
  • このソースコードは1.10でも使用することができますか? - 名無しさん 2016-10-06 00:48:58
    • こちらのチュートリアルは記載の通り1.7.10版のものです。1.8以降は大きくコーディングが変更されておりますので使うことはできません。 - Tom Kate 2016-10-06 23:07:20
最終更新:2016年05月22日 13:38