Créer un bloc avec des metadatas


  • Rédacteurs

    youtubeCe tutoriel est également disponible en vidéo.

    Sommaire

    Introduction

    Bienvenue dans mon premier tutoriel qui aura pour but d'apprendre à réaliser des blocs avec des metadatas.
    Vous avez sûrement remarqué que certains blocs ont des metadatas, ce qui donne un nom du genre <nom dans le game data>:1, <nom dans le game data>:2 etc... Dans ce tutoriel, vous allez apprendre à créer plusieurs blocs en utilisant les metadatas, ce qui permet d'avoir 16 blocs sur un seul id ( metadata de 0 à 15 au maximum, vous pouvez en avoir moins).
    L'avantage des metadatas est donc de pouvoir mettre 16 blocs sur le même id (même si les ids ne sont plus vraiment visibles, il existe toujours, la limite des 4096 blocs), cela permet donc d'économiser les ids. Il existe aussi une autre façon d'utiliser ces metadatas que nous ne verrons pas dans ce tutoriel, par exemple on peut les utiliser comme information, c'est ce que Mojang fait avec beaucoup de blocs de Minecraft (exemple : direction de la porte, de la citrouille, des bûches, etc ...).

    Pré-requis

    Code

    Petit rappel, un bloc est aussi un item, nous allons d'ailleurs voir clairement apparaître cet item dans ce tutoriel. Il faut donc deux classes, celle du bloc et celle de l'item associé au bloc (ItemBlock).

    La classe principale :

    Commençons par déclarer notre bloc, il se déclare comme tous les autres :

        public static Block blockMetadataTuto;
    

    Si vous avez déjà d'autres blocs déclarés, vous pouvez le déclarer à la suite :

        public static Block blockTutoriel, blockTutoriel2, blockMetadataTuto;
    

    et on déclare notre bloc de façon normale comme tous les autres.

            blockMetadataTuto = new BlockTutorielMetadata().setBlockName("metadataTuto").setHardness(1.5F).setResistance(10.0F).setCreativeTab(CreativeTabs.tabBlock);
    

    La seule différence est qu'il n'y a pas la fonction pour les textures, elles seront déclarées dans la classe de notre bloc puisqu'il faut une texture pour chaque metadatas.

    Nous allons maintenant enregistrer notre bloc avec une petite spécificité :

        GameRegistry.registerBlock(blockMetadataTuto, ItemBlockMetadataTutoriel.class, "block_tuto_metadata");
    

    blockMetadataTuto est le bloc. ItemBlockMetadataTutoriel.class et une nouvelle classe que nous allons créer. Il s'agit de l'item bloc dont nous avons déjà parlé plus tôt. "block_tuto_metadata" est le nom du bloc dans le game data, utilisé comme référence pour le bloc (pour le give, etc ...).

    Créez la classe BlockTutorielMetadata avec net.minecraft.block.Block en superClass et ItemBlockMetadataTutoriel avec net.minecraft.item.ItemBlock en superClass.

    La classe du bloc :

    Tout comme un bloc basique créez le constructeur, pour l'instant rien de change d'un bloc classique :

    package fr.minecraftforgefrance.tutoriel.common;
    
    import net.minecraft.block.Block;
    import net.minecraft.block.material.Material;
    
    public class BlockTutorielMetadata extends Block
    {
        public BlockMetadata()
        {
            super(Material.rock);
        }
    }
    

    Ensuite ajoutez au-dessus du constructeur ceci :

        public static String[] subBlock = new String[] {"block1", "block2", "block3", "block4"};
        public IIcon[] iconArray = new IIcon[subBlock.length];
    

    Ceci est un tableau de chaîne de caractères. Je l'ai appelé subBlock, vous pouvez le nommer comme vous voulez. Nous allons utiliser ce tableau pour l'enregistrement des textures, et pour les noms. block1, block2, etc... correspondent aux noms des futurs .png et aux noms non-localisés, adaptez-les selon vos besoins. Je vous conseille d'utiliser un tableau cela permet de beaucoup simplifier le code vu que les variables à l'intérieur vont être utilisées pour plusieurs choses.
    En dessous, j'ai fait un tableau d'icônes, il va servir pour les textures. (Importez net.minecraft.util.IIcon; )

    Nous allons maintenant enregistrer les textures des différents blocs avec cette méthode :

        public void registerBlockIcons(IIconRegister iconRegister)
        {
            for(int i = 0; i < subBlock.length; i++)
            {
                this.iconArray[ i] = iconRegister.registerIcon(ModTutoriel.MODID + ":" + subBlock*);
            }
        }
    

    La boucle for va permettre d'enregistrer toutes les icônes en fonction de la taille du tableau subBlock. Ainsi, si vous souhaitez ajouter un bloc de plus, il vous suffit de l'ajouter au tableau "subBlock" ;).

    Maintenant il faut ajouter la fonction pour voir les blocs dans la table créative :

        public void getSubBlocks(Item item, CreativeTabs tabs, List list)
        {
            for(int i = 0; i < subBlock.length; i++)
            {
                list.add(new ItemStack(item, 1, i));
            }
        }
    

    À nouveau la boucle for nous simplifie la vie, le principe est le même qu'au-dessus. Pour les erreurs, faites ctrl + shift + o pour organiser les importations, pour List, choisissez "java.util.List"

    Il ne nous reste plus qu'à finaliser la texture, car pour l'instant notre bloc utilise l'icône "blockIcon" comme il est extends Block. Nous allons donc ajouter la fonction getIcon dans notre bloc :

        public IIcon getIcon(int side, int metadata)
        {
            if(metadata >= 0 && metadata < subBlock.length)
            {
                return this.iconArray[metadata];
            }
            return this.iconArray[0];
        }
    

    La condition est très importante, en effet elle sert à éviter un java.lang.ArrayIndexOutOfBoundsException. Si par erreur le joueur se give le bloc avec un metadata négatif ou supérieur à la taille du tableau (avec /give <pseudo> <bloc> <quantité> <metadata>), il aura un bloc avec la texture du metadata 0. Sans la condition, le jeu crash.
    Pour ceux qui sont à l'aise avec les conditions ternaires, voici le code "simplifié" :

        public IIcon getIcon(int side, int metadata)
        {
            return metadata >= 0 && metadata < subBlock.length ? this.iconArray[metadata] : this.iconArray[0];
        }
    

    Enfin on va définir le drop du bloc de façon à avoir le bon metadata :

        public int damageDropped(int metadata)
        {
            return metadata;
        }
    

    Elle est assez importante, si vous ne la mettez pas tous vos blocs vont dropper le metadata 0 ce qui est un peu embêtant.

    La classe de l'itemblock :

    Commencez par ajouter un constructeur, et dans ce constructeur ajoutez la méthode this.setHasSubtypes(true);. Sans cette méthode, vous n'arriverez pas à vous give un metadata supérieur à 0.

    package fr.minecraftforgefrance.tutoriel.common;
    
    import net.minecraft.item.ItemBlock;
    
    public class ItemBlockMetadataTutoriel extends ItemBlock
    {
        public ItemBlockMetadataTutoriel(Block block)
        {
            super(block);
            this.setMaxDamage(0);
            this.setHasSubtypes(true);
        }
    }
    

    Ensuite, ajoutez cette fonction :

        public int getMetadata(int metadata)
        {
            return metadata;
        }
    

    Par défaut dans Item.java cette méthode renvoie sur 0, c'est pour ça qu'il est important de la mettre, sinon même avec un bloc de metadata 1 en main, lorsque vous le poserez, il deviendra un bloc de metadata 0. Donc si un jour vous avez ce problème, vous saurez que vous avez oublié de mettre cette méthode. C'est un oubli vite arrivé mais au moins vous savez d'où viendra l'erreur.

    Il faut aussi ajouter une fonction pour la texture :

        @SideOnly(Side.CLIENT)
        public IIcon getIconFromDamage(int metadata)
        {
            return this.field_150939_a.getIcon(2, metadata);
        }
    

    Il reste encore à faire la méthode pour le nom, c'est là que le tableau est utile, vous allez donc ajouter :

        public String getUnlocalizedName(ItemStack stack)
        {
            int metadata = stack.getItemDamage();
            if(metadata < 0 || metadata >= BlockTutorielMetadata.subBlock.length)
            {
                metadata = 0;
            }
            return super.getUnlocalizedName() + "." + BlockTutorielMetadata.subBlock[metadata];
        }
    

    Une fois de plus, if(metadata < 0 || metadata >= BlockTutorielMetadata.subBlock.length) est une condition pour éviter un java.lang.ArrayIndexOutOfBoundsException.
    Le nom non localisé sera donc tile.<le nom dans setblockname("")>.<le nom dans le tableau type>.name

    Les noms et les textures :

    Allez dans votre dossier forge/src/main/resources/assets/votre_mod_id/textures/block/ et créez tous les fichiers .png correspondant aux noms que vous avez mis dans le tableau type. Retournez dans forge/src/main/resources/assets/votre_mod_id/lang, ouvrez le fichier en_US.lang, et ajoutez les noms :
    tile.<le nom dans setblockname("")>.<le nom dans le tableau type>.name=le nom localisé.
    Attention je vous rappelle qu'il ne doit pas y avoir d'espace entre « name= » et le nom du bloc. Exemple dans mon cas :

    tile.metadataTuto.block1.name=Metadata Block 1
    tile.metadataTuto.block2.name=Metadata Block 2
    tile.metadataTuto.block3.name=Metadata Block 3
    tile.metadataTuto.block4.name=Metadata Block 4
    

    Bonus

    La classe de l'item du bloc va être presque la même pour tous vos blocs avec des metadatas, seul BlockTutorielMetadata.subBlock va changer dans la fonction getUnlocalizedName. Il est possible d'utiliser la même classe pour tous vos items de bloc.
    Déclarez un tableau de String dans la classe de votre item de bloc et initialisez-le dans le constructeur. Dans la fonction getUnlocalizedName, remplacez BlockTutorielMetadata.subBlock par ce tableau :

    package fr.minecraftforgefrance.tutoriel.common;
    
    import cpw.mods.fml.relauncher.Side;
    import cpw.mods.fml.relauncher.SideOnly;
    import net.minecraft.block.Block;
    import net.minecraft.item.ItemBlock;
    import net.minecraft.item.ItemBlockWithMetadata;
    import net.minecraft.item.ItemStack;
    import net.minecraft.util.IIcon;
    
    public class ItemBlockMetadataTutoriel extends ItemBlock
    {
        private final String[] subName;
        public ItemBlockMetadataTutoriel(Block block, String[] subBlock)
        {
            super(block);
            this.setMaxDamage(0);
            this.setHasSubtypes(true);
            this.subName = subBlock;
        }
    
        @SideOnly(Side.CLIENT)
        public IIcon getIconFromDamage(int metadata)
        {
            return this.field_150939_a.getIcon(2, metadata);
        }
    
        public int getMetadata(int metadata)
        {
            return metadata;
        }
    
        public String getUnlocalizedName(ItemStack stack)
        {
            int metadata = stack.getItemDamage();
            if(metadata < 0 || metadata >= this.subName.length)
            {
                metadata = 0;
            }
            return super.getUnlocalizedName() + "." + this.subName[metadata];
        }
    }
    

    Maintenant dans votre classe principale, remplacez :

            GameRegistry.registerBlock(blockMetadataTuto, ItemBlockMetadataTutoriel.class, "block_tuto_metadata");
    

    par :

            GameRegistry.registerBlock(blockMetadataTuto, ItemBlockMetadataTutoriel.class, "block_tuto_metadata", new Object[]{BlockTutorielMetadata.subBlock});
    

    BlockTutorielMetadata.subBlock correspond au tableau de string où se trouvent les noms des sous blocs.

    Ainsi si je souhaite ajouter un second bloc avec des metadatas (donc 2x16 au max, soit 32 blocs en deux ids ;)); il me suffit de suivre les sous parties La classe principale, La classe du bloc, Les noms et les textures et d'enregistrer mon second bloc avec :

            GameRegistry.registerBlock(blockMetadataTuto2, ItemBlockMetadataTutoriel.class, "block_tuto_metadata_2", new Object[]{BlockTutorielMetadata2.subBlock});
    

    La classe ItemBlockMetadataTutoriel reste la même pour les deux, et je peux encore en ajouter d'autres si besoin.

    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.

    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



  • Bonsoir !
    C'est un trés bon tutoriel, comme tout ceux que vous faites ici !
    Tu expliques vraiment bien, merci j'ai tout compris et ça fonctionne cette fois-ci ! ;D


  • Rédacteurs

    merci, robin a rectifié certaine des explications donc je te dit aussi merci de sa part.
    J'espère que tu en profitera bien, pour ma part je suis à l'étape au dessus, celle où tu dépasse le nombre de metadatas autorisé (16), c'est devenu ma marque de fabrique.



  • Bah personnelement, je n'ai pas encore besoin d'avoir plus de 16 autorisés donc… ;D

    Ton mod à l'air assez cool ! Mais les images ne s'affichent pas ! ^^
    Je viens de voir que tu avais fait de la laine lumineuse ? Le mod que je suis en train de développer est justement un mod qui rajoute despierres lumineuses et plusieursautres choses que je révelerais plus tard...


  • Rédacteurs

    Mon site web est down et j'ai pas encore re-upload toutes les images



  • J'aurais une question par rapport au sujet principal, j'ai regardez la vidéo youtube, j'ai saise que les métadata permettait de "économiser" les ID, ou faire genre des porte (fermé c'est un bloc, ouvert s'en est un autre), ça j'ai piger

    la vient ma question, en faite j'aimerais faire en sorte qu'un block drop autre chose
    (exemple block redstone, drop poudre redstone),

    qu'elle rapport avec les metadata ?

    (dans customisation du block, j'ai vue une ligne de code, avec (int metadata))
    d'ou ma question ^^

    ensuite 2 ème question,
    dans la vidéo à 14min il dis
    "i = 0 (égal)"

    es qu'il pense à une affectation ? ou vraiment à faire = ?

    je débute dans le modding, soyez gentil avec les coups de fouet 🙂



  • Tu peux faire dropper des trucs différents selon la metadata du bloc
    Exemple : le bloc jeSuisUnBloc drop des melons si il a la metadata 0, des citrouilles si il a une metadata 1…



  • oooooh ok je vois merci à toi 🙂



  • Pour l'affectation (oui c'est une affectation), c'est juste pour la boucle for c'est la valeur qui va être incrémentée. Elle est ensuite utilisée pour le code dans la boucle. Si tu ne comprends pas la boucle for regarde sur google 🙂



  • en faite j'étudie en ce moment même les boucle, et j’avais un doute.

    encore une fois merci, je me doutais de la chose, mais je voulais être sur ^^

    dernière question si je veux faire en sorte que un block drop autre chose (genre redstone et poudre redstone)
    il lui faut obligatoirement des metadata ?


  • Administrateurs

    Non, pas besoin. Le paramètre metadata sert uniquement à faire des conditions pour droper quelque chose de différent en fonction du metadata, mais si tu n'as pas de metadata il suffit juste de ne pas mettre de condition et c'est bon.



  • ok merci

    PS: sa y est c'est bon j'ai réussi a faire drop mes minerai en les minant, merci beaucoup pour vos réponse



  • Bonsoir ! Voilà je viens juste poser une petite question 🙂

    Voilà, je suis débutant et je voudrais comprendre quelque chose, je ne comprends pas comment utiliser les metadata après.

    Par exemple pour la génération de minerais à un moment il faut entrer un metadata :

    this.addOreSpawn(…

    Ce que je n'arrive pas à comprendre c'est que une fois que j'ai suivis le tutoriel comment définir les metadata ? Comment savoir quel numéro appartient à quel metadata ?

    J’espère que vous allez arriver à comprendre ce que je veux dire car c'est très flou .. x)

    Merci d'avance à tous ! 😉


  • Rédacteurs

    regarde la tableau :

    public static String[] subBlock = new String[] {"block1", "block2", "block3", "block4"};
    

    "block1" est le metadata 0, "block2" est le metadata 1, pour faire simple leur position dans le tableau définie leur metadata.

    Si ça ne répond pas à ta question, donne nous plus de détails sur ce que tu veux faire, que l'on puisse t'aider



  • @'Phenix246':

    regarde la tableau :

    public static String[] subBlock = new String[] {"block1", "block2", "block3", "block4"};
    

    :

    "block1" est le metadata 0, "block2" est le metadata 1, pour faire simple leur position dans le tableau définie leur metadata.

    Si ça ne répond pas à ta question, donne nous plus de détails sur ce que tu veux faire, que l'on puisse t'aider

    C'était exactement cela !
    Du coup au passage j'en profite :p, si je veux que mon Block1 drop un item que j'ai créé (par exemple une épée) comment je dois faire ?
    Et aussi, imaginons que je créer encore une nouvelle sorte de bloc, je vais donc recréer des metadata, du coup comment je vais devoir faire, car si dans le tableau de mon block je suis l'ordre -> Block1 = metadata 0, block2 = metadata 1 et que je m'arrête au bloc 4 donc block4 = metadata 3, dans ma nouvelle classe pour ma deuxième sorte de bloc je reprends dans l'ordre ou je m'étais arrêté dans l'autre ? Du coup : le bloc1 = metadata 4 ?

    Merci encore pour ta réponse ! 🙂


  • Administrateurs

    Non, si tu fais un autre bloc, ça reprend à 0. Chaque bloc a ces metadata.
    Pour les drop il faut passer par la fonction getItemDropped



  • @'robin4002':

    Non, si tu fais un autre bloc, ça reprend à 0. Chaque bloc a ces metadata.
    Pour les drop il faut passer par la fonction getItemDropped

    D'accord d'accord !

    Dernière question et merci d’ailleurs pour toutes ses réponses !

    Du coup lors de la génération de minerais, dans la méthode :

    
    (new WorldGenMinable(ModTutoriel.TutorialMetadata.blockID, 5, 12, Block.stone.blockID)).generate(world, rand, x + rand.nextInt(16), 16 + rand.nextInt(32), z + rand.nextInt(16));
    

    Ici on a

    
    ModTutoriel.TutorialMetadata.blockID
    

    Quand j'aurais finis de créer ma classe avec mes metadata, le blockId sera le nom du bloc définis dans le tableau et le metadata celui de celui-ci ?
    Merci encore 🙂


  • Rédacteurs

    vieux système en 1.7 les ".blockID" ont totalement disparu :

    (new WorldGenMinable(ModTutoriel.TutorialMetadata, 5, 12, Blocks.stone)).generate(world, rand, x + rand.nextInt(16), 16 + rand.nextInt(32), z + rand.nextInt(16));
    


  • @'Phenix246':

    vieux système en 1.7 les ".blockID" ont totalement disparu :

    (new WorldGenMinable(ModTutoriel.TutorialMetadata, 5, 12, Blocks.stone)).generate(world, rand, x + rand.nextInt(16), 16 + rand.nextInt(32), z + rand.nextInt(16));
    

    Merci beaucoup !
    Enfin grâce à vous j'ai réussi !

    Dernière dernière question après je n'embête plus !

    Pour le drop Robin ma dit d'utiliser getItemDropped, j'ai essayé plusieurs chose mais je ne trouve pas comment dropper un block, par exemple if metata = 1 on return Monmod.Item1


  • Rédacteurs

    public Item getItemDropped(int metadata, Random rand, int fortune)
    {
    return metadata == 1 ? Monmod.Item1 : Item.getItemFromBlock(this);
    }
    
    public int damageDropped(int metadata)
    {
    return metadata == 1 ? "damage" : 1;
    }
    
    public int quantityDropped(int metadata, int fortune, Random par3Random)
    {
    return "nombre d'items"
    }
    

    Je te donne en plus deux autre méthode dont tu peux avoir besoin (la damage de l'item que tu drop et la quantité dropé)