Aller au contenu
EdwinMindcraft

[WIP] Inventaires et GUIs

Recommended Posts

Introduction

Dans Minecraft, les mods qui ajoutes des block ou des items sont bien mais... Ils sont surtout très limités.

Dans ce tutoriel, je vais tenter de vous expliquer les Inventaire (IInventory) et les Containers et GUIs

Prerequis :

1- Avoir une installation de forge 1.7.10

2-Avoir suivi ce tutoriel

3- Savoir faire un block

4- Savoir faire un TileEntity

Sommaire

1] Block et Tile Entity

2] Sauvegarde

3] Ticking du block

4] Enregistrement

5] Container

6] GUI

7]Finalisation

1] Block et TileEntity

Voila, on est au bases de l’inventaire.

Pour commencer faites un block simple. Pas trop de travail hein…

Votre block doit avoir extends BlockContainer

Changez

 return null; 

dans createNewTileEntity par

return new TileInventoryTuto(); 

et creer TileInventoryTuto en precisant :

public class TileInventoryTuto extends TileEntity implements IInventory

Lorsque vous corrigerais l'erreur, vous verrez beaucoup de méthodes apparaître. Ne vous en faite pas elle ne sont pas compliquées a complétées

A] getSizeInventory()

Bon… là pas grand-chose à expliqué : mettez le nombre de stack que votre block peut stocker.

B] getStackInSlot(int slot)

Ah, c’est un tout petit peu plus compliqué là…

Pour cette méthode : introduction d’une nouvelle variable : stack, c'est cette variable qui vas contenir l'inventaire de votre block, c'est une liste d'ItemStack

	public ItemStack[] stack = new ItemStack[X];

Avec X = getSizeInventory()

Après, c’est juste

        return stack[slot];

Ce code va juste prendre le stack a la position slot.

C]  devrStackSize(int slot, int num)

 

    public ItemStack decrStackSize(int slot, int num) {
        if (this.stack[slot] != null)
        {
            ItemStack itemstack;
            if (this.stack[slot].stackSize <= num)
            {
                itemstack = this.stack[slot];
                this.stack[slot] = null;
            }
            else
            {
                itemstack = this.stack[slot].splitStack(num);

                if (this.stack[slot].stackSize == 0)
                {
                    this.stack[slot] = null;
                }
               
            }
            return itemstack;
        }
        else
        {
            return null;
        }
    }

Voilà le code, c’est le même dans 90 % des inventaires et c'est celui que l'on va utiliser. Il vérifie d'abord que l'item dans le slot existe, ensuite il regarde si le nombre à enlever est supérieur ou égal à la taille du stack. Si oui alors il vas enlever l'item du slot après en avoir fait un copies et va le return. Sinon, il vas couper le stack en 2 et il vas garder une des deux parties qu'il vas return.

D] getItemStackOnSlotClosing(int slot)

 

    @Override
    public ItemStack getStackInSlotOnClosing(int slot) {
        if (this.stack[slot] != null)
        {
            ItemStack itemstack = this.stack[slot];
            this.stack[slot] = null;
            return itemstack;
        }
        else
        {
            return null;
        }
    }

Meme chose qu'au dessus, le meme code est utilisé dans tout les inventaires, il verifie d'abord que le stack dans le slot "slot" n'est pas null et dans ce cas, il return ce stack après l'avoir supprimer de l'inventaire.

E] setInventorySlotContents(int slot, ItemStack input)

 

    public void setInventorySlotContents(int slot, ItemStack input) {
	        this.stack[slot] = input;
	        if (input != null && input.stackSize > this.getInventoryStackLimit())
	        {
	            input.stackSize = this.getInventoryStackLimit();
	        }
	        this.markDirty();        
	    }
Comme les deux precedents, celui si met un stack dans un slot et verifie que le stack en entre n'est pas trop grand.

F] getInventoryName() et hasCustomInventoryName()

    @Override
    public String getInventoryName() {
        return "tileInventoryTuto";
    }

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

Pour cette partie, changer "tileInventoryTuto" par le nom de votre block, c'est le nom de votre inventaire

G]Les autres

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

	@Override
	public boolean isUseableByPlayer(EntityPlayer player) {
		return true;
	}

	@Override
	public void openInventory() {
	}

	@Override
	public void closeInventory() {
	}

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

Pour l'instant laisser ce code tout seul, vous en avez pas vraiment besoin

2] La sauvegarde

La sauvegarde des TileEntity s'appelle writeToNBT, vous allez devoir y mettre ce code

    @Override
    public void writeToNBT(NBTTagCompound nbt)
    {
        super.writeToNBT(nbt);
        NBTTagList nbttaglist = new NBTTagList();

        for (int i = 0; i < this.stack.length; ++i)
        {
            if (this.stack[i] != null)
            {
                NBTTagCompound tmp = new NBTTagCompound();
                tmp.setByte("Slot", (byte)i);
                this.stack[i].writeToNBT(tmp);
                nbttaglist.appendTag(tmp);
            }
        }
         nbt.setTag("Inventory", nbttaglist);
        this.markDirty();
    }

Pour faire simple, ce code passe a travers la totalités des items stockés dans stack, si cet item n'est pas vide, les donnés de cet item sont stocké dans tmp puis mis dans la list. Finalement le la list est placée dans le nbt.

Bon maintenant que on peut sauvegarder, il faudrait pouvoir relire les données. Pour cette fonctionnalité , minecraft nous offre readFromNBT:

    public void readFromNBT(NBTTagCompound nbt)
    {
        super.readFromNBT(nbt);
        NBTTagList nbttaglist = nbt.getTagList("Inventory", 10);
        this.stack = new ItemStack[this.getSizeInventory()];

        for (int i = 0; i < nbttaglist.tagCount(); ++i)
        {
            NBTTagCompound nbttagcompound1 = nbttaglist.getCompoundTagAt(i);
            int j = nbttagcompound1.getByte("Slot") & 255;

            if (j >= 0 && j < this.stack.length)
            {
                this.stack[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
            }
        }
    }

Ici, on s'occupe de passer a travers la liste et de remettre les items dans stack. La chose qui peut être difficile a comprendre est le "& 255" : il permet de passer d'un unsigned byte a un byte et donc à ne pas avoir un nombre aberrant dans "Slot".

3] Ticking du block 

Un inventaire c'est bien mais si il ne tick pas, il ne marche pas.

pour cela nous avons 2 methodes 

	@Override
	public Packet getDescriptionPacket() {
		NBTTagCompound nbt = new NBTTagCompound();
		writeToNBT(nbt);
		return new S35PacketUpdateTileEntity(xCoord, yCoord, zCoord, 0, nbt);
	}
	
	@Override
	public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
		readFromNBT(pkt.func_148857_g());
		worldObj.markBlockForUpdate(xCoord, yCoord, zCoord);
		super.onDataPacket(net, pkt);
	}

getDescriptionPacket est le code qui s’exécute avant l'update d'un block, on lui dit ici d’écrire l'inventaire dans le NBT et d'envoyer ce NBT, qui est ensuite reçu par onDataPacket qui s'occupe de le lire est de dire que le block doit s'update.

4]Enregistrement

Bien, nous avons un block et un inventaire mais vous ne les trouverais jamais en jeu...

Vous allez donc devoir les enregistrer dans forge.

    public static final Block inventoryTuto = new BlockInventoryTuto().setBlockName("tuto").setCreativeTab(CreativeTabs.tabDecorations);

Ce code permet de créer le block et de le mettre dans la tab Decorations du menu creatif.

Dans preInit

    GameRegistry.registerBlock(inventoryTuto, "tuto");
    GameRegistry.registerTileEntity(TileInventoryTuto.class, "TileEntityInventoryTuto");

Ce code est la base de la base, registerBlock dit a forge "ce block existe et il a l'ID tuto" et registerTileEntity fait la meme chose avec les tileEntities

Nous avons donc un inventaire mais... il n'est accessible que par des hopper... donc il ne marcheras pas pour les personnes qui adoooooooore le vanilla...

5]Le Container

J'en vois déjà certain "On s'en fiche du container puis ça sert a quoi d'abord ton truc ?"... Pour faire simple, le container est le lien entre le GUI est la Tile Entity, c'est lui qui défini les positions des Slots dans votre Objet.

Vous comprendrez donc que c'est le plus important dans l'histoire...

Bien pour commencer, un Container extends la class Container. Jusque là tout le monde suit...

Lors de l'ajout des méthodes, vous verrez apparaître canInteractWith qui ne sert que lorsque vous avez un inventaire comme un coffre (ne s'ouvre pas s'il y a un bloc solide au dessus).

Par défaut laisser la donc a true

Pour le constructeur, je vous conseille ce format : 

    public ContainerTuto(InventoryPlayer player, TileInventoryTuto tile) {

    }

Il permet d'avoir accès à la TileEntity et donc de faciliter certaines interactions.

A]Les slots

Les slots sont les endroits où vous pouvez ranger vous items, pour en ajouter, allez dans le constructeur

Pour commencer, il y a 2 type de slot :

  • Les slots du block
  • Les slots du joueur

On vas commencer par le joueur :

        int decalage = 84;
        for (int j = 0; j < 3; ++j)
        {
            for (int k = 0; k < 9; ++k)
            {
                this.addSlotToContainer(new Slot(player, k + j * 9 + 9, 8 + k * 18, j * 18 + decalage));
            }
        }

        for (int j = 0; j < 9; ++j)
        {
            this.addSlotToContainer(new Slot(player, j, 8 + j * 18, 58 + decalage));
        }

Le décalage servira plus tard pour cadrer l'inventaire dans le GUI

comme vous pouvez le voir, on ajoute d'abord les slot inventaire puis hotbar.

Pour les slot du block voici le code basique, vous n'aurez qu'a le répéter autant de fois que vous avez de slot dans votre inventaire

this.addSlotToContainer(new Slot(tile, id, x, y));

avec id qui vas de 0 a votre nombre de slot -1

x la position en X

y la position en Y

NB : pour placer vous slot vous pourrez utiliser le Debug Mode d'clipse qui vous permet de faire des modification en temps réel. 

B] Shift Click

Eh oui vous avez bien compris, car pour l'instant Shift-Click = Crash pour remedier a ce probleme il faut override la methode transferStackInSlot

	@Override
	public ItemStack transferStackInSlot(EntityPlayer player, int slot) {

		ItemStack itemstack = null;
		Slot slotItem = (Slot) this.inventorySlots.get(slot);

		if (slotItem != null && slotItem.getHasStack()) {
			ItemStack itemstack1 = slotItem.getStack();
			itemstack = itemstack1.copy();

			if (slot < <NBSlotInventaire>) {
				if (!this.mergeItemStack(itemstack1, <NBSlotInventaire+1>,
						this.inventorySlots.size(), true)) {
					return null;
				}
			} else if (!this.mergeItemStack(itemstack1, 0, <NBSlotInventaire>, false)) {
				return null;
			}

			if (itemstack1.stackSize == 0) {
				slotItem.putStack((ItemStack) null);
			} else {
				slotItem.onSlotChanged();
			}
			slotItem.onPickupFromSlot(player, itemstack1);
		}

		return itemstack;
	}

Voila le code, c'est barbare, il regarde d'abord si le slot est un slot de l'inventaire du block, si oui il tente d'abord de placer les items dans la hotbar, puis si ça échoue, il le tente de le placer dans l'inventaire en lui meme. Dans le cas ou le joueur a shift-click un object dans l'inventaire, il tente de le mettre dans l'inventaire du block.

C'est quasiment du copier coller a chaque fois donc... n’oublier juste pas de remplacer les <NBSlotInventaire> par le nombre de slot total de votre inventaire. Bon.. On en a fini pour le container maintenant le GUI.

6]GUI

J'ai garder le plus simple pour la fin : le GUI votre classe devra juste extends GUIContainer

pour le constructeur faite ceci : 

    public GUITuto(InventoryPlayer player, TileInventoryTuto tile) {
        super(new ContainerTuto(player, tile));
    }

Cette méthode vous permettra d’accéder au tileEntity sans trop de problème et d'obtenir les informations des progress bars par exemple (Feu dans le four, les bulles du stand d'alchimie...)

puis dans drawBackgroundImage 

        Minecraft.getMinecraft().renderEngine.bindTexture(new ResourceLocation(Tutorial.MODID, "<CheminDeLaTexture(textures/X.png)>"));
        int k = (this.width - this.xSize) / 2;
        int l = (this.height - ySize) / 2;
        drawTexturedModalRect(k, l, 0, 0, xSize, ySize);

pour faire simple : On dit a minecraft met la texture dans la mémoire, calcule le coin X et le coin Y de la texture, affiche la texture en k, l en commençant par le pixel 0x et 0y de la taille xSize et ySize.

Et voila vous avez un Gui presque fonctionnel.

7] Finalisation

Pour cette dernière étape, il va falloir ajouter quelque choses dans le block , dans le mod et dans les proxies.

A]Mod

		NetworkRegistry.INSTANCE.registerGuiHandler(this, proxy);

Ce code vas dire à minecraft que votre proxy vas gérer les GUI de votre mod.

B]Common Proxy

implements IGuiHandler

ajouter ceci, cela vas vous donner accès a 2 nouvelles methodes, getServerGuiElement et getClientGuiElement. Le serveur est toujours le container et le client le GUIContainer

dans la methode getServerGuiElement ajoutez

		if (ID == <IDDuGUI>) return new ContainerTuto(player.inventory, (TileInventoryTuto)world.getTileEntity(x, y, z));

où <IDDuGUI> est une valeur différent a chaque GUI. Ce code vas associer a l'id <IDDuGUI> le container correspondant.

C]Client Proxy

ajouter un override de la méthode getClientGuiElement et ajoutez

		if (ID == <IDDuGUI>) return new GUITuto(player.inventory, (TileInventoryTuto)world.getTileEntity(x, y, z));

Ce code vas associer a l'id <IDDuGUI> le gui correspondant, dans les deux cas, <IDDuGUI> est la même

D]Le Block

Dans le block, dans la méthode onBlockActivated, ajoutez

		TileEntity tileEntity = world.getTileEntity(x, y, z);
		if (tileEntity == null || player.isSneaking()) {
			return false;
		}
		player.openGui(Tutorial.instance, <IDDuGUI>, world, x, y, z);
		return true;

Cette partie de code va vérifier si le joueur n'est pas accroupi, si ce n'est pas le cas, il va ouvrir le GUI avec l'id <IDDuGUI> pour le joueur.

et voila, vous avez un inventaire qui stocke des items.

J’espère que ce tutoriel vous aura aidé

Partager ce message


Lien à poster
Partager sur d’autres sites

J'ai 3 petites remarques :

- Pense à lire et à faire référence à ce sujet afin de ne pas se répéter au fil des tutos (en gros ta partie 1).

- Utilise des balises code pour y mettre le code (visiblement elle ne sont plus dans l'éditeur mais ça devrait être corrigé rapidement).

- Ne laisse pas le code auto-généré, spécifie simplement quand t'en parle genre "ajoutez un int à la fonction XXX (auto-générée grâce à YYY)".

Partager ce message


Lien à poster
Partager sur d’autres sites

Il y a du code au tout début, perdu :')

Aussi mets y un peu de forme, sépare dans des parties et tout (regarde mon tutoriel sur les launchers pour voir comment j'ai fait :p) et surtout DÉTAILLE PLUS !

Relis-toi pour les fautes aussi si tu peux ^^

Partager ce message


Lien à poster
Partager sur d’autres sites

Bonsoir, Titre pas très adapté au contenu du tutoriel "Inventaires et GUIs" 

Oui mais quoi comme inventaire ? Entité ? Bloc ? Item ? Joueur ? (je c c 1 entité lol)

Et pourquoi GUIs ? En général un inventaire (dans le délire MC) à forcément besoin d'une gui

Je te propose "[WIP] Bloc de type chest" ou autre, car là c'est clairement un coffre enfait

Voilà voilà ;)

Partager ce message


Lien à poster
Partager sur d’autres sites
Invité

Pour le système inventaire GUI il y a un mod qui prouve que les deux ne sont pas forcément lié : Botania mais sinon je pense que je vais prendre un autre titre oui.

Partager ce message


Lien à poster
Partager sur d’autres sites

Créer un compte ou se connecter pour commenter

Vous devez être membre afin de pouvoir déposer un commentaire

Créer un compte

Créez un compte sur notre communauté. C’est facile !

Créer un nouveau compte

Se connecter

Vous avez déjà un compte ? Connectez-vous ici.

Connectez-vous maintenant

×