26 mars 2014, 12:58

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