このページはMinecraft Forge WikiのHow to use NBT Tag Compoundの訳です。
目次
NBT Tag Compoundの使い方[編集]
最終的な目標[編集]
最後にブロックを右クリックした5人のプレイヤーを記録する "last 5 visitors"ブロックを作成します
前提となる知識[編集]
NBTとは?[編集]
NBTとは、Minecraftのマップ保存の為にNotchが作成したフォーマットです。ノードを基にしたファイルで、regionファイル、player.datファイル、level.datファイルに使われています。
バイナリだということを除けば、NBTの構造はとてもXMLに似ていると考えることができます。
どのNBTファイルも必ず"Tag_Compound"という種類のルートタグから始まっていて、それがこのチュートリアルの名前になっています。1つのTag_Compoundは他のノードをその中に保持することができます。そのルートタグは通常名前を持ちません。
NBTを使う理由[編集]
NBTはブロックやアイテムのデータ/メタデータに保存出来ないものを保存するのにとても優れています。例えば、チェストの全アイテムの情報は4ビットのメタデータには保存出来ません。でも、NBTなら実質無限なのです。ただ、HDDの容量が実に増えているとは言え、大量に自然に現れるブロックにNBTを使用してはいけません。
また、NBTはアイテムにも便利です。例えば、本は作者と内容を保存するためにNBTを使用しています。
MinecraftのどこにNBTが使われているでしょう?[編集]
- 看板
- 本
- インベントリを持つ全てのブロック(かまど、チェスト、などなど)
タグの種類[編集]
NBTには12種類ものタグがあり、様々なデータが格納可能です:
ID | 名前 | 解説 |
0 | TAG_End | Compound Tagの終わり。通常見ることはない。 |
1 | TAG_Byte | 1バイトのデータを含むタグ |
2 | TAG_Short | 1つのshort(数値)を含むタグ |
3 | TAG_Int | 1つのintegerを含むタグ |
4 | TAG_Long | 1つのlongを含むタグ |
5 | TAG_Float | 1つのfloatを含むタグ |
6 | TAG_Double | 1つのdoubleを含むタグ |
7 | TAG_Byte_Array | byteの配列を含むタグ |
8 | TAG_String | 文字列を含むタグ |
9 | TAG_List | 同じ種類の無名タグのリスト |
10 | TAG_Compound | 名前付きタグのリスト。それぞれのNBTファイルの起点。他のCompoundを含むことが出来る。 |
11 | TAG_Int_Array | integerの配列 |
MinecraftCoalitionのNBTに関する情報はこのページの「参照」以下より参照してください。
ブロックのクラスを書く[編集]
もし作っているのがアイテムなら、ItemStackからCompound Tagを取得出来ます。それについてのチュートリアルを作成する必要があるでしょう。でも今はブロックで忙しいのです。
NBTの値を保存するためにはTileEntityというものが必要です。TileEntityは基本的にブロックと繋がったエンティティで、ブロックの情報を保存するために使われます。
最初に、コンストラクタを作ります。このクラスはBlockContainerを継承することに注意してください。
public BlockVisitor(int id) { super(id, 35, Material.circuits); // TODO Auto-generated constructor stub }
これについては説明する必要は無いでしょう。
次は、右クリックを検知するメソッドです。
public boolean onBlockActivated(World world, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) { if(!world.isRemote) { TileEntityVisitor t = (TileEntityVisitor) world.getBlockTileEntity(par2, par3, par4); t.processActivate(par5EntityPlayer, world); } return true; }
行ごとに見て行きましょう。
if(!world.isRemote)
この行は、メソッドを呼び出しているのがサーバーかクライアントかを確認します。isRemoteはクライアントではtrueを返します。これは、1.2.5以降ではシングルでもローカルサーバー上で実行されているためです。つまり、これはサーバー/クライアントを検知する簡単な方法なのです。
TileEntityVisitor t = (TileEntityVisitor) world.getBlockTileEntity(par2, par3, par4);
この行では変数tにgetBlockTileEntityメソッドを用いてワールドからTileEntityを取得します。par2、3、4はx、y、z座標を表します。 TileEntityVisitorにキャストしていることに注意してください。
t.processActivate(par5EntityPlayer, world);
これは、登録と表示のためにTileEntityに設けた、processActivateというメソッドを呼び出しています。引数はプレイヤーとワールドです。
また、MinecraftにTileEntityを使うことを知らせるためにメソッドを追加する必要があります:
public boolean hasTileEntity(int metadata) { return true; }
ブロックのファイルの最後のメソッドはcreateNewTileEntity()です。これは非常に単純で、TileEntityVisitorの新しいインスタンスを返すのみとなっています:
public TileEntity createNewTileEntity(World par1World) { try { return new TileEntityVisitor(); } catch (Exception var3) { throw new RuntimeException(var3); } }
これでブロックのファイルについては終わりました。TileEntityに移りましょう。
TileEntityを作る[編集]
TileEntityはコンストラクタを必要としません。実際、最初にするのは訪問者を保持する変数の定義です:
String visitor1="none"; String visitor2="none"; String visitor3="none"; String visitor4="none"; String visitor5="none";
配列は使いません。何か問題でも?
次はNBT Tag Compoundから情報を読み込むメソッドを書きます:
@Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); this.visitor1 = nbt.getString("visitor1"); this.visitor2 = nbt.getString("visitor2"); this.visitor3 = nbt.getString("visitor3"); this.visitor4 = nbt.getString("visitor4"); this.visitor5 = nbt.getString("visitor5"); }
お分かりの通り、読み込みのためにNBTTagComponundを使います。コードについては自明です。注意として、super.readFromNBT(TagCompound);の実行を忘れないでください。幾つもの情報をTileEntity自身が保存し、読み込んでいるからです。
次のメソッドはNBTへの保存です:
@Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); nbt.setString("visitor1", visitor1); nbt.setString("visitor2", visitor2); nbt.setString("visitor3", visitor3); nbt.setString("visitor4", visitor4); nbt.setString("visitor5", visitor5); }
このコードもまた自明です。
最後のコードはブロックの方で呼び出されていたメソッドです。
public void processActivate(EntityPlayer par5EntityPlayer, World world) { if(!visitor1.equals(par5EntityPlayer.getEntityName())) { visitor5=visitor4; visitor4=visitor3; visitor3=visitor2; visitor2=visitor1; visitor1=par5EntityPlayer.getEntityName(); } //System.out.println("Visitors: " + visitor1 + ", " + visitor2 + ", " + visitor3 + ", " + visitor4 + ", " + visitor5); par5EntityPlayer.addChatMessage("Visitors: " + visitor1 + ", " + visitor2 + ", " + visitor3 + ", " + visitor4 + ", " + visitor5); world.notifyBlockChange(xCoord, yCoord, zCoord, 2); }
これには幾らかの説明が必要でしょう:
if(!visitor1.equals(par5EntityPlayer.getEntityName()))
これは、同じ訪問者を2度登録させないためです。
visitor5=visitor4; visitor4=visitor3; visitor3=visitor2; visitor2=visitor1; visitor1=par5EntityPlayer.getEntityName();
これは訪問者一覧を入れ替えます。
world.notifyBlockChange(xCoord, yCoord, zCoord, 2);
安全のためです。このメソッドはワールドに周囲のブロックを更新することを伝えます。チャットに訪問者を表示していることにも注意してください。
全てのコード[編集]
VisitorBlock.java(メインのコード)
package com.generic.tutorial.nbt.block; import net.minecraft.src.*; import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod.Init; import cpw.mods.fml.common.Mod.Instance; import cpw.mods.fml.common.Mod.PreInit; import cpw.mods.fml.common.event.FMLInitializationEvent; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.network.NetworkMod; import cpw.mods.fml.common.registry.GameRegistry; @Mod(modid="VisitorBlock", name="VisitorBlock", version="0.0.1") @NetworkMod(clientSideRequired=true, serverSideRequired=false) public class VisitorBlock { @Instance("VisitorBlock") VisitorBlock instance; BlockVisitor visitor; int blockID=200; @PreInit public void preInit(FMLPreInitializationEvent event) { } @Init public void load(FMLInitializationEvent event) { visitor = new BlockVisitor(blockID); GameRegistry.registerBlock(visitor); GameRegistry.registerTileEntity(TileEntityVisitor.class, "visitorBlock"); GameRegistry.addRecipe(new ItemStack(visitor, 1), "###", "#%#", "###", '#', Item.redstone, '%', Item.paper); } }
BlockVisitor.java (ブロックのクラス)
package com.generic.tutorial.nbt.block; import net.minecraft.src.Block; import net.minecraft.src.BlockContainer; import net.minecraft.src.EntityPlayer; import net.minecraft.src.Material; import net.minecraft.src.TileEntity; import net.minecraft.src.World; public class BlockVisitor extends BlockContainer { public BlockVisitor(int id) { super(id, 35, Material.circuits); // TODO Auto-generated constructor stub } public boolean onBlockActivated(World world, int par2, int par3, int par4, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9) { if(!world.isRemote) { TileEntityVisitor t = (TileEntityVisitor) world.getBlockTileEntity(par2, par3, par4); t.processActivate(par5EntityPlayer, world); } return true; } public boolean hasTileEntity(int metadata) { return true; } @Override public TileEntity createNewTileEntity(World par1World) { try { return new TileEntityVisitor(); } catch (Exception var3) { throw new RuntimeException(var3); } } }
TileEntityVisitor.java
package com.generic.tutorial.nbt.block; import net.minecraft.src.*; public class TileEntityVisitor extends TileEntity{ String visitor1="none"; String visitor2="none"; String visitor3="none"; String visitor4="none"; String visitor5="none"; public void processActivate(EntityPlayer par5EntityPlayer, World world) { if(!visitor1.equals(par5EntityPlayer.getEntityName())) { visitor5=visitor4; visitor4=visitor3; visitor3=visitor2; visitor2=visitor1; visitor1=par5EntityPlayer.getEntityName(); } //System.out.println("Visitors: " + visitor1 + ", " + visitor2 + ", " + visitor3 + ", " + visitor4 + ", " + visitor5); par5EntityPlayer.addChatMessage("Visitors: " + visitor1 + ", " + visitor2 + ", " + visitor3 + ", " + visitor4 + ", " + visitor5); world.notifyBlockChange(xCoord, yCoord, zCoord, 2); } @Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); this.visitor1 = nbt.getString("visitor1"); this.visitor2 = nbt.getString("visitor2"); this.visitor3 = nbt.getString("visitor3"); this.visitor4 = nbt.getString("visitor4"); this.visitor5 = nbt.getString("visitor5"); } @Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); nbt.setString("visitor1", visitor1); nbt.setString("visitor2", visitor2); nbt.setString("visitor3", visitor3); nbt.setString("visitor4", visitor4); nbt.setString("visitor5", visitor5); } }
注意事項[編集]
Forge(訳注:これ自体はFML)を使ってTileEntityを登録する必要があります:
GameRegistry.registerTileEntity(TileEntityVisitor.class, "visitorBlock");