提供: Minecraft Modding Wiki
移動先: 案内検索

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

Stone pickaxe.png
中級者向けのチュートリアルです。
C block.png
Blockに関係のあるチュートリアルです。

TileEntity追加のチュートリアルです。

シンプルなTileEntityの追加

TileEntityを持つブロックの一例を挙げます。

ここでは、チェストやかまどなどのようにIInventoryインターフェースの実装がなく、 Blockから受け取った情報(String)を保存してNBT・パケットで同期させる、ごく単機能のTileEntityの例とします。

ソースコード

TutorialCore.java

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

import java.io.IOException;

import tutorial.common.block.SimpleBlock;
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 = "TutorialTiles",
		name = "TileEntityTutorials",
		version = "1.7.10_1.0a",
		dependencies = "required-after:Forge@[10.13.0.1197,)"
		)
public class TutorialCore {
	
	@Instance("TutorialTiles")
	public static TutorialCore instance;
	
	/*
	 * 念の為にプロキシクラスを設定。
	 * 今回は使用していませんが、TileEntitySpecialRendererを使ってモデルを適用する場合などは、
	 * client、serverそれぞれで異なるメソッドを使ってTileEntityを登録します。
	 * (今回は割愛)
	 */
	@SidedProxy(clientSide = "tutorial.client.TutorialClientProxy", serverSide = "tutorial.client.TutorialCommonProxy")
	public static TutorialCommonProxy proxy;
	
	/*
	 * 今回追加されるTileEntityを持つブロックです。
	 */
	public static Block simpleTile;
	
	/*
	 * ブロックの登録はpreInitで行います。
	 */
	@EventHandler
	public void preInit(FMLPreInitializationEvent event)
	{
		simpleTile = new SimpleBlock().setBlockName("tutorialSimpleBlock").setCreativeTab(CreativeTabs.tabDecorations);
		GameRegistry.registerBlock(simpleTile, "tutorialSimpleBlock");
	}
	
	/*
	 * TileEntityの登録をInitで行います。
	 * ここではプロキシクラスの登録メソッドを呼ぶことで、clientとserverで異なる処理が呼ばれるようにします。
	 */
	@EventHandler
	public void init(FMLInitializationEvent event)
	{
		proxy.registerTileEntity();
	}

}

TutorialLogger.class

  • ログ出力用クラス。作成は必須でないが、作っておくと開発の役に立つ。
package tutorial.common;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;


/*
 * Loggerクラスです。
 * 必ず作るべきものでもないのですが、
 * 作っておくとデバッグコンソール画面にModID入りのログを出すことができるので、
 * 要所要所で出すようにすると、不具合の解決や動作の理解に役立ちます。
 */
public class TutorialLogger {
	
	public static Logger logger = LogManager.getLogger("TileTutorial");
	
	/*
	 * 以下のメソッドはわざわざ呼び出さなくても、
	 * TutorialLogger.logger.trace(msg);等で呼び出せば事足りるものではある。
	 * 出力するログに、定型文やエラーログなどを含めて出したい場合は、以下のようなメソッドを好きにカスタマイズして、
	 * ログを出したい場所でこのクラスのメソッドを呼ぶようにすると、少し手間を省略できる。
	 */
	public static void trace(String msg) {
		TutorialLogger.logger.trace(msg);
	}
	
	public static void info(String msg) {
		TutorialLogger.logger.info(msg);
	}
	
	public static void warn(String msg) {
		TutorialLogger.logger.warn(msg);
	}

}

TutorialCommonProxy.class

  • Server側のプロキシクラス。TileEntityの登録に使用。
package tutorial.common;

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

public class TutorialCommonProxy {
	
	public World getClientWorld() {
		return null;
	}

	/*
	 * TileEntityのserver側登録メソッド。
	 */
	public void registerTileEntity() {
		GameRegistry.registerTileEntity(TutorialSimpleTile.class, "simpleTile");
	}

}

TutorialClientProxy.class

  • Client側のプロキシクラス。TileEntityの登録に使用。
package tutorial.client;

import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.registry.GameRegistry;
import net.minecraft.world.World;
import tutorial.common.TutorialCommonProxy;
import tutorial.common.tile.TutorialSimpleTile;

public class TutorialClientProxy extends TutorialCommonProxy{
	
	@Override
	public World getClientWorld() {
		return FMLClientHandler.instance().getClient().theWorld;
	}
	
	/*
	 * client側のTileEntity登録メソッド。
	 * ここでは使用していませんが、TileEntityに独自レンダーを持たせたい場合、
	 * ClientRegistry.registerTileEntity(TileEntityのクラス名.class, "TileEntityの名前", new TileEntityのレンダ―クラス());
	 * のように記述して、レンダークラスとTileEntityを登録します。
	 * 
	 * レンダー関係のクラスはクライアント側にしか存在しないため、サーバ側で誤って登録しないよう、(このように)プロキシを通すなどの対策を必ず行います。
	 */
	@Override
	public void registerTileEntity() {
		GameRegistry.registerTileEntity(TutorialSimpleTile.class, "simpleTile");
	}

}

SimpleBlock.class

  • TileEntityを持たせる追加ブロックのクラス。

 このブロックは、プレイヤーの右クリックにより以下の様な動作をする。

  • プレイヤー未登録時
    • プレイヤー名をTileEntityに記憶させる
  • プレイヤー名登録済み
    • 右クリックしたプレイヤーの名前が登録名に一致するかで、異なるメッセージを出力。

 なお、登録した情報は破壊時に消える。

package tutorial.common.block;

import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
import tutorial.common.tile.TutorialSimpleTile;
import net.minecraft.block.Block;
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.client.renderer.texture.IIconRegister;
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.ChatComponentText;
import net.minecraft.world.World;

/*
 * TileEntityを持つブロックです。
 * このMODでは、このブロッククラスを通して、ブロックに含まれているTileEntityを操作します。
 * チュートリアル用として、比較的client、serverの同期処理に悩まずに済む方法にするためです。
 */
public class SimpleBlock extends BlockContainer{
	
	/*
	 * 設置音や素材は羊毛です。
	 */
	public SimpleBlock ()
	{
		super(Material.cloth);
		this.setStepSound(Block.soundTypeCloth);
	}
	
	/*
	 * ブロックの右クリックメソッド。
	 * TileEntityにString(文字列)でプレイヤー名を渡したり、
	 * 格納されたプレイヤー名を元にアクションを行わせる部分です。
	 */
	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();
        /*ブロックと同じ座標にあるTutorialSimpleTileを取得*/
        TutorialSimpleTile tile = (TutorialSimpleTile) par1World.getTileEntity(par2, par3, par4);
        
        /*
         * TutorialSimpleTileがちゃんとそこに有る & サーバ側の場合のみ処理を行う。
         * 
         * サーバ限定にしている理由ですが、
         * 基本的にTileEntityやEntityはサーバ側にのみ存在します。
         * 
         * よって、サーバ側でTileEntityを操作した後、何らかの方法でクライアント側に同期させる、という流れで操作します。
         * (これを怠ると変化が再ログインでリセットされたり、すぐに戻ってしまったり、TileEntityの変化が見た目に反映されなくなったりします。)
         */
        if (tile != null & !par1World.isRemote)
        {
        	/*プレイヤーの手持ちアイテムが無いとき*/
        	if (itemstack == null)
        	{
        		/*Tileに登録されているプレイヤー名。初期状態は「None」*/
        		String currentName = tile.getString();
        		/*右クリックしたプレイヤー名*/
        		String newName = par5EntityPlayer.getDisplayName();
        		
        		/*
        		 * プレイヤー名が未登録状態だった時に限り、最初に右クリックしたプレイヤーをオーナーとして、
        		 * TileEntityにプレイヤー名を登録します。
        		 */
        		if (currentName.equals("None"))
        		{
        			tile.setString(newName);
        			par5EntityPlayer.addChatMessage(new ChatComponentText("AlartBlock: Registered new owner name! " + newName));
        		}
        		/*登録済みプレイヤーがもう一度右クリックした時のメッセージ*/
        		else if (currentName.equals(newName))
        		{
        			par5EntityPlayer.addChatMessage(new ChatComponentText("AlartBlock: Hello, my owner!"));
        		}
        		/*登録済みの時に、登録者以外のプレイヤーが右クリックした時のメッセージ*/
        		else
        		{
        			par5EntityPlayer.addChatMessage(new ChatComponentText("AlartBlock: Hello, stranger!"));
        		}
        	}
        }
        
        /*
         * onBlockActivatedはbooleanですから、trueかfalseを返す必要があります。
         * falseだと処理が行われませんので、trueを返します。
         * trueの場合、プレイヤーは右クリックで腕を振る動作をします。
         * 
         * 動作の条件に一致した時のみtrueを返すやりかたもあります。
         * 今回の場合は、クライアントとサーバでbooleanが食い違うことのないよう、すべての場合でtrueになるようにしています。
         */
        return true;
    }
	
	/*
     * ブロックの設置時に生成されるTileEntityです。
     */
	@Override
	public TileEntity createNewTileEntity(World world, int a) {
		
		return new TutorialSimpleTile();
	}
	
	/*
     * 紫の羊毛ブロックのテクスチャを流用。
     */
	@Override
	@SideOnly(Side.CLIENT)
	public void registerBlockIcons(IIconRegister par1IconRegister)
	{
		this.blockIcon = Blocks.wool.getIcon(1, 2);
	}

}

TutorialSimpleTile.class

  • TileEntityのクラス。ブロックから受け取ったプレイヤー名を保存し、サーバとクライアント間で同期している。
package tutorial.common.tile;

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;

/*
 * TileEntityのクラスです。
 * TileEntityは、Tick毎に特殊な動作をしたり、複雑なモデルを持ったり、
 * NBTを使ってデータを格納したり、色々な用途に使えます。
 * 
 * ただしこのクラス内で行われた処理やデータは基本的にサーバ側にしかないので、
 * 同期処理についてよく考えて実装する必要があります。
 */
public class TutorialSimpleTile extends TileEntity
{
	/*
     * プレイヤー名を格納するString。初期状態では「None」。
     */
    private String name = "None";

    /*
     * NBTの読み取り。
     * 
     * このメソッドでは、NBTを介してString(文字列)を読み込んでいます。
     * 文字列以外に、変数やboolean、ItemStackなども扱えます。
     * 
     * NBTを使えば一時的には記録されますが、
     * チャンク再生成や再ログイン時にデータが消えてしまいます。
     * また、このクラスで行われた処理・サーバ側で行われた処理をクライアント側に反映させるためには、
     * 別途パケット処理も必要です。
     */
    public void readFromNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.readFromNBT(par1NBTTagCompound);      
        this.name = par1NBTTagCompound.getString("String");
    }

    /*
     * こちらはNBTを書き込むメソッド。
     */
    public void writeToNBT(NBTTagCompound par1NBTTagCompound)
    {
        super.writeToNBT(par1NBTTagCompound);
        par1NBTTagCompound.setString("String", this.name);
    }
    
    /*
     * パケットの送信・受信処理。
     * カスタムパケットは使わず、バニラのパケット送受信処理を使用。
     */
    @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());
    }

	/*
     * プレイヤー名文字列のゲッターとセッター。
     */
    public String getString()
    {
        return this.name;
    }
    
    public void setString(String par1)
    {
    	this.name = par1;
    }
    
    public int getMetadata()
    {
    	return this.worldObj.getBlockMetadata(xCoord, yCoord, zCoord);
    }
}