9日目 Tile Entityへのインベントリの追加
先日作成したTileEntityにインベントリを追加しようと思います。
インベントリ自体はTileEntityクラスのみ編集することで追加できます。
今回はそれに加えてインベントリにアクセスするための機能をブロッククラスに追加していきます。
インベントリを実装するためのLazyOptional
public class TestTileEntity extends TileEntity { private int count = 0; private LazyOptional<ItemStackHandler> itemHandler = LazyOptional.of(() -> new ItemStackHandler(1)); public TestTileEntity() { super(ModTileEntities.TEST_TILE); } @Override public CompoundNBT write(CompoundNBT compound) { compound.putInt("T_count", count); itemHandler.ifPresent(h -> { compound.put("Items", h.serializeNBT() ); }); 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"); itemHandler.ifPresent(h -> { if(compound.contains("Items")) { h.deserializeNBT(compound.getCompound("Items")); } }); } public void increment() { count++; } public int getCount() { return count; } @Override public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) { if(cap == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) { return itemHandler.cast(); } return super.getCapability(cap, side); } }
4行目でインベントリを持たせるためのItemStackHandlerを作成しています。今回は1スロットのみ追加しています。ItemStackHandlerのコンストラクターの引数でスロット数を指定できます。
ItemHandlerを他のクラスに渡すためのgetCapabilityメソッドがLazyOptional
続いて、ItemStackHandlerを保存するために、NBTcompoundのwrtie (15行目~) およびread (29行目~) に書き加えます。
ItemStackHandlerを丸ごとシリアライズしてNBTに書き込み、読み込むときはNBTからItemStackHandlerにデシリアライズします。
ただし読み込みに関して、初回読み込み時にはNBTがnullになってしまうのでnullチェックを入れています。
最後に、このクラスのItemStackHandlerを他のクラスに渡すためのgetCapabilityメソッドをオーバーライドします (49行目~)。
引数のcapabilityがITEM_HANDLER_CAPABILITYである場合には、このクラスのLazyOptionalをIItemHandlerにキャストして渡し、それ以外の場合ではスーパークラスの同メソッドを呼び出します。
これでタイルエンティティへインベントリが追加されました。
続いて、Blockクラスの編集を行ないます。
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(); te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).<ItemStackHandler>cast() .ifPresent(h -> { h.setStackInSlot(0, new ItemStack(Items.DIAMOND, ((TestTileEntity) te).getCount())); } ); 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); te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).ifPresent(h -> { world.addEntity(new ItemEntity(world, pos.getX(), pos.getY(), pos.getZ(), h.getStackInSlot(0))); }); } super.onReplaced(state, world, pos, newState, isMoving); } }
まず、ブロックを右クリックしたときの挙動として、29~34行目の処理を追加しました。
getCapabilityでIItemHandler型としてTestTileEntityのItemStackHandlerを受け取り、それをItemStackHandlerにキャストします (IItemHandlerにはsetStackInSlotメソッドが存在しないため)。
そして、setStackInSlotを使ってcountの数だけダイアモンドをスロットにセットします。
次に、このブロックが壊されたときの挙動として、49~52行目の処理を追加しました。
先程と同様に、getCapabilityでIItemHandlerを受け取ります。
getStackInSlotはIItenHandlerにも実装されているのでキャストなしで使います。
インベントリ内のアイテムスタックをアイテムエンティティとして生成し、コンソールにアイテムと個数を表示するようにしました。
これで実際に動かしてみます。
12回クリックした後、一度セーブします。
その後、再びログインしブロックを破壊します。
すると、12個のダイヤモンドがドロップし、コンソールにもそのように表示されています。
ちゃんとインベントリの内容が保存されています。
次回は、このインベントリをもつタイルエンティティにGUIを実装したいと思います。