Dans ce tutoriel nous allons apprendre à créer un bloc avec des métadonnées allant de 0 à 15.
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 |
| { |
| |
| |
| public TutorialBlock(Material material) |
| { |
| |
| } |
| |
| @Override |
| public int damageDropped(IBlockState state) |
| { |
| |
| } |
| |
| @Override |
| public void getSubBlocks(Item itemIn, CreativeTabs tab, NonNullList <ItemStack>list) |
| { |
| |
| } |
| |
| @Override |
| public IBlockState getStateFromMeta(int meta) |
| { |
| |
| } |
| |
| @Override |
| public int getMetaFromState(IBlockState state) |
| { |
| |
| } |
| |
| @Override |
| protected BlockStateContainer createBlockState() |
| { |
| |
| } |
| |
| public static enum EnumType implements IStringSerializable |
| { |
| |
| ; |
| |
| |
| |
| private EnumType(int metaIn, String nameIn, String unlocalizedIn) |
| { |
| |
| } |
| |
| public static String[] getUnlocalizedNames() |
| { |
| |
| } |
| |
| public int getMetadata() |
| { |
| |
| } |
| |
| public static TutorialBlock.EnumType byMetadata(int meta) |
| { |
| |
| } |
| |
| public String toString() |
| { |
| |
| } |
| |
| @Override |
| public String getName() |
| { |
| |
| } |
| } |
| } |
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.
Voici un exemple de ce que vous pouvez obtenir.

Rédaction :
Correction :
Autres :
- BrokenSwing - Pour les ressources, et aussi pour savoir quel bloc placer.
- robin4002 - Pour savoir quel bloc placer.

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
Retour vers le sommaire des tutoriels