8日目 Tile Entityの追加 (1.15)
タイルエンティティの追加の1.15バージョンを書いていきます。
タイルエンティティは情報を持ったブロックで、チェストやかまど等のように多くの場合にインベントリとGUIを持ちます。
今回も最終的にはインベントリとGUIを持ったタイルエンティティを追加しますが、一度に書くと長くなるので何回かに分けて書こうと思います。
初回は、GUIもインベントリも持たない基本的なタイルエンティティの追加を行ないます。
機能は右クリックされた回数を記録し、破壊されたときにその数だけカメのタマゴをドロップさせます。
タイルエンティティを作成するためには、タイルエンティティ自体のクラスとタイルエンティティと紐付けるブロックのクラスが必要になります。
今回は、TestTileEntityとTestTileBlockを作成します。
まず、TestTileEntityから。
public class TestTileEntity extends TileEntity { private int count = 0; public TestTileEntity() { super(ModTileEntities.TEST_TILE); } @Override public CompoundNBT write(CompoundNBT compound) { compound.putInt("T_count", count); System.out.println("write"); return super.write(compound); } @Override public void read(CompoundNBT compound) { System.out.println("read"); super.read(compound); count = compound.getInt("T_count"); } public void increment() { count++; } public int getCount() { return count; } }
回数を記録するためのcountと、カウントアップのためのincrementメソッド、回数を得るためのgetterを持ちます。
writeメソッドとreadメソッドでは、それぞれnbtに回数を記録、またはnbtから回数を読み込んでいます。
これによって各ブロックに情報を持たせることができ、セーブした際に情報もセーブされるようになります。
続いて、TestTileBlockです。
public class TestTileBlock extends Block { public TestTileBlock(Properties properties, String name) { super(properties); setRegistryName(TestMod.MODID, name); } @Override public boolean hasTileEntity(BlockState state) { return true; } @Override public TileEntity createTileEntity(BlockState state, IBlockReader world) { return ModTileEntities.TEST_TILE.create(); } @Override public ActionResultType func_225533_a_(BlockState state, World world, BlockPos pos, PlayerEntity player, Hand hand, BlockRayTraceResult result) { TileEntity te = world.getTileEntity(pos); if(te instanceof TestTileEntity && !world.isRemote) { ((TestTileEntity) te).increment(); System.out.println(((TestTileEntity) te).getCount() +", "+pos); } return ActionResultType.SUCCESS; } @SuppressWarnings("deprecation") @Override public void onReplaced(BlockState state, World world, BlockPos pos, BlockState newState, boolean isMoving) { if(world.getTileEntity(pos) instanceof TestTileEntity && !world.isRemote) { TestTileEntity te = (TestTileEntity) world.getTileEntity(pos); world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), new ItemStack(Items.TURTLE_EGG, te.getCount()))); } super.onReplaced(state, world, pos, newState, isMoving); } }
hasTileEntity、createTileEntityでこのブロックをタイルエンティティを紐付けるます。
登録用のクラス (後述) からTileEntityTypeを指定して、create()でタイルエンティティが生成されます。
func_225533_a_が右クリックされたときの挙動です。
onReplacedでタイルエンティティが壊されたり、置き換えられたときの挙動を設定します。
スーパークラスで、壊されたときのタイルエンティティの除去を行なっているので、スーパークラスのメソッドも呼んでいます。
最後にブロックとタイルエンティティを登録します。
EventBusSubscriberをつけたどこのクラスでもいいのですが、今回はModTileEntitiesというクラスで登録を行ないました。
@Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD) public class ModTileEntities { public static final TestTileBlock TEST_TILE_BLOCK = new TestTileBlock(Block.Properties.create(Material.PLANTS), "test_tile"); public static final TileEntityType<TestTileEntity> TEST_TILE = TileEntityType.Builder.create(TestTileEntity::new, TEST_TILE_BLOCK).build(null); @SubscribeEvent public static void registerBlocks(final RegistryEvent.Register<Block> event) { event.getRegistry().register(TEST_TILE_BLOCK); } @SubscribeEvent public static void registerBlockItems(final RegistryEvent.Register<Item> event) { event.getRegistry().register(new BlockItem(TEST_TILE_BLOCK, new Item.Properties().group(TestItemGroups.TEST_GROUP)).setRegistryName(TEST_TILE_BLOCK.getRegistryName())); } @SubscribeEvent public static void registerTileEntity(final RegistryEvent.Register<TileEntityType<?>> event) { TEST_TILE.setRegistryName(TestMod.MODID, "test_tile"); event.getRegistry().register(TEST_TILE); } }
ブロックの登録は通常通りです。
タイルエンティティは、TileEntityTypeとして登録します。
Builder.createの引数に(TileEntityのクラスのコンストラクタ参照, ブロックのインスタンス) を渡します。
第一引数がSupplierなので、目的のTileEntityクラスに引数なしのコンストラクタがない場合には、第一引数をラムダ式で記述する必要があります。
無事に登録出来ていれば、画像のように動作します。
各座標に置いたブロックが、それぞれ固有の情報を持っています。
また破壊するとカウントの数分のタマゴをドロップします。
テクスチャ等は今回指定していませんが、ブロックのテクスチャと同様に設定できます。
次回は、このタイルエンティティにインベントリを追加します。