提供: Minecraft Modding Wiki
2015年5月6日 (水) 16:02時点におけるDefeatedcrow (トーク | 投稿記録)による版 (注意書きを追加)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
移動先: 案内検索

この記事は"Minecraft Forge Universal 10.13.0.1197~"を前提MODとしています。

液体タンクを持つTileEntity追加のチュートリアルです。

液体タンクとは

液体(Fluid)は1.6から追加された概念で、アイテムやブロックの実体を持たない流体情報です。詳しくはこちら→1.6の流体追加

液体は「FluidStack」という、ItemStackに似てFluidの種類、量を格納した概念があります。 さらに、FluidTank.class、またはこれを継承したクラスを用いることで、任意の量のFluidStackを出し入れできる容器のような概念を扱うことが出来ます。

アイテムやブロックと異なり、
・FluidRegistryクラスの各メソッドを介して、Fluidの名前(文字列)を介して取得や存在確認が出来る。
・液体の容器アイテムや、液体の入っている容器アイテムなどをFluidContainerRegistryに登録したり、取得することによって、
MODで追加される液体や液体容器アイテムへのアクセスが容易である。
など、MOD内・MOD間での連携要素に便利なシステムを持っています。

ここでは、FluidTank.classを継承した独自クラスを作り、これをTileEntityで扱うことで、 「液体の出し入れができる液体タンクブロック」の追加を行います。

Forge1355以降のFluidについて

Forge1355前後でForgeのFluidに関係する更新があり、ForgeのFluid管理方法が変わりました。 従来のint型のFluidIDが削除され、(BlockやItemのように)文字列IDで管理されるようになります。 ただしその後、後方互換性のためにIDの代わりになる物が用意されているため、1351以前のForgeと互換性を持たせることができます。

※注意:この方法はForge1388時点で存在するメソッドを利用しています。  ビルド及び動作確認は1388で行っており、それより古いバージョンのForge(特に1351~1375あたり)との互換性は確認できていません。あしからず。  今後のForgeの方向性を考慮すると、いずれはFluidの数値IDに依存しない(文字列IDで管理できる)構造が求められるようです

  • FluidStackからFluidIDを取得する部分

・fluidStack.getFluid().getID(); このように変更してください。

下記の方法では、互換性が保てません。
・fluidStack.getFluidID();
・fluidStack.fluidID;

  • Deprecatedになったメソッド

下記はForge1388においてはまだ使用できていますが、非推奨となっています。
・FluidStack(int fluidID, int amount);
・FluidRegistry.getFluidName(int fluidID); など

ソースコード

MODのメインクラスなど

@Modアノテーションを含むメインクラス、プロキシクラス、ブロックのクラスなど

TutorialCore.java

  • @Modアノテーションを含む、このMODのメインクラス
package mods.fluidtutorial.common;

import java.io.IOException;

import net.minecraft.block.Block;
import net.minecraft.creativetab.CreativeTabs;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.Mod.Instance;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;

@Mod(
		modid = "FluidTankTutorial",
		name = "FluidTankTutorial",
		version = "1.7.10_1.0a",
		dependencies = "required-after:Forge@[10.13.0.1197,)"
		)
public class TutorialCore {
	
	@Instance("FluidTankTutorial")
	public static TutorialCore instance;
	
	/*
	 * このMODではTileEntityおよびブロックの特殊レンダーの両方を併用する。
	 * 登録処理をクライアント・サーバで分ける必要があるので、プロキシクラスを作成。 
	 */
	@SidedProxy(clientSide = "mods.fluidtutorial.client.ClientProxyFT", 
                    serverSide = "mods.fluidtutorial.client.CommonProxyFT")
	public static CommonProxyFT proxy;
	
	//水桶ブロック
	public static Block tutorialFluidTab;
	
	//ブロックレンダークラス追加のためのID
	public static int tutorialRenderId;
	
	@EventHandler
	public void preInit(FMLPreInitializationEvent event)
	{
		//ブロックの登録はpreInitで行う。
		//このブロックはレシピを追加しないため、取り出しはクリエイティブタブのみ。
		tutorialFluidTab = new BlockFluidTab().setBlockName("tutorialFluidTab")
                                                      .setCreativeTab(CreativeTabs.tabDecorations);
		GameRegistry.registerBlock(tutorialFluidTab, "tutorialFluidTab");
	}
	
	@EventHandler
	public void init(FMLInitializationEvent event)
	{
		//レンダーブロッククラス・TileEntityの登録。どちらも登録はプロキシクラスを経由する。
		this.tutorialRenderId = proxy.getRenderID();
		proxy.registerTileEntity();
	}

}


CommonProxy.java

  • サーバ側のプロキシクラス
package mods.fluidtutorial.common;

import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.world.World;

public class CommonProxyFT {
	
	public void registerTileEntity()
    {
		GameRegistry.registerTileEntity(TileFluidTab.class, "TileFluidTab");
    }
	
	public int getRenderID()
	{
		return -1;
	}
	
	public World getClientWorld() {
		
		return null;
	}
}


ClientProxy.java

  • クライアント側のプロキシクラス
package mods.fluidtutorial.client;

import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.client.registry.ClientRegistry;
import cpw.mods.fml.client.registry.RenderingRegistry;
import net.minecraft.world.World;
import mods.fluidtutorial.common.CommonProxyFT;
import mods.fluidtutorial.common.TileFluidTab;

public class ClientProxyFT extends CommonProxyFT{
	
	@Override
	public void registerTileEntity()
    {
		RenderingRegistry.registerBlockHandler(new RenderTutorialBlock());
		ClientRegistry.registerTileEntity(TileFluidTab.class, "TileFluidTab", new TileEntityFluidTabRenderer());
    }
	
	@Override
	public int getRenderID()
	{
		return RenderingRegistry.getNextAvailableRenderId();
	}
	
	@Override
	public World getClientWorld() {
		
		return FMLClientHandler.instance().getClient().theWorld;
	}
}


BlockFluidTab.java

  • 水桶ブロックのクラス。このMODでのタンク操作のメイン部分。
package mods.fluidtutorial.common;

import java.util.List;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.block.Block;
import net.minecraft.block.BlockCauldron;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.ITileEntityProvider;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.AxisAlignedBB;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidContainerRegistry;
import net.minecraftforge.fluids.FluidRegistry;
import net.minecraftforge.fluids.FluidStack;

//桶のブロックのクラス。
public class BlockFluidTab extends BlockContainer{
	
	public BlockFluidTab()
	{
		super(Material.wood);
		this.setStepSound(Block.soundTypeWood);
		this.setHardness(1.0F);
	}
	
	//プレイヤーの右クリック処理
	@Override
	public boolean onBlockActivated(World par1World, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
    {
	/*手持ちアイテム*/
        ItemStack itemstack = par5EntityPlayer.inventory.getCurrentItem();
        /*このブロックのTileEntity*/
        TileFluidTab tile = (TileFluidTab) par1World.getTileEntity(par2, par3, par4);
        
        /* 
         * ちなみに、この段階ではworld.isRemoteの判定を行っていない。
         * 意図的にサーバ・クライアントで同じ処理がそれぞれ行われるようにしている。
         */
        if (tile != null)
        {
        	//TileEntityの液体タンクに入っている液体を取得
    		FluidStack fluid = tile.productTank.getFluid();
    		
        	if (itemstack == null)//素手
        	{
        		//表示用の文字列をとりあえず作成
        		String s = "";
        		
        		if (fluid != null && fluid.getFluid() != null)
        		{
        			s = "Fluid current in the tab : " + fluid.getFluid().getLocalizedName(fluid);
        		}
        		else
        		{
        			s = "No fluid in the tab";
        		}
        		
        		/* 
        		 * チャット表示時はリモートワールドの判定を挟む。
        		 * そうしないと、サーバ・クライアントで二重にメッセージが出てしまう。
        		 */
        		if (!par1World.isRemote) par5EntityPlayer.addChatMessage(new ChatComponentText(s));
        		
        		return true;
        	}
        	else
        	{
    			//このメソッドにより、手持ちのアイテムが液体容器に登録されたアイテムかどうか、及び入っている液体を取得する。
    			FluidStack fluid2 =  FluidContainerRegistry.getFluidForFilledItem(itemstack);
        		
    			//満たされた液体コンテナが手持ちの場合
        		if (fluid2 != null && fluid2.getFluid() != null)
        		{
        			/*
    				 * fillメソッドの第二引数にfalseを入れた場合、実際に液体をタンクに入れるのではなく、
    				 * タンクに投入可能な液体の量をシュミレートして値を返す。
    				 */
    				int put = int put = tile.fill(ForgeDirection.UNKNOWN, fluid2, false);
    				
    				//全量投入可能なときのみ
    				if (put == fluid2.amount)
    				{
    					//今度は液体を液体タンクに入れるので、第二引数はtrueにする。
    					tile.fill(ForgeDirection.UNKNOWN, fluid2, true);

                    //液体容器を空にして、空容器を得るメソッド。
    					ItemStack emptyContainer = FluidContainerRegistry.drainFluidContainer(itemstack);
    					if (emptyContainer != null)
    					{
    						if (!par5EntityPlayer.inventory.addItemStackToInventory(emptyContainer.copy()))
        		        	    {
        		        		    par5EntityPlayer.entityDropItem(emptyContainer.copy(), 1);
        		        	    }
    					}
    					
    					//プレイヤーの手持ちアイテムを減らす処理
    					if (!par5EntityPlayer.capabilities.isCreativeMode && itemstack.stackSize-- <= 0)
    	                                {
    	            	                	par5EntityPlayer.inventory.setInventorySlotContents(par5EntityPlayer.inventory.currentItem, (ItemStack)null);
    	                                }
    					
    				        //更新を伝える処理
    				        //TileEntityを更新した場合、このように更新処理を挟まないと見た目に反映しない。
    				        tile.markDirty();
    	        		        par5EntityPlayer.inventory.markDirty();
    	        		        par1World.markBlockForUpdate(par2, par3, par4);
    	        		
    	        		        //効果音の発生
    	        		        par1World.playSoundAtEntity(par5EntityPlayer, "random.pop", 0.4F, 1.8F);
    	        		
    	        		        return true;
    				}
        		}
        		else
        		{
        			//液体タンクに何かしら入っている時
        			if (fluid != null && fluid.getFluid() != null)
        			{
        				if (fluid.amount < 1000) return true;
        				
        				/*
        				 * このメソッドにより、手持ちのアイテムを空容器として指定した液体を入れた「液体で満たされた容器アイテム」を取得している。
        				 * 液体容器に登録された液体の量も判定されるため、とりあえず1000mB(バケツの容量)で判定。
        				 * また、タンク内の液体が1000未満の場合は処理を中断する。
        				 */
        				ItemStack get = FluidContainerRegistry.fillFluidContainer(new FluidStack(fluid.getFluid(), 1000), itemstack);
        				
        				if (get != null)
        				{
        				/*
            				 * タンクの液体の減少処理
            				 * タンク容量 > 1000 であることを事前にチェック済みのためにここではシュミレート無しとしたが、
            				 * fillの場合と同様に、シュミレートで投入可能量を確かめでから行っても良いと思う。
            				 */
            				tile.drain(ForgeDirection.UNKNOWN, 1000, true);
            				
            				//プレイヤーに、先に取得した「液体で満たされた容器アイテム」を与える処理
        					if (!par5EntityPlayer.inventory.addItemStackToInventory(get.copy()))
        		        	{
        		        		par5EntityPlayer.entityDropItem(get.copy(), 1);
        		        	}
            				
            				//プレイヤーの手持ちアイテムを減らす処理
        				if (!par5EntityPlayer.capabilities.isCreativeMode && itemstack.stackSize-- <= 0)
        	                        {
        	            	        par5EntityPlayer.inventory.setInventorySlotContents(par5EntityPlayer.inventory.currentItem, (ItemStack)null);
        	                        }
        					
        				//更新を伝える処理
        				//TileEntityを更新した場合、このように更新処理を挟まないと見た目に反映しない。
        				tile.markDirty();
        	        		par5EntityPlayer.inventory.markDirty();
        	        		par1World.markBlockForUpdate(par2, par3, par4);
        	        		
        	        		//効果音の発生
        	        		par1World.playSoundAtEntity(par5EntityPlayer, "random.pop", 0.4F, 1.8F);
        				}
        				
    	        		        return true;
        			}
        			else
        			{
        				//アイテムが液体入り容器でなく、かつタンクが空だった場合は何もしない
        				return true;
        			}
        		}
        	}
        }
        
        return true;
    }
	
    @Override
    public TileEntity createNewTileEntity(World world, int a) {
		
	return new TileFluidTab();
    }
	
    /*=== レンダー関係  ===*/

    //当たり判定を設定。大釜のように中にエンティティが入り込める。
    @Override
    public void addCollisionBoxesToList(World world, int x, int y, int z, AxisAlignedBB aabb, List list, Entity entity)
    {
        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 0.125F, 1.0F);
        super.addCollisionBoxesToList(world, x, y, z, aabb, list, entity);
        float f = 0.0675F;
        this.setBlockBounds(0.0F, 0.0F, 0.0F, f, 1.0F, 1.0F);
        super.addCollisionBoxesToList(world, x, y, z, aabb, list, entity);
        this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, f);
        super.addCollisionBoxesToList(world, x, y, z, aabb, list, entity);
        this.setBlockBounds(1.0F - f, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
        super.addCollisionBoxesToList(world, x, y, z, aabb, list, entity);
        this.setBlockBounds(0.0F, 0.0F, 1.0F - f, 1.0F, 1.0F, 1.0F);
        super.addCollisionBoxesToList(world, x, y, z, aabb, list, entity);
        this.setBlockBoundsForItemRender();
    }
	
    @Override
    public boolean isOpaqueCube()
    {
        return false;
    }

    //追加したブロックレンダークラスのIDを入れる
    @Override
    public int getRenderType()
    {
        return TutorialCore.tutorialRenderId;
    }

    @Override
    public boolean renderAsNormalBlock()
    {
        return false;
    }

    @SideOnly(Side.CLIENT)
    @Override
    public void registerBlockIcons(IIconRegister p_149651_1_)
    {
        this.blockIcon = Blocks.planks.getIcon(0, 0);
        //ここで登録するテクスチャは破壊時のエフェクトくらいにしか使用されない。なのでバニラ木材を流用。
    }

}

液体タンク

液体処理を扱う液体タンク、及び液体タンクを含むTileEntityのクラス

TileFluidTab.java

  • TileEntityのクラス
package mods.fluidtutorial.common;

import java.util.Iterator;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.NetworkManager;
import net.minecraft.network.Packet;
import net.minecraft.network.play.server.S35PacketUpdateTileEntity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.world.biome.BiomeGenBase;
import net.minecraftforge.common.BiomeDictionary;
import net.minecraftforge.common.BiomeDictionary.Type;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTankInfo;
import net.minecraftforge.fluids.IFluidHandler;

/*
 * TileEntityのクラス。
 * プレイヤーの操作はブロックのクラスで行うため、このMODのTileEntityは液体タンクの情報を保持するだけの役目である。
 */
public class TileFluidTab extends TileEntity implements IFluidHandler{
	
    //このTileEntityに持たせる液体タンク。引数は最大容量。
    public TutorialTank productTank = new TutorialTank(1000);

    /*=== NBT、パケットの読み書き部分 ===*/
	
    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.readFromNBT(par1NBTTagCompound);
        
        this.productTank = new TutorialTank(1000);
		if (par1NBTTagCompound.hasKey("productTank")) {
		    this.productTank.readFromNBT(par1NBTTagCompound.getCompoundTag("productTank"));
		}
    }

    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.writeToNBT(par1NBTTagCompound);
        
        NBTTagCompound tank = new NBTTagCompound();
		this.productTank.writeToNBT(tank);
		par1NBTTagCompound.setTag("productTank", tank);
    }
    
    @Override
	public Packet getDescriptionPacket() {
        NBTTagCompound nbtTagCompound = new NBTTagCompound();
        this.writeToNBT(nbtTagCompound);
        return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 1, nbtTagCompound);
	}
 
	@Override
    public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
        this.readFromNBT(pkt.func_148857_g());
    }
	
	/*=== レンダー用に用意したメソッド。使わない方法もあると思う ===*/
	
	@SideOnly(Side.CLIENT)
	public int getMetadata()
    {
    	return this.worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
    }
	
	//TileEntityの特殊レンダークラスで使う液体のアイコンを取得。
	@SideOnly(Side.CLIENT)
	public IIcon getFluidIcon()
    {
		Fluid fluid = this.productTank.getFluidType();
    	return fluid != null ? fluid.getIcon() : null;
    }
    
	/*====== IFluidHandlerの実装部分 ======*/

	@Override
	public FluidStack drain(ForgeDirection from, FluidStack resource,
			boolean doDrain) {
		if (resource == null) {
			return null;
		}
		if (productTank.getFluidType() == resource.getFluid()) {
			return productTank.drain(resource.amount, doDrain);
		}
		return null;
	}

	@Override
	public FluidStack drain(ForgeDirection from, int maxDrain, boolean doDrain) {
		return this.productTank.drain(maxDrain, doDrain);
	}
	
	//
	@Override
	public int fill(ForgeDirection from, FluidStack resource, boolean doFill) {
		if (resource == null || resource.getFluid() == null){
			return 0;
		}
		
		FluidStack current = this.productTank.getFluid();
		FluidStack resourceCopy = resource.copy();
		if (current != null && current.amount > 0 && !current.isFluidEqual(resourceCopy)){
			return 0;
		}
		
		int i = 0;
		int used = this.productTank.fill(resourceCopy, doFill);
		resourceCopy.amount -= used;
		i += used;
		
		return i;
	}

	
	@Override
	public boolean canFill(ForgeDirection from, Fluid fluid) {
		return fluid != null && this.productTank.isEmpty();
	}

	@Override
	public boolean canDrain(ForgeDirection from, Fluid fluid) {
		return true;
	}

	@Override
	public FluidTankInfo[] getTankInfo(ForgeDirection from) {
		return new FluidTankInfo[]{productTank.getInfo()};
	}

}

TutorialTank.java

  • FluidTankを継承した、このMOD独自の液体タンク
package mods.fluidtutorial.common;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import net.minecraftforge.fluids.Fluid;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidTank;

/*
 * 液体タンクのクラス。
 * 基本的な増減処理などはFluidTankに備わっているので、
 * ここでは必要に応じてメソッドを書き換えたり、追加している。
 */
public class TutorialTank extends FluidTank{

	public TutorialTank(int capacity) {
		super(capacity);
	}
	
	public TutorialTank(FluidStack stack, int capacity) {
		super(stack, capacity);
	}
	
	public TutorialTank(Fluid fluid, int amount, int capacity) {
		super(fluid, amount, capacity);
	}
	
	//判定処理の短縮用のemptyフラグ
	public boolean isEmpty() {
	    return (getFluid() == null) || getFluid().getFluid() == null || (getFluid().amount <= 0);
	}

	//判定処理の短縮用の満タンフラグ
	public boolean isFull() {
	    return (getFluid() != null) && (getFluid().amount == getCapacity());
	}

	//Fluid型で中身を得る
	public Fluid getFluidType() {
	    return getFluid() != null ? getFluid().getFluid() : null;
	}

	//翻訳された液体名を得るメソッド
	public String getFluidName()
	{
	    return (this.fluid != null) && (this.fluid.getFluid() != null) ? this.fluid.getFluid().getLocalizedName(this.fluid): "Empty";
	}
	
	//同期処理用
	@SideOnly(Side.CLIENT)
	public void setAmount(int par1)
	{
		if (this.fluid != null && this.fluid.getFluid() != null)
		{
			this.fluid.amount = par1;
		}
	}
}

レンダークラス

ブロックの見た目や、中身の描写を扱うクラス

RenderTutorialBlock.java

  • ブロックの特殊レンダー
/* Original code was created by regin666 */
package mods.fluidtutorial.client;

import org.lwjgl.opengl.GL11;

import mods.fluidtutorial.common.TutorialCore;
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.RenderBlocks;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.init.Blocks;
import net.minecraft.util.IIcon;
import net.minecraft.world.IBlockAccess;
import cpw.mods.fml.client.registry.ISimpleBlockRenderingHandler;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

/*
 * このクラスでは、桶の部分のレンダーを作っている。
 * 中身のみ、別のクラス(TileEntityの特殊描画クラス)で描画している。
 */
@SideOnly(Side.CLIENT)
public class RenderTutorialBlock implements ISimpleBlockRenderingHandler{
	
	private IIcon boxIIcon;

	@Override
	public void renderInventoryBlock(Block block, int metadata, int modelID,
			RenderBlocks renderer) {
		
		//アイコンはバニラの樫の木材
		this.boxIIcon = Blocks.planks.getBlockTextureFromSide(1);
		
		if (modelID == this.getRenderId())
		{
			//底
			renderInvCuboid(renderer, block,  0.0F/16.0F, 0.0F/16.0F, 0.0F/16.0F, 16.0F/16.0F, 2.0F/16.0F, 16.0F/16.0F,  this.boxIIcon);
			
			//壁面
			renderInvCuboid(renderer, block,  0.0F/16.0F, 2.0F/16.0F, 0.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F, 1.0F/16.0F,  this.boxIIcon);
			renderInvCuboid(renderer, block,  0.0F/16.0F, 2.0F/16.0F, 15.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F,  this.boxIIcon);
			renderInvCuboid(renderer, block,  0.0F/16.0F, 2.0F/16.0F, 1.0F/16.0F, 1.0F/16.0F, 16.0F/16.0F, 15.0F/16.0F,  this.boxIIcon);
			renderInvCuboid(renderer, block,  15.0F/16.0F, 2.0F/16.0F, 1.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F, 15.0F/16.0F,  this.boxIIcon);
		}
		
	}

	@Override
	public boolean renderWorldBlock(IBlockAccess world, int x, int y, int z,
			Block block, int modelId, RenderBlocks renderer) {
		
		this.boxIIcon = Blocks.planks.getBlockTextureFromSide(1);
		
		if (modelId == this.getRenderId())
		{
			
			renderer.setOverrideBlockTexture(this.boxIIcon);
			block.setBlockBounds(0.0F/16.0F, 0.0F/16.0F, 0.0F/16.0F, 16.0F/16.0F, 2.0F/16.0F, 16.0F/16.0F);
			renderer.setRenderBoundsFromBlock(block);
			renderer.renderStandardBlock(block, x, y, z);
			
			renderer.setOverrideBlockTexture(this.boxIIcon);
			block.setBlockBounds(0.0F/16.0F, 2.0F/16.0F, 0.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F, 1.0F/16.0F);
			renderer.setRenderBoundsFromBlock(block);
			renderer.renderStandardBlock(block, x, y, z);
			renderer.setOverrideBlockTexture(this.boxIIcon);
			block.setBlockBounds(0.0F/16.0F, 2.0F/16.0F, 15.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F);
			renderer.setRenderBoundsFromBlock(block);
			renderer.renderStandardBlock(block, x, y, z);
			renderer.setOverrideBlockTexture(this.boxIIcon);
			block.setBlockBounds(0.0F/16.0F, 2.0F/16.0F, 1.0F/16.0F, 1.0F/16.0F, 16.0F/16.0F, 15.0F/16.0F);
			renderer.setRenderBoundsFromBlock(block);
			renderer.renderStandardBlock(block, x, y, z);
			renderer.setOverrideBlockTexture(this.boxIIcon);
			block.setBlockBounds(15.0F/16.0F, 2.0F/16.0F, 1.0F/16.0F, 16.0F/16.0F, 16.0F/16.0F, 15.0F/16.0F);
			renderer.setRenderBoundsFromBlock(block);
			renderer.renderStandardBlock(block, x, y, z);
			
			
			renderer.clearOverrideBlockTexture();
			block.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
			renderer.setRenderBoundsFromBlock(block);
			return true;
		}
		return false;
	}

	@Override
	public boolean shouldRender3DInInventory(int a) {
		
		return true;
	}

	//コアクラスのレンダー用IDをここに入れる
	@Override
	public int getRenderId() {
		
		return TutorialCore.tutorialRenderId;
	}
	
	private void renderInvCuboid(RenderBlocks renderer, Block block, float minX, float minY, float minZ, float maxX, float maxY, float maxZ, IIcon icon)
	{
		Tessellator tessellator = Tessellator.instance;
		block.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		renderer.setRenderBoundsFromBlock(block);
		GL11.glTranslatef(-0.5F, -0.5F, -0.5F);
		block.setBlockBounds(minX, minY, minZ, maxX, maxY, maxZ);
		renderer.setRenderBoundsFromBlock(block);
		tessellator.startDrawingQuads();
		tessellator.setNormal(0.0F, -1F, 0.0F);
		renderer.renderFaceYNeg(block, 0.0D, 0.0D, 0.0D, icon);
		tessellator.draw();
		tessellator.startDrawingQuads();
		tessellator.setNormal(0.0F, 1.0F, 0.0F);
		renderer.renderFaceYPos(block, 0.0D, 0.0D, 0.0D, icon);
		tessellator.draw();
		tessellator.startDrawingQuads();
		tessellator.setNormal(0.0F, 0.0F, -1F);
		renderer.renderFaceXPos(block, 0.0D, 0.0D, 0.0D, icon);
		tessellator.draw();
		tessellator.startDrawingQuads();
		tessellator.setNormal(0.0F, 0.0F, 1.0F);
		renderer.renderFaceXNeg(block, 0.0D, 0.0D, 0.0D, icon);
		tessellator.draw();
		tessellator.startDrawingQuads();
		tessellator.setNormal(-1F, 0.0F, 0.0F);
		renderer.renderFaceZNeg(block, 0.0D, 0.0D, 0.0D, icon);
		tessellator.draw();
		tessellator.startDrawingQuads();
		tessellator.setNormal(1.0F, 0.0F, 0.0F);
		renderer.renderFaceZPos(block, 0.0D, 0.0D, 0.0D, icon);
		tessellator.draw();
		GL11.glTranslatef(0.5F, 0.5F, 0.5F);
		block.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
		renderer.setRenderBoundsFromBlock(block);
	}
}

TileEntityFluidTabRenderer.java

  • 桶の中身の水面部分を描写するためのクラス
package mods.fluidtutorial.client;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;

import mods.fluidtutorial.common.TileFluidTab;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.entity.Render;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.tileentity.TileEntityRendererDispatcher;
import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
import net.minecraft.entity.Entity;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.IIcon;
import net.minecraft.util.ResourceLocation;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;

/*
 * 桶の中身の部分を描写しているクラス。
 * アイコンはFluidが持っているものをTileEntityの液体タンクから取得している。
 * よって、Fluidとしてアイコンを登録されていれば、MODで追加される液体にも対応している。
 */
@SideOnly(Side.CLIENT)
public class TileEntityFluidTabRenderer extends TileEntitySpecialRenderer
{
    public static TileEntityFluidTabRenderer renderer;

    public void renderTileEntityCupAt(TileFluidTab par1Tile, double par2, double par4, double par6, float par8)
    {
        this.setRotation(par1Tile, (float)par2, (float)par4, (float)par6);
    }

    public void setTileEntityRenderer(TileEntityRendererDispatcher par1TileEntityRenderer)
    {
        super.func_147497_a(par1TileEntityRenderer);
        renderer = this;
    }

    public void setRotation(TileFluidTab par0Tile, float par1, float par2, float par3)
    {
    	//テセレータを使って、一枚の平面テクスチャとして表示させる。
    	
    	Tessellator tessellator = Tessellator.instance;
    	
    	if (par0Tile.getFluidIcon() != null)
    	{
            //コメントアウト部分を復帰させると、水面の描写が半透明になる。
    	    GL11.glPushMatrix();
            GL11.glEnable(GL12.GL_RESCALE_NORMAL);
//            GL11.glEnable(GL11.GL_BLEND);
//            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
            GL11.glColor4f(2.0F, 2.0F, 2.0F, 0.75F);
            GL11.glTranslatef((float)par1, (float)par2 + 0.5F, (float)par3);
            GL11.glScalef(1.0F, -1.0F, -1.0F);
            GL11.glRotatef(0.0F, 0.0F, 1.0F, 0.0F);
            
            IIcon iicon = par0Tile.getFluidIcon();
            float f14 = iicon.getMinU();
            float f15 = iicon.getMaxU();
            float f4 = iicon.getMinV();
            float f5 = iicon.getMaxV();
            
            this.bindTexture(TextureMap.locationBlocksTexture);
            
            float f = 0.0625F;
            tessellator.startDrawingQuads();
            tessellator.setNormal(1.0F, 0.0F, 0.0F);
            tessellator.addVertexWithUV(0.0D + f, -0.4D, -1.0D + f, (double)f15, (double)f4);
            tessellator.addVertexWithUV(1.0D - f, -0.4D, -1.0D + f, (double)f14, (double)f4);
            tessellator.addVertexWithUV(1.0D - f, -0.4D, 0.0D - f, (double)f14, (double)f5);
            tessellator.addVertexWithUV(0.0D + f, -0.4D, 0.0D - f, (double)f15, (double)f5);
            tessellator.draw();
            
            GL11.glDisable(GL12.GL_RESCALE_NORMAL);
//            GL11.glDisable(GL11.GL_BLEND);
            GL11.glPopMatrix();
    	}
    	
    	
    }

    public void renderTileEntityAt(TileEntity par1TileEntity, double par2, double par4, double par6, float par8)
    {
        this.renderTileEntityCupAt((TileFluidTab)par1TileEntity, par2, par4, par6, par8);
    }
}