« Minecraft MOD開発 14 | トップページ | Minecraft MOD開発 16 »

2014年4月 6日 (日)

Minecraft MOD開発 15

onBlockActivatedでtrueを返すようにしたら、ブロック固有の情報が保存されるようになった。当面の目標としていたことは実現できた。チュートリアルが参考になった。

機能仕様

  • ピンク色をした独自ブロックを追加する
  • 独自ブロックを作成するレシピは無い(クリエイティブ専用)
  • ワールドに設置されたブロックは、ブロックごとにカウンタを持つ
  • プレイヤーが右クリックすると、ブロックのカウンタがインクリメントされるとともにチャットメッセージで表示される
  • 制限: クライアントとサーバの区別をしていないので、チャットメッセージは二重に表示される
  • ブロックに保持されたカウンタの値は、ゲームをセーブロードしても保持され続ける

開発/動作環境

OS
Windows 7 Professional (64bit)
JDK
1.7.0_51-b13 (64bit)
Minecraft
1.6.4
Minecraft Forge
1.6.4-9.11.1.965
Eclipse
20140224-0627
併用MOD
NotEnoughItems: 1.6.1.9

ソースコード

BlockCounterCore.java

// このクラスはBaseMODとかCoreMODと呼ばれるものです。
// @Modアノテーションを付けることで、ForgeはこのクラスがCoreMODであることを認識します。
// 複数のブロックを登録するようなMODでも、CoreMODは一つだけにします。
// ゲームの起動時に呼ばれブロックの登録などをしますが、
// その後は基本的に呼ばれることはありません。
 
// このファイルで使用する名前空間を指定します。
// 同じ名前空間に所属するクラスはimportすることなく使用できます。
package nishina;
 
// このファイルで参照するクラスをimportします。
// importが不足すると、"cannot be resolved to a type"というエラーメッセージが出ます。
import net.minecraft.block.Block;
import net.minecraft.block.material.Material;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.Mod.EventHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.registry.GameRegistry;
import cpw.mods.fml.common.registry.LanguageRegistry;
 
@Mod(modid = "CounterBlockMod", name = "CounterMod", version = "1.0")
public class BlockCounterCore
{
 public static Block block;
 public static int blockID = 3500; // 4096未満の他のブロックと重複しない整数
 
 // 各初期化イベントがいつのタイミングで呼ばれるのかは私もよく分かりませんが、
 // 他のMODのアイテムを使うレシピを登録する可能性を考えると、
 // preInitでブロック/アイテムを登録し、initでレシピを登録するのが正しいでしょう。
 @EventHandler
 public void preInit(FMLPreInitializationEvent event)
 {
  // ブロックの登録
  // マテリアルとはブロックが燃えるか、ピストンで押せるかなどの性質をまとめたもの
  // ここでは石ブロックと同じ性質で良いので流用している
  block = new BlockCounter( blockID, Material.rock);
  GameRegistry.registerBlock( block, "blockCounter");
 }
 
 @EventHandler
 public void init(FMLInitializationEvent event)
 {
  // 表示名の登録
  LanguageRegistry.addName( block, "Counter Block");
  LanguageRegistry.instance().addNameForObject( block, "ja_JP", "カウンタ ブロック");
  
  // BlockCounterの独自情報を保持するTileEntityCounterを登録
  GameRegistry.registerTileEntity( TileEntityCounter.class, "TileEntityCounter");
 }
 
}

BlockCounter.java

// このクラスはBlockCounterの動作を定義します。
// ブロックの種類ごとにクラスを1つ作ります。
// metadata等を利用して、複数種類のブロックを1つのクラスで実現することも可能です。
// ブロックが右クリックされたときはonBlockActivatedが呼ばれるなど、
// ゲーム中でのブロックの動作を記述します。
 
package nishina;
 
import net.minecraft.block.BlockContainer;
import net.minecraft.block.material.Material;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChatMessageComponent;
import net.minecraft.world.World;
 
// TileEntityを持つブロックはBlockContainerから継承します。
public class BlockCounter extends BlockContainer
{
 
 // idとmaterialを基底クラスのコンストラクタへ渡して自分自身を設定します。
 public BlockCounter(int id, Material material)
 {
  super( id, material);
  this.setCreativeTab( CreativeTabs.tabBlock);
  this.setUnlocalizedName( "Counter Block");
  // 開発環境ではテクスチャをpink_block.pngを
  // mcp\bin\minecraft\assets\nishina\textures\blocksへ置きます。
  // (mcp\jar下ではありません)
  this.setTextureName( "nishina:pink_block");
 }
 
 // このブロックが右クリックされたときに呼ばれる
 @Override
 public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer entityPlayer, int side, float hitX, float hitY, float hitZ)
 {
  // worldからTileEntityを取得して、TileEntityCounterへキャストします。
  TileEntityCounter tileentitycounter = (TileEntityCounter) world.getBlockTileEntity( x, y, z);
  // 右クリックされたブロックに関連づけられたcounterの値をインクリメントします。
  tileentitycounter.incrementCount();
  // counterの値を取得します。
  int counter = tileentitycounter.getCount();
  // ブロック固有のカウンタ値をチャットメッセージの形で表示します。
  // サーバとクライアントの場合分けをしていないので2回表示されます。
  entityPlayer.sendChatToPlayer( new ChatMessageComponent().createFromText( new Integer( counter).toString()));
  // ここでtrueを返さないとTileEntityの内容がセーブされません。
  return true;
 }
 
 @Override
 public TileEntity createNewTileEntity(World world)
 {
  // class BlockCounterとclass TileEntityCounterを関連づけます。
  return new TileEntityCounter();
 }
 
}

TileEntityCounter.java

// このクラスはBlockCounterに関連づけられ、ブロック固有の情報(右クリックされた回数)を保持します。
// ワールドに設置されたブロックの数だけインスタンスが作られます。
 
package nishina;
 
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.INetworkManager;
import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.Packet132TileEntityData;
import net.minecraft.tileentity.TileEntity;
 
// ブロック固有の情報を保持するクラスはTileEntityを継承して作ります。
public class TileEntityCounter extends TileEntity
{
 // ブロックが右クリックされた回数を保持するカウンタ。
 // Javaの仕様により構築時に0で初期化されることを期待しています。
 private int counter;
 
 public TileEntityCounter()
 {
  super();
 }
 
 // ブロック固有の情報(counter)をNBTTagから読み出します。
 // ブロックの情報を書き換えるので、onInventoryChangedも呼びます。
 @Override
 public void readFromNBT(NBTTagCompound par1nbtTagCompound)
 {
  counter = par1nbtTagCompound.getInteger( "count");
  super.readFromNBT( par1nbtTagCompound);
  onInventoryChanged();
 }
 
 // ブロック固有の情報(counter)をNBTTagへ書き込みます。
 @Override
 public void writeToNBT(NBTTagCompound par1nbtTagCompound)
 {
  par1nbtTagCompound.setInteger( "count", counter);
  super.writeToNBT( par1nbtTagCompound);
 }
 
 // 右クリックされた回数を返すgetter
 public int getCount()
 {
  return counter;
 }
 
 // 右クリックされたときに呼ばれるカウンタ値をインクリメントするメソッド。
 // ブロックの情報を書き換えるので、onInventoryChangedも呼びます。
 public void incrementCount()
 {
  counter++;
  onInventoryChanged();
 }
 
 // このTileEntityをパケットの形式で表現します。
 // Minecraft本体から適当なタイミングで呼ばれ、
 // クライアント-サーバの同期に使われるはずです。
 public Packet getDescriptionPacket()
 {
  NBTTagCompound nbtTag = new NBTTagCompound();
  writeToNBT( nbtTag);
  return new Packet132TileEntityData( xCoord, yCoord, zCoord, 1, nbtTag);
 }
 
 // パケットを受信したときに呼ばれTileEntityの内容を設定します。
 // Minecraft本体から適当なタイミングで呼ばれ、
 // クライアント-サーバの同期に使われるはずです。
 @Override
 public void onDataPacket(INetworkManager net, Packet132TileEntityData pkt)
 {
  readFromNBT( pkt.data);
 }
 
}

マインクラフトは最大で4096個のclass Blockを持つ。class Blockはブロックの種類毎にインスタンス化される。ワールドに設置されたブロックに固有の情報を持つものはclass BlockContainerにする。ブロック固有の情報は、TileEntityに保持される。

動作から推測したシーケンス図。

アセット

以下のファイルをmcp\bin\minecraft\assets\nishina\textures\blocks\pink_block.pngに置きます。

スクリーンショット

カウンタが2回表示されるのは仕様です。

« Minecraft MOD開発 14 | トップページ | Minecraft MOD開発 16 »

コメント

コメントを書く

コメントは記事投稿者が公開するまで表示されません。

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.f.cocolog-nifty.com/t/trackback/1499066/55667575

この記事へのトラックバック一覧です: Minecraft MOD開発 15:

« Minecraft MOD開発 14 | トップページ | Minecraft MOD開発 16 »