28 févr. 2017, 15:47

Sommaire

Introduction

Dans ce tutoriel nous allons apprendre à créer un bloc avec des métadonnées allant de 0 à 15.

Pré-requis

Code

La classe du bloc :

Vous devez avoir un bloc déjà créé et enregistré. Si tout s’est déroulé correctement vous devriez avoir une classe similaire :

public class TutorialBlock extends Block
{
    public static final String NAME = "tutorial_block"

    public TutorialBlock(Material material)
    {
        super(material);

        TutorialBlocks.setBlockName(this, NAME);

        setResistance(5.0F);
        setHardness(3.0F);
        setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
    }
}

Nous allons ajouter de nombreuses fonctions dans cette classe mais également une énumération pour stocker l’intégralité des différentes possibilités pour le bloc.
Vous devez obtenir une classe comme ceci :

public class TutorialBlock extends Block
{
    // Ici les variables dont nous avons besoin pour la classe. Nous allons en rajouter une.

    public TutorialBlock(Material material)
    {
        // Ici le constructeur, nous allons ajouter une ligne.
    }

    @Override
    public int damageDropped(IBlockState state)
    {
        // Ici nous allons dire à Forge quelle valeur de métadonnée doit avoir l'objet obtenu lorsque l'on casse le bloc.
    }

    @Override
    public void getSubBlocks(Item itemIn, CreativeTabs tab, NonNullList <ItemStack>list)
    {
        // Nous allons définir ici les variantes du bloc.
    }

    @Override
    public IBlockState getStateFromMeta(int meta)
    {
        // Nous allons renvoyer l'état du bloc selon sa métadonnée.
    }

    @Override
    public int getMetaFromState(IBlockState state)
    {
        // Nous allons renvoyer la métadonnée selon l'état du bloc.
    }

    @Override
    protected BlockStateContainer createBlockState()
    {
        // Nous allons créer l'état de l'objet.
    }

    public static enum EnumType implements IStringSerializable
    {
        // Nous allons définir ici les différents sous-blocs.
        ;

        // Nous allons créer ici quelques variables que nous utiliserons dans les fonctions.

        private EnumType(int metaIn, String nameIn, String unlocalizedIn)
        {
            // Ici nous créons le constructeur de l'énumération
        }

        public static String[] getUnlocalizedNames()
        {
            // Nous utiliserons cette fonction pour récupérer une liste des noms non-localisés.
        }

        public int getMetadata()
        {
            // Nous utiliserons cette fonction pour obtenir la métadonnée de l'objet.
        }

        public static TutorialBlock.EnumType byMetadata(int meta)
        {
            // Nous allons ici obtenir la valeur dans l'énumération correspondant à la métadonnée passée en paramètre. Remplacez TutorialBlock par le nom de votre classe.
        }

        public String toString()
        {
            // Nous allons renvoyer le nom de l'objet.
        }

        @Override
        public String getName()
        {
            // Nous allons ici aussi renvoyer le nom de l'objet.
        }
    }
}

Il y a un nombre conséquent de fonctions à définir mais ne vous inquiétez pas, nous allons y aller pas à pas et vous allez vous rendre compte que ce code n’est pas si compliqué qu’il en a l’air.

Nous allons commencer par nous placer dans l’énumération et nous allons la définir.

Avant le premier “;”, ajoutez ces deux lignes :

FIRST(0, "tutorial", "tutorial_block"),
SECOND(1, "tutorial2", "tutorial_block_m1");

Vous définissez les éléments de l’énumération, avec comme métadonnées 0 et 1, et comme noms “tutorial” et “tutorial2”. Vous pouvez modifier FIRST et SECOND, leurs noms ne sont pas très importants pour ce tutoriel. Vous pouvez également modifier les valeurs des chaînes de caractères, cependant vous ne devez pas modifier les métadonnées car vous pourriez obtenir des erreurs si vous ne les modifiez pas correctement. Vous pouvez également modifier les noms non-localisés (troisième argument) comme bon vous semble, faites simplement attention à le modifier lorsque vous le verrez par la suite.

Nous allons ensuite définir quatre variables :

private static final TutorialBlock.EnumType[] META_LOOKUP = new TutorialBlock.EnumType[values().length];
private final int meta;
private final String name;
private final String unlocalizedName;

Nous les initialiserons plus tard. La première variable est simplement une liste de tous les éléments de l’énumération. Vous comprendrez son utilité par la suite.

Nous allons nous placer dans le constructeur pour initialiser trois de ces valeurs :

private EnumType(int metaIn, String nameIn, String unlocalizedIn)
{
    this.meta = metaIn;
    this.name = nameIn;
    this.unlocalizedName = unlocalizedIn;
}

Voici la fonction getUnlocalizedNames. Elle utilise META_LOOKUP que nous initialiserons plus loin dans le code.

public static String[] getUnlocalizedNames() {
    String[] names = new String[values().length];

    for (int i = 0; i < META_LOOKUP.length; i++)
        names[i] = META_LOOKUP[i].unlocalizedName;

    return names;
}

Pour chaque valeur dans META_LOOKUP, nous récupérons la valeur du nom non-localisé et la mettons dans names.

La fonction getMetadata ne fait rien d’autre que renvoyer la valeur de meta :

public int getMetadata()
{
    return this.meta;
}
public static TutorialBlock.EnumType bymetadata(int meta)
{
    if (meta < 0 || meta >= META_LOOKUP.length)
    {
        meta = 0;
    }

    return META_LOOKUP[meta]
}

Ce code est simple, on vérifie si la valeur passée en paramètre est bien une valeur de métadonnée valide, sinon nous renvoyons la valeur du bloc avec comme métadonnée 0.

Dans les fonctions toString() et getName(), ajoutez cette ligne :

return this.name;

Nous en avons presque terminé avec l’énumération. Ajoutez simplement ces quelques lignes pour initialiser META_LOOKUP :

static
{
    for (TutorialBlock.EnumType type : values())
    {
        META_LOOKUP[type.getMetadata()] = type;
    }
}

Nous en avons terminé avec notre énumération, nous sommes prêts à faire des blocs avec métadonnées !
Mais il nous reste quelques fonctions à définir dans la classe du bloc.

Après la variable NAME, ajoutez cette ligne :

public static final PropertyEnum<TutorialBlock.EnumType> VARIANT = PropertyEnum.<TutorialBlock.EnumType>create("variant", TutorialBlock.EnumType.class);

Cette variable va être utilisée pour définir l’objet correspondant à la métadonnée correcte dans l’état du bloc.

setDefaultState(this.blockState.getBaseState.withProperty(VARIANT, TutorialBlock.EnumType.FIRST));

Cette ligne doit se trouver dans le constructeur et elle définit l’état du bloc par défaut, en utilisant notre énumération.

Dans la fonction damageDropped, vous devez écrire ceci :

return state.getValue(VARIANT).getMetadata();

Ce code récupère la valeur de la propriété VARIANT (que nous avons définie au début de la classe), et prend la valeur de la métadonnée correspondante.

Voici le code la fonction getSubBlocks :

for (TutorialBlock.EnumType type : TutorialBlock.EnumType.values())
{
    list.add(new ItemStack(itemIn, 1, type.getMetadata()));
}

Il ne fait rien d’autre qu’ajouter toutes les valeurs de l’énumération en tant que sous-blocs.

@Override
public IBlockState getStateFromMeta(int meta) {
    return this.getDefaultState().withProperty(VARIANT, TutorialBlock.EnumType.byMetadata(meta));
}

@Override
public int getMetaFromState(IBlockState state)
{
    return ((TutorialBlock.EnumType)state.getValue(VARIANT)).getMetadata();
}

@Override
protected BlockStateContainer createBlockState()
{
    return new BlockStateContainer(this, new IProperty[] {VARIANT});
}

La première fonction appelle simplement la fonction byMetadata de notre énumération, et renvoie sa valeur.
La seconde appelle la fonction getMetadata de notre énumération.
Et la dernière fonction crée un état de bloc avec notre propriété VARIANT.

Nous en avons terminé avec la classe du bloc, ce n’était pas si compliqué, vous voyez ?

Vous devriez obtenir une classe ressemblant à celle-ci :

public class TutorialBlock extends Block
{

    public static final String NAME = "tutorial_block";
    public static final PropertyEnum <tutorialblock.enumtype>VARIANT = PropertyEnum.<tutorialblock.enumtype>create("variant", TutorialBlock.EnumType.class);

    public TutorialBlock(Material material)
    {
        super(material);

        TutorialBlocks.setBlockName(this, NAME);
        setDefaultState(this.blockState.getBaseState().withProperty(VARIANT, TutorialBlock.EnumType.FIRST));

        setResistance(5.0F);
        setHardness(3.0F);
        setCreativeTab(CreativeTabs.BUILDING_BLOCKS);
    }

    @Override
    public int damageDropped(IBlockState state)
    {
        return state.getValue(VARIANT).getMetadata();
    }

    @Override
    public void getSubBlocks(Item itemIn, CreativeTabs tab, NonNullList <ItemStack> list)
    {
        for (TutorialBlock.EnumType type : TutorialBlock.EnumType.values()) {
            list.add(new ItemStack(itemIn, 1, type.getMetadata()));
        }
    }

    @Override
    public IBlockState getStateFromMeta(int meta)
    {
        return this.getDefaultState().withProperty(VARIANT, TutorialBlock.EnumType.byMetadata(meta));
    }

    @Override
    public int getMetaFromState(IBlockState state)
    {
        return ((TutorialBlock.EnumType)state.getValue(VARIANT)).getMetadata();
    }

    @Override
    protected BlockStateContainer createBlockState()
    {
        return new BlockStateContainer(this, new IProperty[] {VARIANT});
    }

    public static enum EnumType implements IStringSerializable
    {
        FIRST(0, "tutorial", "tutorial_block"),
        SECOND(1, "tutorial2", "tutorial_block_m1");

        private static final TutorialBlock.EnumType[] META_LOOKUP = new TutorialBlock.EnumType[values().length];
        private final int meta;
        private final String name;
        private final String unlocalizedName;

        private EnumType(int metaIn, String nameIn, String unlocalizedIn)
        {
            this.meta = metaIn;
            this.name = nameIn;
            this.unlocalizedName = unlocalizedIn;
        } 

        public static String[] getUnlocalizedNames()
        {
            String[] names = new String[values().length];

            for (int i = 0; i < META_LOOKUP.length; i++)
                names[i] = META_LOOKUP[i].unlocalizedName;

            return names;
        }

        public int getMetadata()
        {
            return this.meta;
        }

        public static TutorialBlock.EnumType byMetadata(int meta)
        {
            if (meta < 0 || meta >= META_LOOKUP.length)
            {
                meta = 0;
            }

            return META_LOOKUP[meta];
        }

        public String toString()
        {
            return this.name;
        }

        @Override
        public String getName()
        {
            return this.name;
        }

        static
        {
            for (TutorialBlock.EnumType type : values())
            {
                META_LOOKUP[type.getMetadata()] = type;
            }
        }        
    }
}

La classe des items :

Dans la classe des items, nous allons définir l’ItemBlock légèrement différemment, parce que nous allons créer notre propre classe qui héritera de l’ItemBlock.
Voici donc la déclaration :

public static final Item BLOCK_TUTORIAL_ITEM = new ItemBlockMetadata(TutorialBlocks.TUTORIAL, new String[]{"tutorial_block", "tutorial_block_m1"} ).setRegistryName(TutorialBlocks.TUTORIAL.getRegistryName());

Nous allons créer la classe dans la prochaine partie.

Et dans votre fonction registerItemsModels, écrivez ces lignes :

for (int i = 0; i < TutorialBlock.EnumType.values().length; i++)
    registerModel(BLOCK_TUTORIAL_ITEM, i);

Il va simplement définir les modèles pour chaque métadonnée.

Nous en avons fini pour la classes des items.

La classe de l’ItemBlock :

Voici la classe que nous allons remplir :

public class ItemBlockMetadata extends ItemBlock
{
    String[] unlocalizedNames;

    public ItemBlockmetadata(Block block, String[] unlocalizedNamesIn)
    {
        super(block);
    }

    @Override
    public int getMetadata(int damage)
    {

    }

    @Override
    public String getUnlocalizedName(ItemStack stack)
    {

    }
}

Placez-vous dans le constructeur et ajoutez ces trois lignes :

this.unlocalizedNames = unlocalizedNamesIn;
setHasSubtypes(true);
setMaxDamage(0);

La première ligne initialise notre liste de noms non-localisés avec la liste passée en paramètres.
La seconde prévient Forge que cet objet possède des sous-objets, utilisant des métadonnées.
La dernière ligne prévient Forge que les métadonnées sont des métadonnées, et non pas des dégâts infligés à l’objet. Elle empêche donc l’apparition d’une ligne de durabilité sur l’objet. (parce qu’il faut savoir que les objets tels que les outils utilisent les métadonnées pour définir le taux de destruction de l’objet).

Dans la fonction getMetadata, nous allons simplement renvoyer comme valeur le paramètre :

return damage;

Par défaut, Minecraft renvoie 0, ignorant le paramètre. En renvoyant le paramètre, nous permettons au jeu de savoir que l’objet possède une métadonnée spécifique.

La dernière fonction doit être remplie comme ceci :

return ModTutorial.MODID + "." + unlocalizedNames[stack.getMetadata()];

Nous aurons donc un nom non-localisé de la forme modid.nomdelametadonnee. Nous utilisons ici le troisième paramètre que nous avons défini dans notre énumération.

Mes deux blocs ont donc comme nom non-localisé “tutorial.tutorial_block” et “tutorial.tutorial_block_m1”.
Nous en avons fini pour cette classe, nous pouvons donc passer aux ressources.

Les ressources :

Dans les fichiers de langue, nous n’allons pas écrire la même ligne qu’habituellement.
Normalement, nous devrions écrire “tile.modid.nomobjet.name”. Mais en définissant getUnlocalizedName, nous avons omis le “tile.”. Nous allons donc le retirer dans notre fichier .lang.
Dans mon cas, j’obtiens ça :
fr_FR.lang :

tutorial.tutorial_block.name=Bloc de Tutoriel
tutorial.tutorial_block_m1.name=Bloc de Tutoriel 2

en_US.lang :

tutorial.tutorial_block.name=Tutorial Block
tutorial.tutorial_block_m1.name=Tutorial Block 2

Nous allons maintenant définir les modèles, et textures.

Dans le dossier resources/assets/modid/blockstates, vous devez avoir un fichier tutorial_block.json, avec un nom différent selon le nom de votre bloc.
Ouvrez-le, nous allons le définir comme ceci :

{
    "variants": {
        "variant=tutorial": {
            "model": "tutorial:tutorial_block"
        },
        "variant=tutorial2": {
            "model": "tutorial:tutorial_block_2"
        }
    }
}

“tutorial” et “tutorial2” correspondent au deuxième argument dans l’énumération.
Nous allons donc devoir créer deux fichiers .json, tutorial_block.json et tutorial_block_2.json, dans le dossier resources/assets/modid/models/block

tutorial_block.json :

{
    "parent": "block/cube_all",
    "textures": {
        "all": "tutorial:blocks/tutorial"
    }
}

tutorial_block_2.json

{
    "parent": "block/cube_all",
    "textures": {
        "all": "tutorial:blocks/tutorial2"
    }
}

Vous devrez donc avoir deux textures nommées tutorial.png et tutorial2.png dans le dossier resources/assets/modid/textures/blocks

Nous allons maintenant définir les modèles des items. Rendez-vous dans le dossier resources/assets/modid/models/item :
Dans notre fonction gérant les modèles des items, nous avons décidé que si jamais la texture utilisait une métadonnée différente de 0, elle aurait comme suffixe “_mX” avec X le numéro de métadonnée.

Nous avons donc comme fichiers tutorial_block.json et tutorial_block_m1.json.

tutorial_block.json :

{
    "parent": "tutorial:block/tutorial_block"
}

Ici, nous ne faisons rien d’autre que de dire d’utiliser le modèle du bloc.

Nous allons créer tutorial_block_m1.json de la même manière :

{
    "parent": "tutorial:block/tutorial_block_2"
}

Et voilà ! Vous avez terminé votre bloc avec des métadonnées. Si vous lancez le jeu, vous devriez le retrouver dans l’onglet créatif que vous avez choisi.

Résultat

Voici un exemple de ce que vous pouvez obtenir.

0_1529096619601_bloc metadata.png

Crédits

Rédaction :

Correction :

Autres :

  • BrokenSwing - Pour les ressources, et aussi pour savoir quel bloc placer.
  • robin4002 - Pour savoir quel bloc placer.

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