Ajouter une entité à votre bloc (Tile Entity)


  • Administrateurs

    youtubeCe tutoriel est également disponible en vidéo.

    Sommaire

    Introduction

    Tous les blocs du même type possèdent la même instance cela cause problème lorsqu'on souhaite avoir des variables différentes sur chaque bloc du monde. Par exemple, si vous déclarez un nombre dans la classe du bloc, et que vous le faites varier en cliquant sur le bloc, vous constaterez qu'en plaçant deux blocs sur la map et en faisant varier le nombre sur le premier bloc puis sur le second bloc, les valeurs vont se succéder. De plus, si vous quittez le monde, le nombre reviendra à 0.
    Pour éviter ce problème il faut utiliser les entités de bloc (TileEntity). Les entités de bloc possèdent une instance par bloc posé dans le monde, ainsi leurs variables sont uniques. Par exemple, si vous placez deux coffres, que vous mettez un item dans l'un, il ne sera pas dans l'autre. C'est grâce aux tileentity, car comme dit plus haut, si la variable valant le contenu du coffre était dans la classe du bloc, tous les coffres auront le même contenu.
    Du fait que les entités de bloc ont une instance par bloc du monde, pour obtenir de l'instance d'une entité de bloc, il vous faudra 4 valeur pour instancier une entité de bloc :

    • le monde (World)
    • la coordonnée x (int)
    • la coordonnée y (int)
    • la coordonnée z (int)

    La fonction à utiliser est : world.getTileEntity(x, y, z)
    Les entités de bloc ont aussi une fonction pour écrire leurs données dans le tag nbt du monde, et une fonction pour lire ces données.

    Autre chose qui ne sera pas traitée dans ce tutoriel, les entités de bloc permettent aussi de faire un rendu de bloc avancé (comme le coffre et portail de l'end) avec les TileEntitySpecialRenderer.

    Ce qu'il faut retenir :
    Les entités de bloc permettent d'avoir une instance par bloc posé dans le monde.
    Les entités de bloc permettent de sauvegarder des données dans le monde.
    Les entités de bloc permettent de faire des rendus de bloc complexes.
    Maîtriser les entités de bloc est donc très important pour la suite.

    Pré-requis

    Code

    La classe du bloc :

    Pour signaler que votre bloc possède une entité de bloc, il faut ajouter deux méthodes. La première va spécifier la classe du tileentity, et la deuxième signaler que le bloc a une entité de bloc :

        @Override
        public TileEntity createTileEntity(World world, int metadata)
        {
            return new TileEntityTutoriel();
        }
    
        @Override
        public boolean hasTileEntity(int metadata)
        {
            return true;
        }
    

    Ces deux méthodes ont un argument int qui correspond au metadata, vous pouvez donc attribuer une entité de bloc différente en fonction du metadata :

        @Override
        public TileEntity createTileEntity(World world, int metadata)
        {
            if(metadata == 0)
            {
            return new TileEntityTutoriel();
            }
            else if(metadata == 1)
            {
                return new TileEntityTutoriel2();
            }
            else if(metadata == 2)
            {
                return new TileEntityTutoriel3();
            }
            return null;
        }
    
        @Override
        public boolean hasTileEntity(int metadata)
        {
            if(metadata >= 0 && metadata <= 2)
                return true;
            return false;
        }
    

    Ici mes blocs de 0 à 2 inclus ont une entité de bloc, les autres non.

    La classe du TileEntity :

    Créez simplement la classe :

    package fr.minecraftforgefrance.tutoriel.common;
    
    import net.minecraft.tileentity.TileEntity;
    
    public class TileEntityTutoriel extends TileEntity
    {
    
    }
    

    Et voila, l'entité de bloc est terminé. Dans le cas ou vous avez besoin d'un TileEntity pour faire un rendu TESR, vous n'avez pas besoin d'ajouter des fonctions dans cette classe.

    Dans l'autre cas où vous souhaitez enregistrer des variables dans le monde, vous aurez besoin de ces deux fonctions :

        @Override
        public void readFromNBT(NBTTagCompound compound)
        {
            super.readFromNBT(compound);
        }
    
        @Override
        public void writeToNBT(NBTTagCompound compound)
        {
            super.writeToNBT(compound);
        }
    

    La première va servir à lire les variables depuis un tag nbt, et la deuxième va servir à écrire les variables dans un tag nbt.
    Vous pouvez écrire différent types de variable :

    • compound.setByte(String nom, byte valeur)
    • compound.setShort(String nom, short valeur)
    • compound.setInteger(String nom, int valeur)
    • compound.setLong(String nom, long valeur)
    • compound.setFloat(String nom, float valeur)
    • compound.setDouble(String nom, double valeur)
    • compound.setString(String nom, String valeur)
    • compound.setByteArray(String nom, byte[] valeur)
    • compound.setIntArray(String nom, int[] valeur)
    • compound.setBoolean(String nom, boolean valeur)

    Et il existe évidemment un équivalent pour les lire :

    • valeur = compound.getByte(String nom)
    • valeur = compound.getShort(String nom)
    • valeur = compound.getInteger(String nom)
    • valeur = compound.getLong(String nom)
    • valeur = compound.getFloat(String nom)
    • valeur = compound.getDouble(String nom)
    • valeur = compound.getString(String nom)
    • valeur = compound.getByteArray(String nom)
    • valeur = compound.getIntArray(String nom)
    • valeur = compound.getBoolean(String nom)

    /!\ Important : Les variables que vous allez déclarer dans l'entité de bloc ne doivent pas être statiques ! Sinon vous perdez tout l’intérêt des entités de bloc, qui est d'avoir une instance par bloc posé dans le monde (les variables statiques n'ont qu'une instance, donc déclarer une variable statique dans l'entité de bloc reviendrait au même que déclarer une variable dans le bloc et causerai donc le même problème que celui énoncé dans l'introduction) /!\

    Deux autres fonctions qui pourront vous être utile :

        public Packet getDescriptionPacket()
        {
            NBTTagCompound nbttagcompound = new NBTTagCompound();
            this.writeToNBT(nbttagcompound);
            return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, 0, nbttagcompound);
        }
    
        public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt)
        {
            this.readFromNBT(pkt.func_148857_g());
        }
    

    Elles permettent d'envoyer au client toutes les valeurs qui ont été enregistrées dans le tag nbt lorsque l'entité de bloc est chargée.
    Attention, elles ne permettent pas d'envoyer les valeurs du client vers le serveur, pour cela il faut utiliser un paquet, ce que nous verrons plus tard dans un autre tutoriel.

    La classe principale :

    Toute entité de bloc doit être enregistrée, sinon vous allez avoir des erreurs et ses données ne seront pas enregistrés (la fonction writeToNBT en a besoin).
    Pour cela, dans la fonction init de votre classe principale, ajoutez :

            GameRegistry.registerTileEntity(VotreTileEntity.class, "modid:nom");
    

    Vous n'êtes pas obligé de mettre le modid dans le nom, mais je vous recommande de le mettre pour éviter tout risque de conflit avec un autre mod (si deux entités de bloc avec le même nom sont enregistrés, le jeu crash).

    Exemple d'utilisation :

    Vous n'avez pas compris comment utiliser writeToNBT et readFromNBT ? Heureusement cet exemple et là pour vous. Ici je vais enregistrer dans mon entité de bloc un int qui va pouvoir être augmenté en faisant un clic droit sur le haut du bloc, et diminué en faisait un clic droit sur le bas du bloc.
    Je vais simplement copier/coller le code et le commenter afin que vous le comprenez.

    La classe de l'entité de bloc :

    package fr.minecraftforgefrance.tutoriel.common;
    
    import net.minecraft.nbt.NBTTagCompound;
    import net.minecraft.tileentity.TileEntity;
    
    public class TileEntityTutoriel extends TileEntity
    {
        private int number; // on déclare le nombre qui va varier
    
        @Override
        public void readFromNBT(NBTTagCompound compound)
        {
            super.readFromNBT(compound);
            this.number = compound.getInteger("Number"); // pour lire sa valeur depuis  la sauvegarde du monde lorsqu'on charge le chunk qui contient l'entité de bloc
        }
    
        @Override
        public void writeToNBT(NBTTagCompound compound)
        {
            super.writeToNBT(compound);
            compound.setInteger("Number", this.number); // pour enregistrer sa valeur dans la sauvegarde du monde lorsqu'on décharge le chunk qui contient l'entité de bloc
        }
    
        public void increase() // une fonction pour augmenter sa valeur
        {
            this.number++;
        }
    
        public void decrease() // une fonction pour diminuer sa valeur
        {
            this.number--;
        }
    
        public int getNumber() // et une fonction pour obtenir sa valeur (on appelle ça un getter)
        {
            return number;
        }
    }
    

    La classe du bloc :

    package fr.minecraftforgefrance.tutoriel.common;
    
    import net.minecraft.block.Block;
    import net.minecraft.block.material.Material;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.ChatComponentTranslation;
    import net.minecraft.world.World;
    
    public class BlockTutoriel2 extends Block
    {
        protected BlockTutoriel2(Material material)
        {
            super(material);
        }
    
        @Override
        public boolean hasTileEntity(int metadata)
        {
            return true; // signale que le bloc a une entité
        }
    
        @Override
        public TileEntity createTileEntity(World world, int metadata)
        {
            return new TileEntityTutoriel(); // indique quelle est l'entité de bloc
        }
    
        public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int side, float hitX, float hitY, float hitZ)
        {
            /* 
                * petit explication sur la condition world.isRemote
                * world.isRemote = monde client, c'est celui qui va gérer le rendu
                * !world.isRemote = monde serveur, il va gérer le reste, notamment la sauvegarde, les variables d'une entité de bloc sont donc à manipuler côté serveur seulement, d'où la condition juste en dessous
                */
            if(!world.isRemote)
            {
                TileEntity tile = world.getTileEntity(x, y, z); // on obtient l'instance du TileEntity
                if(tile instanceof TileEntityTutoriel) // si le TileEntity est bien le nôtre (cette condition est importante pour éviter tout risque de corruption de monde, car il peut arriver qu'une mauvaise entité de bloc soit sur les coordonnées de votre bloc)
                {
                    TileEntityTutoriel tileTuto = (TileEntityTutoriel)tile; // on cast pour avoir accès au méthode qui se trouve dans TileEntityTutoriel
                    if(side == 0) // si le côté est 0, donc en dessous, on appelle la fonction decrease pour diminuer la valeur
                    {
                        tileTuto.decrease();
                    }
                    else if(side == 1) // si le côté est 1, donc en dessous, on appelle la fonction increase pour augmenter la valeur
                    {
                        tileTuto.increase();
                    }
                    player.addChatMessage(new ChatComponentTranslation("tile.tutoriel2.number", tileTuto.getNumber())); // et on affiche par un message tchat la valeur. ChatComponentTranslation permet de faire un String.format, dans mon fichier de lang je vais mettre %d qui sera remplacé par la valeur de tileTuto.getNumber(). (voir plus bas)
                    return true;
                }
            }
            return false;
        }
    }
    

    Dans mon fichier en_US.lang :

    tile.tutoriel2.number=Value : %d
    

    Comme dit plus haut, %d sera remplacé par la valeur de tileTuto.getNumber(). L'avantage avec ça, c'est que si dans un langage on aurait mit la valeur avant le texte, on aurait fait : %d texte.
    Pour les curieux, tapez sur un moteur de recherche "formatage string java", vous trouverez plus d'informations à ce sujet.

    Et pour finir, l'entité de bloc est enregistrée dans ma classe principale :

            GameRegistry.registerTileEntity(TileEntityTutoriel.class, "modtutoriel:tutoriel");
    

    Résultat

    Voir le commit sur github
    Le commit sur github montre clairement où ont été placés les fichiers, ainsi que ce qui a été ajouté et retiré dans le fichier.

    En vidéo

    Youtube Video

    Crédits

    Rédaction :

    Correction :

    Creative Commons
    Ce tutoriel de Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International

    retourRetour vers le sommaire des tutoriels



  • Ainsi, ils leurs variables sont uniques.

    Tu devrait plutôt dire "Ainsi leurs variables sont uniques"


  • Administrateurs

    En effet, merci d'avoir signalé, un "ils" sauvage s'était incrusté x)



  • @'robin4002':

    En effet, merci d'avoir signalé, un "ils" sauvage s'était incrusté x)

    En plus je me souviens de l'avoir lus pendant la période de rédaction xD (j'avais vue une faute :)).



  • Très bon tutoriel, merci robin !



  • Changement 1.8 :

    Dans la classe de la TileEntity :

    ​ @Override
    
    public Packet getDescriptionPacket() {
    NBTTagCompound nbttagcompound = new NBTTagCompound();
    this.writeToNBT(nbttagcompound);
    return new S35PacketUpdateTileEntity(this.pos, 0, nbttagcompound);
    }
    
    @Override
    
    public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) {
    super.onDataPacket(net, pkt);
    this.readFromNBT(pkt.getNbtCompound());
    }
    


  • bonjour je voudrais savoir si en utilisant compound.setIntArray(String nom, int[][][] valeur) çà marche ?



  • @'Asmath':

    bonjour je voudrais savoir si en utilisant compound.setIntArray(String nom, int[][][] valeur) çà marche ?

    Non, il faut mettre des listes dans des listes (list-ception). Mais pourquoi avoir une triple array de int ?! (C'est pour le challenge de la pièce fermée ?)



  • exact c'est pour détecter la sortie de la piece et j'ai pas envie que tout les block partage les piece entre eux (surtout que çà bugerrai un peu)



  • @'Asmath':

    exact c'est pour détecter la sortie de la piece et j'ai pas envie que tout les block partage les piece entre eux (surtout que çà bugerrai un peu)

    J'ai l'impression que tu cherches un peu compliqué.



  • moi aussi mais je trouve pas de faille dans mon raisonnent et si tu veux continuer cette conversation fait le en message privé qu'on flood pas le forum



  • çà doit etre tout bete , mais y a t'il moyen de détecter un click droit directemetn dans le tile entity , sans passer par la class du block ?



  • Non, mais tu peux tout simplement appeler une méthode de la TileEntity depuis le block en lui passant tous les paramètres.



  • oui tu as raison , suis je bete , la réponse est oui visiblement



  • sinon autre probleme : j'ai mis un constructeur , mais le probleme c'est que quand il est lancée les nbt tag ne sont pas encore "mis a jour" comment je peut changer çà ?


  • Administrateurs

    Pas possible.
    Passe par la fonction update, ou fait ce que tu veux directement dans readFromNBT.



  • Bonjour,
    Vue le moment ou j'écrit mon post je ne pense pas avoir une réponse, mais bon je vais test on sais jamais.
    Voilà j'aimerais juste créer un nouveaux four pour des minerais que j'ai créer, mais je ne trouve pas de tutoriel valable ENTIÈREMENT avec la version 1.8 de Forge et sa commence a m'énerver ^^
    J’espère potentiellement une réponse, bisous 😘


  • Administrateurs

    En effet il y a encore très peu de tutoriel 1.8, en raison des nombreux changement de la 1.8 il faut du temps pour rédiger des nouveaux tutoriels et également expérimenté cette nouvelle version. Or quand on est étudiant le temps libre ce n'est pas qu'on a le plus.

    Je n'ai aucun lien sous la main pour créer un four en 1.8, désolé.



  • Et les sources de Minecraft ?



  • woaw une réponse aussi vite, merci les gars, 
    Comment sa les sources de minecraft ? Je m'y connait que très peux ::D
    Sinon quelqu'un qui si connait sur les mods pour m'aider et se faire un skype ? :3