GUIの実装(1.7.10)

1.7.10の開発講座を修正中です。このページには誤りや古い情報が含まれる可能性があります。

MOD製作チュートリアル/TileEntityの追加で追加したTileEntityにGUI(Graphical User Interface)を実装する。

ソースコード


package tutorial.aluminiummod;

import cpw.mods.fml.common.Mod; import cpw.mods.fml.common.Mod.EventHandler; import cpw.mods.fml.common.Mod.Instance; import cpw.mods.fml.common.event.FMLPreInitializationEvent; import cpw.mods.fml.common.network.NetworkRegistry; import cpw.mods.fml.common.registry.GameRegistry; import net.minecraft.block.Block;

@Mod(modid = AluminiumMod.MODID, name = AluminiumMod.MODNAME, version = AluminiumMod.VERSION) public class AluminiumMod {

public static final String MODID = "AluminiumMod"; public static final String MODNAME = "Aluminium Mod"; public static final String VERSION = "1.0.0";

@Instance(MODID) public static AluminiumMod instance;

public static Block chestAluminium;

@EventHandler public void perInit(FMLPreInitializationEvent event) { chestAluminium = new BlockAluminiumChest() .setBlockName("chestAluminium") .setBlockTextureName("aluminiummod:Aluminium_Chest"); GameRegistry.registerBlock(chestAluminium, "chestAluminium"); GameRegistry.registerTileEntity(TileEntityAluminiumChest.class, "TileEntityAluminiumChest");

NetworkRegistry.INSTANCE.registerGuiHandler(this.instance, new AluminiumModGuiHandler()); }

}


package tutorial.aluminiummod;

import java.util.Random;

import net.minecraft.block.Block; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.material.Material; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World;

public class BlockAluminiumChest extends Block implements ITileEntityProvider {

private Random random = new Random();

public BlockAluminiumChest() { super(Material.rock); this.setCreativeTab(CreativeTabs.tabDecorations); this.setHardness(5.0F); this.setResistance(1.0F); this.setStepSound(soundTypeMetal); isBlockContainer = true; }

@Override public TileEntity createNewTileEntity(World world, int meta) { return new TileEntityAluminiumChest(); }

@Override public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { // GUIを開く。 player.openGui(AluminiumMod.instance, 1, world, x, y, z); return true; }

@Override public void breakBlock(World world, int x, int y, int z, Block block, int meta) { // TileEntityの内部にあるアイテムをドロップさせる。 TileEntityAluminiumChest tileentity = (TileEntityAluminiumChest) world.getTileEntity(x, y, z); if (tileentity != null) { for (int i = 0; i < tileentity.getSizeInventory(); i++) { ItemStack itemStack = tileentity.getStackInSlot(i);

if (itemStack != null) { float f = random.nextFloat() * 0.6F + 0.1F; float f1 = random.nextFloat() * 0.6F + 0.1F; float f2 = random.nextFloat() * 0.6F + 0.1F;

while (itemStack.stackSize > 0) { int j = random.nextInt(21) + 10;

if (j > itemStack.stackSize) { j = itemStack.stackSize; }

itemStack.stackSize -= j; EntityItem entityItem = new EntityItem(world, x + f, y + f1, z + f2, new ItemStack(itemStack.getItem(), j, itemStack.getItemDamage()));

if (itemStack.hasTagCompound()) { entityItem.getEntityItem() .setTagCompound(((NBTTagCompound) itemStack.getTagCompound().copy())); }

float f3 = 0.025F; entityItem.motionX = (float) random.nextGaussian() * f3; entityItem.motionY = (float) random.nextGaussian() * f3 + 0.1F; entityItem.motionZ = (float) random.nextGaussian() * f3; world.spawnEntityInWorld(entityItem); } } } world.func_147453_f(x, y, z, block); } super.breakBlock(world, x, y, z, block, meta); }

}


package tutorial.aluminiummod;

import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity;

public class TileEntityAluminiumChest extends TileEntity implements IInventory {

protected ItemStack[] itemStacks = new ItemStack[54];

@Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < itemStacks.length; i++) { if (itemStacks[i] == null) continue; NBTTagCompound nbt1 = new NBTTagCompound(); nbt1.setByte("Slot", (byte) i); itemStacks[i].writeToNBT(nbt1); nbttaglist.appendTag(nbt1); } nbt.setTag("Items", nbttaglist); }

@Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); NBTTagList nbttaglist = nbt.getTagList("Items", 10); itemStacks = new ItemStack[54]; for (int i = 0; i < nbttaglist.tagCount(); i++) { NBTTagCompound nbt1 = nbttaglist.getCompoundTagAt(i); byte b0 = nbt1.getByte("Slot"); if (0 <= b0 && b0 < itemStacks.length) { itemStacks[b0] = ItemStack.loadItemStackFromNBT(nbt1); } } }

@Override public int getSizeInventory() { return 54; }

@Override public ItemStack getStackInSlot(int slot) { return itemStacks[slot]; }

@Override public ItemStack decrStackSize(int slot, int amount) { if (itemStacks[slot] == null) return null; ItemStack itemstack; if (itemStacks[slot].stackSize <= amount) { itemstack = itemStacks[slot]; itemStacks[slot] = null; return itemstack; } itemstack = itemStacks[slot].splitStack(amount); if (itemStacks[slot].stackSize < 1) { itemStacks[slot] = null; } return itemstack; }

@Override public ItemStack getStackInSlotOnClosing(int slot) { return null; }

@Override public void setInventorySlotContents(int slot, ItemStack itemStack) { itemStacks[slot] = itemStack; if (itemStack != null && itemStack.stackSize > this.getInventoryStackLimit()) { itemStack.stackSize = this.getInventoryStackLimit(); } }

@Override public boolean hasCustomInventoryName() { return false; }

@Override public String getInventoryName() { return "container.AluminiumMod.AluminiumChest"; }

@Override public int getInventoryStackLimit() { return 64; }

@Override public boolean isUseableByPlayer(EntityPlayer player) { return worldObj.getTileEntity(xCoord, yCoord, zCoord) != this ? false : player.getDistanceSq(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D) <= 64.0D; }

@Override public void openInventory() {}

@Override public void closeInventory() {}

@Override public boolean isItemValidForSlot(int slot, ItemStack itemStack) { return true; }

}


package tutorial.aluminiummod;

import cpw.mods.fml.common.network.IGuiHandler; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World;

public class AluminiumModGuiHandler implements IGuiHandler {

@Override public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { if (!world.blockExists(x, y, z)) return null; TileEntity tileentity = world.getTileEntity(x, y, z); if (tileentity instanceof TileEntityAluminiumChest) { return new ContainerAluminiumChest(player, (TileEntityAluminiumChest) tileentity); } return null; }

@Override public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { if (!world.blockExists(x, y, z)) return null; TileEntity tileentity = world.getTileEntity(x, y, z); if (tileentity instanceof TileEntityAluminiumChest) { return new GuiAluminiumChest(player, (TileEntityAluminiumChest) tileentity); } return null; }

}


package tutorial.aluminiummod;

import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack;

public class ContainerAluminiumChest extends Container {

private TileEntityAluminiumChest tileEntity; /** アルミニウムチェストのインベントリの第一スロットの番号 */ private static final int index0 = 0; /** プレイヤーのインベントリの第一スロットの番号 */ private static final int index1 = 54; /** クイックスロットの第一スロットの番号 */ private static final int index2 = 81; /** このコンテナの全体のスロット数 */ private static final int index3 = 90;

public ContainerAluminiumChest(EntityPlayer player, TileEntityAluminiumChest tileEntity) { // スロットを設定する。 this.tileEntity = tileEntity; for (int iy = 0; iy < 6; iy++) { for (int ix = 0; ix < 9; ix++) { this.addSlotToContainer(new Slot(tileEntity, ix + (iy * 9), 8 + (ix * 18), 18 + (iy * 18))); } } for (int iy = 0; iy < 3; iy++) { for (int ix = 0; ix < 9; ix++) { this.addSlotToContainer(new Slot(player.inventory, ix + (iy * 9) + 9, 8 + (ix * 18), 140 + (iy * 18))); } } for (int ix = 0; ix < 9; ix++) { this.addSlotToContainer(new Slot(player.inventory, ix, 8 + (ix * 18), 198)); } }

@Override public boolean canInteractWith(EntityPlayer player) { return tileEntity.isUseableByPlayer(player); }

@Override public ItemStack transferStackInSlot(EntityPlayer player, int slotNumber) { ItemStack itemStack = null; Slot slot = (Slot) inventorySlots.get(slotNumber); if (slot != null && slot.getHasStack()) { ItemStack itemStack1 = slot.getStack(); itemStack = itemStack1.copy(); if (index0 <= slotNumber && slotNumber < index1) { // アルミニウムチェストのインベントリならプレイヤーのインベントリに移動。 if (!this.mergeItemStack(itemStack1, index1, index3, true)) { return null; } } else { // プレイヤーのインベントリならアルミニウムチェストのインベントリに移動。 if (!this.mergeItemStack(itemStack1, index0, index1, false)) { return null; } }

if (itemStack1.stackSize == 0) { slot.putStack((ItemStack) null); } else { slot.onSlotChanged(); } if (itemStack1.stackSize == itemStack.stackSize) { return null; } slot.onPickupFromSlot(player, itemStack1); } return itemStack; }

}


package tutorial.aluminiummod;

import org.lwjgl.opengl.GL11;

import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector;

public class GuiAluminiumChest extends GuiContainer {

private TileEntityAluminiumChest tileEntity; // GUIのテクスチャの場所を指定する。今回はバニラのラージチェストと同じものを使う。 private static final ResourceLocation GUITEXTURE = new ResourceLocation("textures/gui/container/generic_54.png");

public GuiAluminiumChest(EntityPlayer player, TileEntityAluminiumChest tileEnttiy) { super(new ContainerAluminiumChest(player, tileEnttiy)); this.tileEntity = tileEnttiy; ySize = 222; }

@Override protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY) { fontRendererObj.drawString(StatCollector.translateToLocal(tileEntity.getInventoryName()), 8, 6, 4210752); fontRendererObj.drawString(StatCollector.translateToLocal("container.inventory"), 8, ySize - 96 + 2, 4210752); }

@Override protected void drawGuiContainerBackgroundLayer(float tick, int mouseX, int mouseY) { GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); mc.getTextureManager().bindTexture(GUITEXTURE); int k = (width - xSize) / 2; int l = (height - ySize) / 2; this.drawTexturedModalRect(k, l, 0, 0, xSize, ySize); }

}

解説

Instance

String modid

コアクラスのインスタンスにつけるアノテーション。
登録やインスタンスの生成・代入はForge側がやってくれるので何もしなくてよい。

NetworkRegistry

GUIやパケットなどを管理するenum。

void registerGuiHandler(Object mod, IGuiHandler handler)

MODのコアクラスのインスタンスと、それに対応したGuiHandlerを登録する処理。

EntityPlayer

プレイヤーのEntity継承クラス。

void openGui(Object mod, int modGuiId, World world, int x, int y, int z)

第一引数はコアクラスのインスタンス、第二引数はMOD内でのGUIのID。
プレイヤーにGUIを開かせる。

IGuiHandler

GuiHandlerのためのインターフェース。

Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z)

サーバー側で呼ばれる。
基本的にはIDに対応したContainerを返す。
Worldのインスタンスと座標も引数に含まれているので、TileEntityを取得して判別してもよい。

Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z)

クライアント側で呼ばれる。
基本的にはIDに対応したGuiを返す。

Container

Slot addSlotToContainer(Slot slot)

新たなSlotをコンテナに登録する処理。

boolean canInteractWith(EntityPlayer player)

プレイヤーがコンテナを使えるかどうかの判定。

ItemStack transferStackInSlot(EntityPlayer player, int slotNumber)

スロットがShift+クリックされた時の処理。
クリックされたスロットの中身を移動する。

boolean mergeItemStack(ItemStack itemStack, int beginIndex, int endIndex, boolean doReverse)

引数のItemStackを指定された範囲内の空のスロットに移動する。
範囲は、beginIndexを含み、endIndexは含まない。
doReverseがtrueの場合は、検索方向がend→beginになる。

GuiContainer

コンテナを持つGUIのクラス。
クライアントのみで使用される。

public GuiContainer(Container container)

引数は対応するコンテナ。

void drawGuiContainerForegroundLayer(int mouseX, int mouseY)

Guiの前面レイヤーを描画する処理。
文字の描画などをする。

void drawGuiContainerBackgroundLayer(float tick, int mouseX, int mouseY)

Guiの背景レイヤーを描画する処理。
Guiのテクスチャの描画などをする。

ResourceLocation

リソースの場所を保持するクラス。

public ResourceLocation(String path)

コンストラクタ。
引数でリソースの場所を指定する。
"modid:textures/..."というように、すべて小文字にしたMODIDと、textures以下のファイルのパスを指定する。
例えば、assets/aluminiummod/textures/gui/gui.pngだとすると、private static final ResourceLocation GUITEXTURE = new ResourceLocation("aluminiummod:textures/gui/gui.png");と記述する。
なお、GUIのテクスチャは画像ファイルを正方形にしないと縦横比がおかしくなる可能性があるので、カンバスサイズを256x256や512x512などにしておいて、余白を透明で残しておくと良い。

使用例

オファレンプレゼントボックスを追加している部分。


package nahama.ofalenmod;

/*略*/

/** * @author Akasata Nahama */ @Mod(modid = OfalenModCore.MODID, name = OfalenModCore.MODNAME, version = OfalenModCore.VERSION) public class OfalenModCore {

public static final String MODID = "OfalenMod"; public static final String MODNAME = "Ofalen Mod"; public static final String MCVERSION = "1.7.10"; public static final String OMVERSION = "1.1.0"; public static final String VERSION = "[" + MCVERSION + "]" + OMVERSION;

/** coreクラスのインスタンス */ @Instance(MODID) public static OfalenModCore instance;

/*略*/

/** 初期化前処理。 */ @EventHandler public void preInit(FMLPreInitializationEvent event) { /*略*/ OfalenModBlockCore.registerBlock(); // 機械類のGUIを登録する。 NetworkRegistry.INSTANCE.registerGuiHandler(this.instance, new OfalenModGuiHandler()); /*略*/ }

/*略*/

}


package nahama.ofalenmod.core;

/*略*/

public class OfalenModBlockCore {

/*略*/ public static Block boxPresentOfalen;

/** ブロックを登録する処理。 */ public static void registerBlock() { /*略*/ boxPresentOfalen = new BlockPresentBox() .setBlockName("boxPresentOfalen") .setBlockTextureName("ofalenmod:present_box"); GameRegistry.registerBlock(boxPresentOfalen, "boxPresentOfalen"); GameRegistry.registerTileEntity(TileEntityPresentBox.class, "TileEntityOfalenPresentBox"); }

}


package nahama.ofalenmod.block;

import java.util.Random;

import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import nahama.ofalenmod.OfalenModCore; import nahama.ofalenmod.handler.OfalenModAnniversaryHandler; import nahama.ofalenmod.tileentity.TileEntityPresentBox; import net.minecraft.block.Block; import net.minecraft.block.ITileEntityProvider; import net.minecraft.block.material.Material; import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.IIcon; import net.minecraft.world.World;

public class BlockPresentBox extends Block implements ITileEntityProvider {

private Random random = new Random(); /** 0:下,1:上,2:横,3:クリスマス下,4;クリスマス上,5:クリスマス横 */ private IIcon[] iicon = new IIcon[6];

public BlockPresentBox() { super(Material.sponge); this.setCreativeTab(OfalenModCore.tabOfalen); this.setHardness(1.0F); this.setResistance(1.0F); this.setStepSound(Block.soundTypeCloth); }

@Override public TileEntity createNewTileEntity(World world, int meta) { return new TileEntityPresentBox(); }

/** プレイヤーに右クリックされたときの処理。 */ @Override public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ) { // GUIを開く player.openGui(OfalenModCore.instance, 1, world, x, y, z); return true; }

/** ブロックが破壊された時の処理。 */ @Override public void breakBlock(World world, int x, int y, int z, Block block, int meta) { // TileEntityの内部にあるアイテムをドロップさせる。 TileEntityPresentBox tileentity = (TileEntityPresentBox) world.getTileEntity(x, y, z); if (tileentity != null) { for (int i = 0; i < tileentity.getSizeInventory(); i++) { ItemStack itemStack = tileentity.getStackInSlot(i);

if (itemStack != null) { float f = random.nextFloat() * 0.6F + 0.1F; float f1 = random.nextFloat() * 0.6F + 0.1F; float f2 = random.nextFloat() * 0.6F + 0.1F;

while (itemStack.stackSize > 0) { int j = random.nextInt(21) + 10;

if (j > itemStack.stackSize) { j = itemStack.stackSize; }

itemStack.stackSize -= j; EntityItem entityItem = new EntityItem(world, x + f, y + f1, z + f2, new ItemStack(itemStack.getItem(), j, itemStack.getItemDamage()));

if (itemStack.hasTagCompound()) { entityItem.getEntityItem().setTagCompound(((NBTTagCompound) itemStack.getTagCompound().copy())); }

float f3 = 0.025F; entityItem.motionX = (float) random.nextGaussian() * f3; entityItem.motionY = (float) random.nextGaussian() * f3 + 0.1F; entityItem.motionZ = (float) random.nextGaussian() * f3; world.spawnEntityInWorld(entityItem); } } } world.func_147453_f(x, y, z, block); } super.breakBlock(world, x, y, z, block, meta); }

/** ブロックのアイコンを登録する処理。 */ @Override @SideOnly(Side.CLIENT) public void registerBlockIcons(IIconRegister register) { for (int i = 0; i < 6; i++) { iicon[i] = register.registerIcon(this.getTextureName() + "-" + i); } }

/** ブロックのアイコンを返す。 */ @Override @SideOnly(Side.CLIENT) public IIcon getIcon(int side, int meta) { int i = 2; if (side == 0) i = 0; if (side == 1) i = 1; if (OfalenModAnniversaryHandler.isChristmas) i += 3; return iicon[i]; }

}


package nahama.ofalenmod.tileentity;

import nahama.ofalenmod.handler.OfalenModAnniversaryHandler; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.IInventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.tileentity.TileEntity;

public class TileEntityPresentBox extends TileEntity implements IInventory {

protected ItemStack[] itemStacks = new ItemStack[54]; protected String owner;

/** プレイヤーがインベントリを開けた時の処理。 */ public void openInventory(EntityPlayer player) { if (owner != null || worldObj.isRemote) return; // オーナーに設定し、中身を上書きする。 owner = player.getCommandSenderName(); ItemStack[] presents = OfalenModAnniversaryHandler.getPresents(player); if (presents == null) return; itemStacks = presents; }

@Override public void writeToNBT(NBTTagCompound nbt) { super.writeToNBT(nbt); NBTTagList nbttaglist = new NBTTagList(); for (int i = 0; i < itemStacks.length; i++) { if (itemStacks[i] == null) continue; NBTTagCompound nbt1 = new NBTTagCompound(); nbt1.setByte("Slot", (byte) i); itemStacks[i].writeToNBT(nbt1); nbttaglist.appendTag(nbt1); } nbt.setTag("Items", nbttaglist); nbt.setString("Owner", owner); }

@Override public void readFromNBT(NBTTagCompound nbt) { super.readFromNBT(nbt); NBTTagList nbttaglist = nbt.getTagList("Items", 10); itemStacks = new ItemStack[54]; for (int i = 0; i < nbttaglist.tagCount(); i++) { NBTTagCompound nbt1 = nbttaglist.getCompoundTagAt(i); byte b0 = nbt1.getByte("Slot"); if (0 <= b0 && b0 < itemStacks.length) { itemStacks[b0] = ItemStack.loadItemStackFromNBT(nbt1); } } owner = nbt.getString("Owner"); }

/** インベントリのスロット数を返す。 */ @Override public int getSizeInventory() { return 54; }

/** スロットのアイテムを返す。 */ @Override public ItemStack getStackInSlot(int slot) { return itemStacks[slot]; }

/** スロットのスタック数を減らす。 */ @Override public ItemStack decrStackSize(int slot, int amount) { if (itemStacks[slot] == null) return null; ItemStack itemstack; if (itemStacks[slot].stackSize <= amount) { itemstack = itemStacks[slot]; itemStacks[slot] = null; return itemstack; } itemstack = itemStacks[slot].splitStack(amount); if (itemStacks[slot].stackSize < 1) { itemStacks[slot] = null; } return itemstack; }

@Override public ItemStack getStackInSlotOnClosing(int slot) { return null; }

/** スロットの中身を設定する。 */ @Override public void setInventorySlotContents(int slot, ItemStack itemStack) { itemStacks[slot] = itemStack; if (itemStack != null && itemStack.stackSize > this.getInventoryStackLimit()) { itemStack.stackSize = this.getInventoryStackLimit(); } }

/** 金床で設定された名前を持つかどうか。 */ @Override public boolean hasCustomInventoryName() { return false; }

/** インベントリの名前を返す。 */ @Override public String getInventoryName() { return "container.OfalenMod.PresentBox"; }

/** このインベントリの最大スタック数を返す。 */ @Override public int getInventoryStackLimit() { return 64; }

/** プレイヤーが使用できるかどうか。 */ @Override public boolean isUseableByPlayer(EntityPlayer player) { if (owner != null && !player.getCommandSenderName().equals(owner)) return false; return worldObj.getTileEntity(xCoord, yCoord, zCoord) != this ? false : player.getDistanceSq(xCoord + 0.5D, yCoord + 0.5D, zCoord + 0.5D) <= 64.0D; }

@Override public void openInventory() {}

@Override public void closeInventory() {}

/** スロットにアクセスできるかどうか。 */ @Override public boolean isItemValidForSlot(int slot, ItemStack itemStack) { return false; }

}


package nahama.ofalenmod.handler;

/*略*/

public class OfalenModGuiHandler implements IGuiHandler {

@Override public Object getServerGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { if (id == 2) return new ContainerItemShield(player); if (id == 3) return new ContainerItemTeleporter(player); if (id == 4) return new ContainerItemFloater(player); if (!world.blockExists(x, y, z)) return null; TileEntity tileentity = world.getTileEntity(x, y, z); if (tileentity instanceof TileEntitySmeltingMachine) { return new ContainerSmeltingMachine(player, (TileEntitySmeltingMachine) tileentity); } if (tileentity instanceof TileEntityRepairMachine) { return new ContainerRepairMachine(player, (TileEntityRepairMachine) tileentity); } if (tileentity instanceof TileEntityConversionMachine) { return new ContainerConversionMachine(player, (TileEntityConversionMachine) tileentity); } if (tileentity instanceof TileEntityTeleportMarker) { return new ContainerTeleportMarker(player, (TileEntityTeleportMarker) tileentity); } if (tileentity instanceof TileEntityPresentBox) { return new ContainerPresentBox(player, (TileEntityPresentBox) tileentity); } return null; }

@Override public Object getClientGuiElement(int id, EntityPlayer player, World world, int x, int y, int z) { if (id == 2) return new GuiItemShield(player); if (id == 3) return new GuiItemTeleporter(player); if (id == 4) return new GuiItemFloater(player); if (!world.blockExists(x, y, z)) return null; TileEntity tileentity = world.getTileEntity(x, y, z); if (tileentity instanceof TileEntitySmeltingMachine) { return new GuiSmeltingMachine(player, (TileEntitySmeltingMachine) tileentity); } if (tileentity instanceof TileEntityRepairMachine) { return new GuiRepairMachine(player, (TileEntityRepairMachine) tileentity); } if (tileentity instanceof TileEntityConversionMachine) { return new GuiConversionMachine(player, (TileEntityConversionMachine) tileentity); } if (tileentity instanceof TileEntityTeleportMarker) { return new GuiTeleportMarker(player, (TileEntityTeleportMarker) tileentity); } if (tileentity instanceof TileEntityPresentBox) { return new GuiPresentBox(player, (TileEntityPresentBox) tileentity); } return null; }

}


package nahama.ofalenmod.inventory;

import nahama.ofalenmod.tileentity.TileEntityPresentBox; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.inventory.Container; import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack;

public class ContainerPresentBox extends Container {

private TileEntityPresentBox tileEntity; /** プレゼントボックスのインベントリの第一スロットの番号 */ private static final int index0 = 0; /** プレイヤーのインベントリの第一スロットの番号 */ private static final int index1 = 54; /** クイックスロットの第一スロットの番号 */ private static final int index2 = 81; /** このコンテナの全体のスロット数 */ private static final int index3 = 90;

public ContainerPresentBox(EntityPlayer player, TileEntityPresentBox tileEntity) { this.tileEntity = tileEntity; tileEntity.openInventory(player); for (int iy = 0; iy < 6; iy++) { for (int ix = 0; ix < 9; ix++) { this.addSlotToContainer(new Slot(tileEntity, ix + (iy * 9), 8 + (ix * 18), 18 + (iy * 18))); } } for (int iy = 0; iy < 3; iy++) { for (int ix = 0; ix < 9; ix++) { this.addSlotToContainer(new Slot(player.inventory, ix + (iy * 9) + 9, 8 + (ix * 18), 140 + (iy * 18))); } } for (int ix = 0; ix < 9; ix++) { this.addSlotToContainer(new Slot(player.inventory, ix, 8 + (ix * 18), 198)); } }

@Override public boolean canInteractWith(EntityPlayer player) { return tileEntity.isUseableByPlayer(player); }

@Override public ItemStack transferStackInSlot(EntityPlayer player, int slotNumber) { ItemStack itemStack = null; Slot slot = (Slot) inventorySlots.get(slotNumber); if (slot != null && slot.getHasStack()) { ItemStack itemStack1 = slot.getStack(); itemStack = itemStack1.copy(); if (index0 <= slotNumber && slotNumber < index1) { // プレゼントボックスのインベントリならプレイヤーのインベントリに移動。 if (!this.mergeItemStack(itemStack1, index1, index3, true)) { return null; } } else { if (index1 <= slotNumber && slotNumber < index2) { // プレイヤーのインベントリならクイックスロットに移動。 if (!this.mergeItemStack(itemStack1, index2, index3, false)) { return null; } } else if (index2 <= slotNumber && slotNumber < index3 && !this.mergeItemStack(itemStack1, index1, index2, false)) { // クイックスロットからプレイヤーのインベントリに移動できなかったら終了。 return null; } }

if (itemStack1.stackSize == 0) { slot.putStack((ItemStack) null); } else { slot.onSlotChanged(); } if (itemStack1.stackSize == itemStack.stackSize) { return null; } slot.onPickupFromSlot(player, itemStack1); } return itemStack; }

}


package nahama.ofalenmod.gui;

import org.lwjgl.opengl.GL11;

import nahama.ofalenmod.inventory.ContainerPresentBox; import nahama.ofalenmod.tileentity.TileEntityPresentBox; import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.ResourceLocation; import net.minecraft.util.StatCollector;

public class GuiPresentBox extends GuiContainer {

private TileEntityPresentBox tileEntity; private static final ResourceLocation GUITEXTURE = new ResourceLocation("textures/gui/container/generic_54.png");

public GuiPresentBox(EntityPlayer player, TileEntityPresentBox tileEnttiy) { super(new ContainerPresentBox(player, tileEnttiy)); this.tileEntity = tileEnttiy; ySize = 222; }

@Override protected void drawGuiContainerForegroundLayer(int par1, int par2) { fontRendererObj.drawString(StatCollector.translateToLocal(tileEntity.getInventoryName()), 8, 6, 4210752); fontRendererObj.drawString(StatCollector.translateToLocal("container.inventory"), 8, ySize - 96 + 2, 4210752); }

@Override protected void drawGuiContainerBackgroundLayer(float par1, int par2, int par3) { GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); mc.getTextureManager().bindTexture(GUITEXTURE); int k = (width - xSize) / 2; int l = (height - ySize) / 2; this.drawTexturedModalRect(k, l, 0, 0, xSize, ySize); }

}

GUIの実装(1.7.10)” への12件のフィードバック

    1. EntityPlayer.openGui()の第二引数であるIDはIGuiHandler.getServerGuiElement()及びIGuiHandler.getClientGuiElement()の第一引数として渡されます。
      この値はGUIの種類の識別に使うことが想定されていますが、Forgeは関与しないため、MOD側で自由に設定できます。
      このチュートリアルでは、AluminiumModGuiHandlerで座標からTileEntityを取得してTileEntityAluminiumChestであるか判定しているため、IDを利用しておらず、EntityPlayer.openGui()には意味なく1を渡しています。

      MOD内で、バニラにおけるチェストやかまどのような、TileEntityに対してのGUIだけを実装する場合、IDを取り扱う必要性はないでしょう。
      ただ、使用例やオファレンMODのソースコードを見ていただくとわかりやすいと思いますが、オファレンテレポーターのような、アイテムに対するGUIも実装する場合、開くGUIがTileEntityのものかアイテムのものか区別をつけるためにIDを使用する必要があります。

  1. タイルエンティティにIInventoryインターフェースを実装しない、内部にインベントリを持たないタイルエンティティのGUIを作る場合はどうすればいいのでしょうか。
    また、GUI上でのマウスやキーのリスナーなどはないのでしょうか

    1. IInventoryを実装しないTileEntityのGUIについても、基本的な実装方法はこのページの解説と変わりません。
      GUIにプレイヤーのインベントリを表示するかどうかで分け、それぞれの変更点を説明します。

      表示する場合、ContainerAluminiumChestのコンストラクタでTileEntityのインベントリに対応するスロットを追加している部分を削除します。
      スロット番号がずれるため、transferStackInSlot()も変更してください。
      この場合は、オファレンMODのテレポートマーカーの実装が参考になると思います。

      表示しないものは作ったことがないので正確には分かりませんが、Containerを継承したクラスは必要ないかもしれません。
      その場合は、getServerGuiElement()でnullを、getClientGuiElement()でGuiScreenを継承したクラスのインスタンスをそれぞれ返せばよいと思います。

      マウスのリスナーはGuiScreenでmouseClicked()、mouseMovedOrUp()、mouseClickMove()が用意されています。
      また、GuiContainerCreativeでは、これらの呼び出し元であるhandleMouseInput()をオーバーライドしてホイールの動作を制御しています。
      GuiContainerは操作情報をサーバー側のContainerに送信し、スロットの処理を行わせています。

      キーボードのリスナーも同様に、GuiScreenのkeyTyped()を利用できます。
      特殊な動作をする場合は呼び出し元のhandleKeyboardInput()で制御してもよいでしょう。
      GuiContainerは数字キーの入力を受け取り、ホットバー移動を実装しています。
      テキストボックスの処理はGuiContainerCreativeの検索窓が参考になると思います。

      マウスやキーボードのリスナーからTileEntityの状態を変更した場合、その変更内容をクライアントからサーバーに送信しなければなりません。
      解説を作成していないため分かりにくいかもしれませんが、テレポートマーカーのボタンによるチャンネル変更の同期や、OfalenModPacketCoreを参考にしてください。

  2. ありがとうございます。
    失礼ですがOfalenModPacketCoreクラスは
    githubのAkasataNahama/OfalenMod
    のどこのパッケージに属しているのでしょうか..?

  3. Itemを右クリックした際にスロットを持つインベントリを表示させるにはどうすればよいでしょうか、(BackPackのような物)

    1. バックパックのItemStackのNBTを読み書きするIInventoryを作り、TileEntityの代わりにします。
      また、バックパックのインベントリにそのバックパックを入れると消えるので、GUIを開いているバックパックは移動できないようにする必要があります。
      継承構造とフィルター機能で少し複雑になっていますが、オファレンMODのアイテムフィルターの実装が参考になると思います。
      OfalenModGuiHandler, ItemFilter, InventoryItemFilter, InventoryItemBase, ContainerItemFilter, ContainerItemBase, GuiContainerBasicなどのクラスが関係しています。

  4. オファレンMODなどを参考にコードを書いてみましたが、追加したアイテムをクリックすると強制的にマインクラフトが落ちてしまいます。このような場合に考えられるエラーを引き起こしている文は何でしょうか?(すみません大雑把で)

    1. クラッシュが発生したのであれば、クラッシュレポートに例外(Exception)の種類とその発生場所までの呼び出し階層(Stacktrace)が記載されています。
      バックパックの右クリックでクラッシュしているのであれば、GUIとContainerの登録部分などが怪しそうです。
      もし解決しなかったら、詳しい状況説明とともに、クラッシュレポートのExceptionとStacktraceの最初の数行を記載してください。

    1. GuiAluminiumChestは、drawGuiContainerForegroundLayerでGUIの文字を描画しています。StatCollector#translateToLocalは引数の文字列をlangファイルにより翻訳するので、この記事の例ではcontainer.AluminiumMod.AluminiumChestに対応した翻訳が表示されます。GUIに限らず、翻訳が登録されていない文字列はそのまま表示されるので、その文字列をlangファイルの記述に利用してください。詳しくは「リソースの追加」をご覧ください。

コメントはこちらです。(スパム対策の為コメントは手動承認になっています。未承認のコメントは表示されないので連投はお控え下さい。)

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください