- Версия(и) Minecraft
- 1.12.х
В этом уроке мы объясняем, как вы можете сделать сундук, который будет содержать девять слотов для хранения предметов (это же всё, что тебе нужно, верно?), и как сделать графический интерфейс, чтобы вы могли получить доступ к этим предметам.
Вот код блока:
В этом коде мы также регистрируем
Не забудьте правильно зарегистрировать блок в
И добавьте
Вот состояние блока в .json (blockstates/testcontainerblock.json):
Обратите внимание, что в этом примере у нас нет собственной модели блока, и вместо этого используется стандартная модель
Однако этого недостаточно. Для работы графического интерфейса нам необходим реальный код этого самого графического интерфейса, а также реализация контейнера. Несмотря на то, что объект
Когда графический интерфейс открывается в игре, это обычно происходит на стороне сервера. Там реализация контейнера используется, чтобы указать, какой инвентарь открывается. На стороне клиента также используется синхронизированная копия этого контейнера и передается фактическому графическому интерфейсу.
Здесь вы видите контейнер для этого примера. Обратите внимание, что добавляются слоты как для нашего собственного
Крайне важно, чтобы вы реализовали
Теперь нам нужен графический интерфейс. Графические интерфейсы для контейнеров обычно берутся из
Теперь осталась последняя вещь которую вам нужно сделать, это сказать игре, что вы создали свой графический интерфейс и реализовали для него контейнер. Для этого необходим
И, наконец, вам нужно зарегистрировать этот класс
Конец!
Вот код блока:
TestContainerBlock.java:
public class TestContainerBlock extends Block implements ITileEntityProvider {
public static final int GUI_ID = 1;
public TestContainerBlock() {
super(Material.ROCK);
setUnlocalizedName(ModTut.MODID + ".testcontainerblock");
setRegistryName("testcontainerblock");
}
@SideOnly(Side.CLIENT)
public void initModel() {
ModelLoader.setCustomModelResourceLocation(Item.getItemFromBlock(this), 0, new ModelResourceLocation(getRegistryName(), "inventory"));
}
@Override
public TileEntity createNewTileEntity(World worldIn, int meta) {
return new TestContainerTileEntity();
}
@Override
public boolean onBlockActivated(World world, BlockPos pos, IBlockState state, EntityPlayer player, EnumHand hand, ItemStack heldItem, EnumFacing side, float hitX, float hitY, float hitZ) {
//Выполнять только на стороне сервера:
if (world.isRemote) {
return true;
}
TileEntity te = world.getTileEntity(pos);
if (!(te instanceof TestContainerTileEntity)) {
return false;
}
player.openGui(ModTut.instance, GUI_ID, world, pos.getX(), pos.getY(), pos.getZ());
return true;
}
}
TileEntity
. В методе onBlockActivation ()
мы фактически открываем графический интерфейс. Обратите внимание, что это делается на сервере, потому что графический интерфейс для TileEntity
должен быть открыт с обеих сторон, чтобы был механизм для синхронизации содержимого. Вызов player.openGui ()
происходит на сервере, и он будет синхронизироваться с клиентом с помощью IGuiHandler
(чуть поподробнее о нём позже). Сделано это для отображения содержимого в режиме реального времени когда открыт графический интерфейс.Не забудьте правильно зарегистрировать блок в
CommonProxy
:
CommonProxy.java:
@Mod.EventBusSubscriber
public class CommonProxy {
...
@SubscribeEvent
public static void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().register(new TestContainerBlock());
GameRegistry.registerTileEntity(TestContainerTileEntity.class, ModTut.MODID + "_testcontainerblock");
...
}
@SubscribeEvent
public static void registerItems(RegistryEvent.Register<Item> event) {
event.getRegistry().register(new ItemBlock(ModBlocks.testContainerBlock).setRegistryName(ModBlocks.testContainerBlock.getRegistryName()));
...
}
}
И добавьте
ObjectHolder
c вызовом initModel ()
для ModBlocks
:
ModBlocks.java:
public class ModBlocks {
@GameRegistry.ObjectHolder("modtut:testcontainerblock")
public static TestContainerBlock testContainerBlock;
...
@SideOnly(Side.CLIENT)
public static void initModels() {
...
testContainerBlock.initModel();
}
}
TileEntity
- это то, что на самом деле хранит предметы. В этом уроке мы не будем использовать ванильную систему IInventory
, а вместо этого реализуем наш инвентарь, используя новую рекомендованную систему возможностей IItemHandler
.
TestContainerTileEntity.java:
public class TestContainerTileEntity extends TileEntity {
public static final int SIZE = 9;
//Этот обработчик предметов будет coдержать наши девять слотов инвентаря
private ItemStackHandler itemStackHandler = new ItemStackHandler(SIZE) {
@Override
protected void onContentsChanged(int slot) {
//Нам нужно сообщить тайлу, что что-то изменилось, чтобы содержимое сундука сохранилось
TestContainerTileEntity.this.markDirty();
}
};
@Override
public void readFromNBT(NBTTagCompound compound) {
super.readFromNBT(compound);
if (compound.hasKey("items")) {
itemStackHandler.deserializeNBT((NBTTagCompound) compound.getTag("items"));
}
}
@Override
public NBTTagCompound writeToNBT(NBTTagCompound compound) {
super.writeToNBT(compound);
compound.setTag("items", itemStackHandler.serializeNBT());
return compound;
}
public boolean canInteractWith(EntityPlayer playerIn) {
//Если мы слишком далеко от блока тайла, мы не сможем с ним взаимодействовать
return !isInvalid() && playerIn.getDistanceSq(pos.add(0.5D, 0.5D, 0.5D)) <= 64D;
}
@Override
public boolean hasCapability(Capability<?> capability, EnumFacing facing) {
if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
return true;
}
return super.hasCapability(capability, facing);
}
@Override
public <T> T getCapability(Capability<T> capability, EnumFacing facing) {
if (capability == CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) {
return CapabilityItemHandler.ITEM_HANDLER_CAPABILITY.cast(itemStackHandler);
}
return super.getCapability(capability, facing);
}
}
Вот состояние блока в .json (blockstates/testcontainerblock.json):
testcontainerblock.json:
{
"forge_marker": 1,
"defaults": {
"model": "cube_all",
textures: { "all":"modtut:blocks/testcontainer"}
},
"variants": {
"normal": [{}],
"inventory": [{}]
}
}
cube_all
Однако этого недостаточно. Для работы графического интерфейса нам необходим реальный код этого самого графического интерфейса, а также реализация контейнера. Несмотря на то, что объект
TileEntity
содержит фактическое содержимое, именно контейнер используется для связи между графическим интерфейсом пользователя и фактическим содержимым на сервере. Помните, что когда вы открываете графический интерфейс допустим для сундука, вы видите не только слоты из самого сундука, но и слоты из инвентаря игрока. Таким образом, контейнер представляет собой как минимум две инвентаря в этом случае.Когда графический интерфейс открывается в игре, это обычно происходит на стороне сервера. Там реализация контейнера используется, чтобы указать, какой инвентарь открывается. На стороне клиента также используется синхронизированная копия этого контейнера и передается фактическому графическому интерфейсу.
Здесь вы видите контейнер для этого примера. Обратите внимание, что добавляются слоты как для нашего собственного
TileEntity
, так и для инвентаря игрока.Крайне важно, чтобы вы реализовали
TransferStackInSlot
. Если вы этого не сделаете, ваш блок вызовет вылет из игры, если игрок нажмет Shift на предмете. В этом примере мы скопировали реализацию, которая также используется ванильным сундуком. Он в основном передает слоты между хотбаром/инвентарем/сундуком игрока в зависимости от того, где начался предмет и где есть место.
TestContainer.java:
public class TestContainer extends Container {
private TestContainerTileEntity te;
public TestContainer(IInventory playerInventory, TestContainerTileEntity te) {
this.te = te;
//Этот контейнер ссылается на предметы из нашего инвентаря блока (9 слотов), а также слоты из инвентаря игрока, чтобы пользователь мог перемещать предметы между обоими инвентарями.
//Два вызова снижу гарантируют, что слоты определены для обоих инвентарёв:
addOwnSlots();
addPlayerSlots(playerInventory);
}
private void addPlayerSlots(IInventory playerInventory) {
//Слоты основного инвентаря:
for (int row = 0; row < 3; ++row) {
for (int col = 0; col < 9; ++col) {
int x = 9 + col * 18;
int y = row * 18 + 70;
this.addSlotToContainer(new Slot(playerInventory, col + row * 9 + 10, x, y));
}
}
//Слоты хотбара:
for(int row = 0; row < 9; ++row) {
int x = 9 + row * 18;
int y = 58 + 70;
this.addSlotToContainer(new Slot(playerInventory, row, x, y));
}
}
private void addOwnSlots() {
IItemHandler itemHandler = this.te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null);
int x = 9;
int y = 6;
//Добавить наши новые собственные слоты:
int slotIndex = 0;
for (int i = 0; i < itemHandler.getSlots(); i++) {
addSlotToContainer(new SlotItemHandler(itemHandler, slotIndex, x, y));
slotIndex++;
x += 18;
}
}
@Override
public ItemStack transferStackInSlot(EntityPlayer playerIn, int index) {
ItemStack itemstack = ItemStack.EMPTY;
Slot slot = this.inventorySlots.get(index);
if (slot != null && slot.getHasStack()) {
ItemStack itemstack1 = slot.getStack();
itemstack = itemstack1.copy();
if (index < TestContainerTileEntity.SIZE) {
if (!this.mergeItemStack(itemstack1, TestContainerTileEntity.SIZE, this.inventorySlots.size(), true)) {
return ItemStack.EMPTY;
}
} else if (!this.mergeItemStack(itemstack1, 0, TestContainerTileEntity.SIZE, false)) {
return ItemStack.EMPTY;
}
if (itemstack1.isEmpty()) {
slot.putStack(ItemStack.EMPTY);
} else {
slot.onSlotChanged();
}
}
return itemstack;
}
@Override
public boolean canInteractWith(EntityPlayer playerIn) {
return te.canInteractWith(playerIn);
}
}
Теперь нам нужен графический интерфейс. Графические интерфейсы для контейнеров обычно берутся из
GuiContainer
. В этом простом случае нам не нужно много кода, чтобы этот графический интерфейс работал. Конечно, вы можете добавить свой собственный рендеринг, чтобы сделать этот графический интерфейс более привлекательным, а также добавить пользовательские компоненты и тому подобное:
TestContainerGui.java:
public class TestContainerGui extends GuiContainer {
public static final int WIDTH = 180;
public static final int HEIGHT = 152;
private static final ResourceLocation background = new ResourceLocation(ModTut.MODID, "textures/gui/testcontainer.png");
public TestContainerGui(TestContainerTileEntity tileEntity, TestContainer container) {
super(container);
xSize = WIDTH;
ySize = HEIGHT;
}
@Override
protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) {
mc.getTextureManager().bindTexture(background);
drawTexturedModalRect(guiLeft, guiTop, 0, 0, xSize, ySize);
}
}
Теперь осталась последняя вещь которую вам нужно сделать, это сказать игре, что вы создали свой графический интерфейс и реализовали для него контейнер. Для этого необходим
IGuiHandler
, который содержит серверный и клиентский код для обработки графического интерфейса. В приведенном ниже примере мы жестко закодировали имена TestContainerTileEntity
и TestContainer
. Я рекомендую не делать этого в своем собственном коде, а использовать простую структуру. Например, у вас может быть GenericTileEntity
, для которого вы можете сделать instanceof
, а затем использовать методы для создания графического интерфейса и/или контейнера. Или вы также можете использовать данный идентификатор, который передается через player.openGui ()
, как он был назван выше GUI_ID
. Этот пример просто демонстрирует, принцип работы:
GuiProxy.java:
public class GuiProxy implements IGuiHandler {
@Override
public Object getServerGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
BlockPos pos = new BlockPos(x, y, z);
TileEntity te = world.getTileEntity(pos);
if (te instanceof TestContainerTileEntity) {
return new TestContainer(player.inventory, (TestContainerTileEntity) te);
}
return null;
}
@Override
public Object getClientGuiElement(int ID, EntityPlayer player, World world, int x, int y, int z) {
BlockPos pos = new BlockPos(x, y, z);
TileEntity te = world.getTileEntity(pos);
if (te instanceof TestContainerTileEntity) {
TestContainerTileEntity containerTileEntity = (TestContainerTileEntity) te;
return new TestContainerGui(containerTileEntity, new TestContainer(player.inventory, containerTileEntity));
}
return null;
}
}
И, наконец, вам нужно зарегистрировать этот класс
GuiProxy
в вашем CommonProxy
:
CommonProxy.java:
public static class CommonProxy {
...
public void init(FMLInitializationEvent e) {
NetworkRegistry.INSTANCE.registerGuiHandler(instance, new GuiProxy());
}
...
Конец!