Les fichiers de texture et modèles JSON


  • Rédacteurs

    Sommaire

    Introduction

    Ce tutoriel a pour but d'expliquer le fonctionnement des fichiers JSON utilisés pour les modèles des blocs et des objets.

    Pré-requis

    Code

    Les états de bloc

    Pour les blocs, 2 fichiers JSON sont nécessaires, le premier se trouve dans la package assets.modid.blockstates où bien entendu
    il faut remplacer modid par l'ID de votre mod.
    Ce fichier JSON aura pour fonction d'indiquer quel modèle utiliser pour rendre le bloc, selon son état. On peut le voir comme un aiguillage.
    Nous allons donc créer ce fichier, il devra avoir pour nom, le nom sous lequel il est enregistré.
    Pour ce bloc ci :

    public static final Block blockSimpleTexture = new Block(Material.ROCK).setUnlocalizedName("blockSimpleTexture").setRegistryName("blockSimpleTexture");
    

    Le nom du fichier JSON sera celui mis en paramètre de setRegistryName(String name), donc "blockSimpleTexture".
    Nous créons donc le fichier, si on prend comme exemple le bloc déclaré ci-dessus on obtient blockSimpleTexture.json se trouvant dans le package assets.modid.blockstates.
    Pour commencer le fichier JSON doit ressembler à cela (structure de base d'un fichier JSON) :

    {
    
    }
    

    Nous allons ajouter un tag variants qui contiendra toute les variantes de notre bloc

    {
        "variants": {
    
        }
    }
    

    Viennent ensuite le nom des variantes de votre bloc, si votre bloc n'a qu'une seule variante, alors son nom est normal, ce qui donne :

    {
        "variants": {
            "normal": {
    
            }
        }
    }
    

    Dans la variantes nous pouvons ensuite indiquer quel model utiliser pour rendre notre bloc, ce model est un fichier JSON qui se trouvera dans le
    package assets.modid.models.block et qui aura pour nom celui que vous allez indiquer.

    {
        "variants": {
            "normal": {
                "model": "modid:nomDuModel"
            }
        }
    }
    

    Il faut bien sûr remplacer modid et nomDuModel. Ici nous avons indiquer le modèle à utiliser, des tags supplémentaires peuvent venir s'ajouter.
    Grâce au tag x nous pouvons effectuer une rotation du modèle sur l'axe x et grâce au tag y nous pouvons effectuer une rotation sur l'axe y.
    Attention : le tag z, n'existe pas.
    Ces tags de rotation doivent s'incrémenter de 90 en 90. Exemple : 0, 90, 180, 270. Si vous utilisez les tags x ou y vous pouvez aussi utiliser
    le tag uvlock qui permet que la texture ne tourne pas avec le modèle, il peut prendre les valeurs true ou false.
    Voici une exemple non concret avec tout les tags :

    {
        "variants": {
            "normal": {
                "model": "modid:nomDuModel",
                "x": 90,
                "y": 270,
                "uvlock": true
            }
        }
    }
    

    Nous pouvons aussi associer plusieurs modèles à une seule variantes de la façon suivante :

    {
        "variants": {
            "normal": [{
                "model": "modid:nomDuModelUn",
                "x": 90,
                "weight": 2
            }, {
                "model": "modid:nomDuModelDeux",
                "y": 180,
                "uvlock": true,
                "weight": 1
            }]
        }
    }
    

    Vous pouvez voir ici 2 choses, premièrement chaque modèles peut avoir des tags x, y et uvlock différents et deuxièmement que l'on peut ajouter un
    tag weight (facultatif) si nous avons plusieurs modèles afin de déterminer, la probabilité d'être choisi, de chaque modèle. Si vous ne mettez pas ce tag, tous les modèles
    auront la même chance d'être utilisé (tous auront pour valeur 1).

    Il existe une autre façon d'indiquer quels modèles utiliser en utilisant le tag multipart à la place du tag variants, sont utilisation permet
    d'afficher plusieurs modèles en même temps. Un bon exemple de son utilisation est la barrière, grâce au multipart, Minecraft affiche le pilier et si d'autres
    blocs sont situés à côté de la barrière alors il affiche les jointures.
    Premièrement il faut mettre le tag multipart :

    {
        "multipart": [
    
        ]
    }
    

    Il contient un tableau de cas, un cas est défini par une condition (facultative) et un/des modèle(s) affiché(s) si la condition est vraie (si il n'y a pas de condition
    le jeu considère qu'elle vaut vraie). Un cas est défini entre accolades et possède donc le tag when qui est facultatif et le tag apply qui est obligatoire.
    On peut traduire cela par : Quand conditions appliquer modèle(s)
    Ajoutons un cas à notre fichier :

    {
        "multipart": [{
            "when": {
    
            },
            "apply": {
    
            }
        }]
    }
    

    Dans le tag when on peut trouver soit le tag OR, soit des conditions. Commençons par les conditions :
    Imaginons que j'ai une propriété nommée ready est une autre nommée dead, je veux appliquer le modèle modid:modelReady si ready vaut "true".

    {
        "multipart": [{
            "when": {
                "ready": "true"
            },
            "apply": {
                "model": "modid:modelReady"
            }
        }]
    }
    

    Ici nous n'avons qu'une condition mais nous pouvons en ajouter d'autres, si je veux par exemple que le modèle soit afficher seulement si dead vaut "false" et que ready vaut "true" alors:

    {
        "multipart": [{
            "when": {
                "ready": "true",
                "dead": "false"
            },
            "apply": {
                "model": "modid:modelReady"
            }
        }]
    }
    

    Vous pouvez voir qu'on peut ajouter pleins de conditions à la suite, il faudra que toutes les conditions soient vraies pour que le modèle soit appliqué.
    Je tient à préciser que l'on peut spécifier plusieurs modèles dans le tag apply de la même façon qu'énoncé au début :

    {
        "multipart": [{
            "when": {
                "ready": "true",
                "dead": "false"
            },
            "apply": [{
                "model": "modid:modelReadyUn",
                "x": 90,
                "weight": 2
            }, {
                "model": "modid:modelReadyDeux",
                "y": 180,
                "weight": 1
            }]
        }]
    }
    

    Pour ce qui est du OR il faut l'utiliser de cette façon :

    {
        "multipart": [{
            "when": {
                "OR": [{
                    "ready": "false"
                }, {
                    "dead": "true"
                }]
            },
            "apply": {
                "model": "modid:modelNotReadyOrDead"
            }
        }]
    }
    

    Le tag OR est une liste de cas dans lequels il faut appliquer le ou les modèle(s). Un cas se met un accolades et les conditions qui le compose sont
    sous la forme "propriété" : "valeur", on peut aussi séparer les valeurs avec le symbole | ainsi :

    "orientation": "top|bottom"
    

    Signifie "Si orientation vaut top ou bottom" ou en code if(orientation == "top" || orientation == "bottom").
    Un dernier pour bien comprendre la logique :

    {
        "multipart": [{
            "when": {
                "OR": [{
                    "ready": "false",
                    "orientation": "south|north"
                }, {
                    "dead": "true"
                }]
            },
            "apply": {
                "model": "modid:modelNotReadyOrDead"
            }
        }]
    }
    

    Est équivalent en terme de code à :

    if( (ready == "false" && (orientation == "south" || orientation == "north") ) || dead == "true") {
        apply();
    }
    

    Voilà pour le fichier JSON situé dans le package assets.modid.blockstates

    Les modèles de bloc

    Ce fichier JSON se trouve dans le package assets.modid.models.block, son nom doit correspondre avec celui donné dans le fichier d'état de bloc.
    C'est dans ce fichier que nous allons vraiment définir ce que nous devons afficher. Nous allons commencer avec le tag ambientocclusion, il peut prendre
    comme valeur true ou false, il sert à définir si l'occlusion ambiente doit être utilisé.

    {
        "ambientocclusion": true
    }
    

    Nous allons à présent nous intéresser au tag textures, c'est ici que nous allons déclarer nos textures. Une texture est déclarée par son nom associé au
    chemin de l'image. Si je veux définir une texture qui s'appelerai jambon alors j'aurai le code suivant :

    {
        "ambientocclusion": true,
        "textures": {
            "jambon": "modid:blocks/jambon"
        }
    }
    

    Le chemin de l'image commence à partir du package assets.modid.textures, vous pouvez mettre ce que vous voulez pour le nom de la texture, cependant
    un nom est réservé, c'est particle qui défini la texture pour les particules, donc pour dire que vous voulez des particules de jambon :

    {
        "textures": {
            "particle": "modid:blocks/jambon"
        }
    }
    

    Si vous ne mettez pas votre modid dans le chemin de la texture, l'image sera cherchée dans les fichiers de Minecraft, il est donc tout à fait possible
    d'utiliser une texture de Minecraft.

    Maintenant voyons le tag elements, c'est celui qui définie les cubes à afficher. Ce tag est un tableau :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [
    
        ]
    }
    

    C'est dans ce tableau que les différents cubes vont être définis. Un cube possède deux coordonnées, le début et la fin du cube. Une coordonnée est un tableau
    de 3 nombres pouvant aller de -16 à 32, il a pour forme [x, y, z], il faut savoir qu'un cube de minecraft basique commence en [0, 0, 0] et fini en [16, 16, 16].
    Vous pouvez ainsi rendre un cube sur un volume de 3x3x3 blocs.
    Nous allons définir nos coordonnées pour obtenir un bloc plus petit :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10]
        }]
    }
    

    Comme vous pouvez voir on utilise les tags from et to pour définir les coordonnées. Ici pour obtenons un cube 9 fois plus petit qu'un cube
    normal, et il est centré.
    Nous devons ensuite indiquer quelle texture utiliser pour chaque face du cube. Pour cela nous allons utiliser le tag faces :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "faces": {
                "down": {
                    "texture": "#stone"
                },
                "up": {
                    "texture": "#stone"
                },
                "north": {
                    "texture": "#stone"
                },
                "south": {
                    "texture": "#stone"
                },
                "west": {
                    "texture": "#stone"
                },
                "east": {
                    "texture": "#stone"
                }
            }
        }]
    }
    

    Ceci est le code minimal pour un cube, on utilise le tag texture pour indiquer quelle texture utiliser, on s'aperçoit qu'il faut mettre un # devant
    le nom de la texture que nous avons défini dans le tag textures.
    Si vous supprimez un tag de face, par exemple down, alors cette face ne sera plus rendue, à faire si la face ne devra en aucun cas être rendue.
    Il y a plusieurs autres tags facultatifs pour le tag faces, le tag uv qui sert à indiquer quel morceau de la texture utiliser pour la face,
    les cordonnées vont [0, 0] à [16, 16], si la position de la texture n'est pas indiqué alors sera basée sur la position du cube (de l'élément).
    Si je veux que mon petit cube ressemble à un petit bloc de stone :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }]
    }
    

    Un autre tag optionnel est cullface qui indique si la face ne doit pas être rendu si un bloc touche la face indiqué, par exemple avec le code suivant
    si un bloc touche la face du bas, la face du haut ne sera plus rendue.

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16],
                    "cullface": "down"
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }]
    }
    

    Le tag rotation permet de faire tourner la texture, la valeur doit être 0, 90, 180 ou 270. Dans le code suivant, la texture effectue on rotation de
    90° sur la face du haut :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16],
                    "rotation": 90
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }]
    }
    

    Il existe une autre tag qui s'appelle tintindex mais qui ne sert que pour quelques blocs, tels que la grass, les leaves, etc ... Pour les autres
    il n'a aucun effet, il se place au même endroit que rotation ou cullface. Voilà c'est tout pour le tag faces.

    D'autres tags existent pour les éléments, on a déjà from, to et faces. On peut ajouter le tag shade pour indiquer si le bloc doit
    rendre une ombre, si il n'est spécifié alors la valeur true lui sera associée.
    Maintenant intéressons-nous au tag rotation :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "rotation": {
    
            },
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }]
    }
    

    Dans ce tag rotation on va indiquer la rotation de l'élément, il faut tout d'abord annoncé le centre de rotation avec le tag origin, puis
    indiquer l'axe de rotation avec le tag axis, viens ensuite l'angle avec angle qui accepte les valeurs -45/-22.5/0/22.5/45 et enfin le
    tag rescale (facultatif, sur false par défaut) qui indique si il faut redimensionner le cube à l'échelle après sa rotation, c'est à dire
    qu'il aura la même distance avec les bords du cube qu'avant sa rotation (déforme le cube).
    Ici nous faisons tourner notre cube de 45 sur l'axe y avec une remise à l'échelle :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "rotation": {
                "origin": [8, 8, 8],
                "axis": "y",
                "angle": 45,
                "rescale": true
            },
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }]
    }
    

    C'est tout pour les éléments, maintenant je vais vour parler du tag display qui va définir la façon de rendre le bloc quand il n'est pas posé
    (dans le gui, dans les item frames, au sol, dans la main (première et troisième personne), sur le tête).
    Tout d'abord le il faut ajouter le tag display :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "rotation": {
                "origin": [8, 8, 8],
                "axis": "y",
                "angle": 45,
                "rescale": true
            },
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }],
        "display": {
    
        }
    }
    

    Dans ce tag, nous allons définir la dimension, la rotation, et la translation de l'objet pour les tags suivants :
    thirdperson_righthand, thirdperson_lefthand, firstperson_righthand, firstperson_lefthand, gui, head, ground, fixed
    Vous pouvez définir seulement le tag de la main droite, et la main gauche appliquera les changements en miroir.
    Attention : Les changements à la première personne n'influent pas sur la troisième personne.
    Les tags à mettre sont rotation, translation, scale.
    Je fait fait une exemple avec des valeurs aléatoires pour montrer où placer le code :

    {
        "textures": {
            "stone": "blocks/stone",
            "particle": "blocks/stone"
        },
        "elements": [{
            "from": [6, 6, 6],
            "to": [10, 10, 10],
            "rotation": {
                "origin": [8, 8, 8],
                "axis": "y",
                "angle": 45,
                "rescale": true
            },
            "faces": {
                "down": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "up": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "north": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "south": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "west": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                },
                "east": {
                    "texture": "#stone",
                    "uv": [0, 0, 16, 16]
                }
            }
        }],
        "display": {
            "gui": {
                "rotation": [-5, 45, 10],
                "translation": [2, -2, 8],
                "scale": [1.1, 1.2, 1.1]
            },
            "fixe": {
                "rotation": [-5, 45, 10],
                "translation": [2, -2, 8],
                "scale": [0.9, 1.0, 0.9]
            }
        }
    }
    

    Maintetant je vais parler du tag parent, il indique quel modèle utiliser, par exemple si plusieurs blocs ont les mêmes éléments mais pas les mêmes
    textures, on va créer un fichier dans lequel tout les éléments seront définis puis pour chaque bloc ont définira ce bloc comme parent et on écrira le tag
    textures. Ce système correspond à l'héritage, si vous ré-écrivait un tag, il va override celui du parent.
    Ce tag est très utilisé pour faire différents blocs, par exemple le fichier blocks/cube_all défini un bloc de 16x16x16 avec la même texture sur toute
    les faces qui s'appelle all, donc pour faire ce type de bloc il faudra ce code :

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

    Voici à quoi ressemble le fichier cube_all.json :

    {
        "parent": "block/cube",
        "textures": {
            "particle": "#all",
            "down": "#all",
            "up": "#all",
            "north": "#all",
            "east": "#all",
            "south": "#all",
            "west": "#all"
        }
    }
    

    Et le fichier cube.json

    {
        "parent": "block/block",
        "elements": [
            {   "from": [ 0, 0, 0 ],
                "to": [ 16, 16, 16 ],
                "faces": {
                    "down":  { "texture": "#down", "cullface": "down" },
                    "up":    { "texture": "#up", "cullface": "up" },
                    "north": { "texture": "#north", "cullface": "north" },
                    "south": { "texture": "#south", "cullface": "south" },
                    "west":  { "texture": "#west", "cullface": "west" },
                    "east":  { "texture": "#east", "cullface": "east" }
                }
            }
        ]
    }
    

    Et enfin le fichier block.json

    {
        "display": {
            "gui": {
                "rotation": [ 30, 225, 0 ],
                "translation": [ 0, 0, 0],
                "scale":[ 0.625, 0.625, 0.625 ]
            },
            "ground": {
                "rotation": [ 0, 0, 0 ],
                "translation": [ 0, 3, 0],
                "scale":[ 0.25, 0.25, 0.25 ]
            },
            "fixed": {
                "rotation": [ 0, 0, 0 ],
                "translation": [ 0, 0, 0],
                "scale":[ 0.5, 0.5, 0.5 ]
            },
            "thirdperson_righthand": {
                "rotation": [ 75, 45, 0 ],
                "translation": [ 0, 2.5, 0],
                "scale": [ 0.375, 0.375, 0.375 ]
            },
            "firstperson_righthand": {
                "rotation": [ 0, 45, 0 ],
                "translation": [ 0, 0, 0 ],
                "scale": [ 0.40, 0.40, 0.40 ]
            },
            "firstperson_lefthand": {
                "rotation": [ 0, 225, 0 ],
                "translation": [ 0, 0, 0 ],
                "scale": [ 0.40, 0.40, 0.40 ]
            }
        }
    }
    

    Comme vous le voyez il y a une hiérarchie qui se construit. Il y a d'autres fichiers qui héritent de cube.json, nous avons les suivants :

    • cube_bottom_top : Il faut définir les textures side, top, bottom
    • cube_column : Il faut définir les textures side, end, cette dernière est la textures en haut et en bas du bloc
    • cube_top : Il faut définir les textures side, top
      D'autres fichiers JSON peuvent vous être utiles :
    • flower_pot_cross : Il faut définir la texture plant, permet d'avoir une fleur dans un pot.
    • torch et torch_wall : Il faut défnir la texture torch
    • trapdoor_bottom, trapdoor_top et trapdoor_open : Pour chacun il faut définir la texture texture
    • cross : Il faut défnir la texture cross, permet de faire des plantes
    • crop : Il faut définir la texture crop, permet de faire des plantations
    • carpet : Il faut définir la texture wool et particule
    • button, button_pressed, button_inventory : Il faut définir la texture texture
    • anvil : Il faut définir les textures particle, body, top
    • fence_inventory, fence_post, fence_side : Il faut définir la texture texture
    • fence_gate_closed, fence_gate_open : Il faut définir la texture texture
      Cette liste n'est pas exhaustive.

    Les modèles des objets

    Les modèles des objets ressemblent beaucoup à ceux des blocs. Je vais tout d'abord tout d'abord parler du nom du fichier JSON et de où le placer.
    Ce fichier doit se trouver dans le package assets.modid.models.item et doit avoir pour nom celui donné lors de l'enregistrement de l'objet dans le jeu et
    le l'enregistrement du modèle.

    Nous allons commencer par parler du tag parent cette fois-ci, si vous voulez juste afficher une texture comme c'est le cas pour beaucoup d'objets dans
    Minecraft (redstone, nourriture, lit, etc ...) il vous faut alors utiliser le fichier generated avec comme texture layer0 :

    {
        "parent": "item/generated",
        "textures": {
            "layer0": "modid:items/yourTexture"
        }
    }
    

    Des modèles comme handheld et handheld_rod s'occupent de positionner l'objet dans votre main, il vous suffit de les défnir comme parent.

    Si vous voulez que votre objet soit un bloc vous pouvez utiliser comme parent le modèle de votre bloc, et oui, comme je l'ai dit ils se ressemblent. En fait
    les modèles des objets ont plus de tags disponibles que ceux des blocs, mais sinon ils possèdent les mêmes tags vous pouvez donc appliquer tout ce que j'ai
    dit précédemment pour les modèles des blocs avec les modèles des objets.
    Exemple d'utilisation du modèle d'un bloc comme parent pour un modèle d'objet :

    {
        "parent": "modid:block/modeleDeVotreBloc"
    }
    

    Ici aussi si vous re-définissez un tag, alors ce dernier override celui du parent. Je ne vais expliquer que les nouveaux tags, les autres n'ont pas changés.
    Un nouveau tag est overrides, il permet de diriger vers un autre modèle (il aiguille comme le fichier d'état de bloc) en fonction des propriétés de l'objet
    (par exemple : la canne à pêche possède la propriété cast, l'arc possède les propriétés pull et pulling).

    Ces propriétés sont ajoutées dans le constructeur de l'objet avec la fonction Item#addPropertyOverride(ResourceLocation key, IItemPropertyGetter getter)
    L'interface IItemPropertyGetter implémente la fonction public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn)
    Comme on peut le voir cette fonction renvoie un float en fonction des paramètres, c'est cette valeur qu'il faudra tester dans le JSON.
    Par exemple si nous avons cela dans le constructeur :

    this.addPropertyOverride(new ResourceLocation("held"), new IItemPropertyGetter()
        {
            @SideOnly(Side.CLIENT)
            public float apply(ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity)
            {
                return entity == null ? 0.0F : 1.0F;
            }
        });
    

    Cette propriété renvoie 0 si l'item n'est pas dans l'inventaire du joueur et 1 si il y est, ainsi avec le code suivant :

    {
        "parent": "block/acacia_log",
        "overrides": [{
            "predicate": {
                "held": 1
            },
            "model": "block/stone"
        }]
    }
    

    Quand l'objet est au sol c'est du bois et quand le joueur le ramasse c'est de la pierre (seulement côté visuel). Voilà comment le code se présente :
    Le tag overrides qui est un tableau de cas, chaque cas est entouré d'accolades, dans les accolades le tag predicate contient les conditions qui
    sont de la forme "key": floatValue et enfin au même niveau que le tag predicate, on met le tag model qui contient le model vers lequel il
    faut se rendre.

    Bravo, vous savez à présent tout ce qu'il faut savoir sur les fichiers JSON de Minecraft. Vous ne direz plus "Oh, non la 1.8 et les jsons"

    Bonus

    Créer une barrière

    Nous allons voir comment créer une nouvelle barrière, ici je vais créer une barrière en pierre. Je vais tout simplement appliquer la texture
    de la pierre au modèle de la barrière.

    Première on va déclarer notre bloc :

    public static final Block stoneFence = new BlockFence(Material.ROCK, MapColor.STONE).setUnlocalizedName("stone_fence").setRegistryName("stone_fence");
    

    Ici notre bloc est une barrière donc nous utilisons la classe BlockFence puis nous l'enregistrons :

    GameRegistry.<Block>register(stoneFence);
    

    Nous allons aussi déclarer notre ItemBlock car rappelons-le ils ne sont plus créés automatiquement en 1.9 :

    public static final Item itemBlockStoneFence = new ItemBlock(BlocksModTutoriel.stoneFence).setUnlocalizedName("stone_fence").setRegistryName("stone_fence");
    

    Et enregistrons-le :

    GameRegistry.register(itemBlockStoneFence);
    

    N'oubliez pas d'enregistrer son rendu, pour ma part j'ai une fonction ClientProxy#registerItemTexture :

    registerItemTexture(itemBlockStoneFence, "stone_fence");
    

    Voici ma fonction :

    public void registerItemTexture(Item item, int metadata, String name)
    {
        ItemModelMesher mesher = Minecraft.getMinecraft().getRenderItem().getItemModelMesher();
        mesher.register(item, metadata, new ModelResourceLocation(ModTutoriel.MODID + ":" + name, "inventory"));
    }
    

    Venons-en au centre du tutoriel, les fichiers JSON, commençons par le fichier d'état de bloc :
    assets/modid/blockstates/stone_fence.json

    {
        "multipart": [{
            "apply": {
                "model": "modid:stone_fence_post"
            }
        }, {
            "when": {
                "north": "true"
            },
            "apply": {
                "model": "modid:stone_fence_side",
                "uvlock": true
            }
        }, {
            "when": {
                "east": "true"
            },
            "apply": {
                "model": "modid:stone_fence_side",
                "y": 90,
                "uvlock": true
            }
        }, {
            "when": {
                "south": "true"
            },
            "apply": {
                "model": "modid:stone_fence_side",
                "y": 180,
                "uvlock": true
            }
        }, {
            "when": {
                "west": "true"
            },
            "apply": {
                "model": "modid:stone_fence_side",
                "y": 270,
                "uvlock": true
            }
        }]
    }
    

    Comme vous pouvez le voir on utilise le multipart, il faut savoir que les propriétés north, west, south, east valent "true" si la barrière peut se lier
    au bloc qui se trouve dans cette direction. La barrière se compose de deux modèles, le pilier central et la jointure à laquelle on appliquer une rotation suivant
    la direction dans laquelle elle doit pointer. La pilier est toujours affiché, d'où l'absence de tag when pour ce dernier. Ensuite pour chaque côté on
    regarde si il y a un bloc auquel il faut lier et si c'est le cas on affiche le modèle de la jointure avec la rotation appropriée.

    On maintenant créer les modèles stone_fence_post et stone_fence_side :
    assets/modid/models/block/stone_fence_post.json

    {
        "parent": "block/fence_post",
        "textures": {
            "texture": "blocks/stone"
        }
    }
    

    assets/modid/models/block/stone_fence_side.json

    {
        "parent": "block/fence_side",
        "textures": {
            "texture": "blocks/stone"
        }
    }
    

    Grâce au système d'héritage nous n'avons pas besoin de définir les éléments composants notre barrière, il faut seulement dire quelle texture utiliser.
    Pour le modèle l'objet de la barrière on va utiliser le fichier block/fence_inventory auquel nous allons appliquer la texture de la pierre.
    assets/modid/models/item/stone_fence.json

    {
        "parent": "block/fence_inventory",
        "textures": {
            "texture": "blocks/stone"
        }
    }
    

    Voilà, pour pouvez maintenant utiliser une barrière toute belle, toute neuve et surtout solide.

    Créer un modèle pour les tuyaux/câbles de vos mods

    Pour créer un câble nous allons procéder de la même manière que Minecraft lorsqu'il relie les barrière ensembles, cepedant nous allons rajouter les
    directions haut et bas, en plus de nord, sud, est, ouest.

    Nous allons commencer par créer la classe du bloc, elle héritera de classe Block. On va y ajouter 6 PropertyBool qui correspondront aux 6
    faces du bloc, si la propriété vaut vrai, alors c'est qu'il est relié au bloc dans cette direction.
    blocks/BlockPipe.java

    public class BlockPipe extends Block {
    
        public static final PropertyBool NORTH = PropertyBool.create("north");
        public static final PropertyBool EAST = PropertyBool.create("east");
        public static final PropertyBool SOUTH = PropertyBool.create("south");
        public static final PropertyBool WEST = PropertyBool.create("west");
        public static final PropertyBool DOWN = PropertyBool.create("down");
        public static final PropertyBool UP = PropertyBool.create("up");
    
        public BlockPipe(Material material)
        {
            super(material);
            this.setDefaultState(this.blockState.getBaseState()
            .withProperty(NORTH, false)
            .withProperty(EAST, false)
            .withProperty(SOUTH, false)
            .withProperty(WEST, false)
            .withProperty(DOWN, false)
            .withProperty(UP, false)
            );
        }
    }
    

    Comme vous pouvez le voir, on a créé un propriété par face puis dans le constructeur du bloc nous avons définit l'état du bloc par défaut, c'est à dire
    qu'il n'est relié à aucun bloc par défaut. Notre bloc ne sera pas un bloc plein, donc il faut le spécifier.
    blocks/BlockPipe.java

    public boolean isFullCube(IBlockState state)
    {
        return false;
    }
    
    public boolean isPassable(IBlockAccess worldIn, BlockPos pos)
    {
        return false;
    }
    
    @SideOnly(Side.CLIENT)
    public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side)
    {
        return true;
    }
    

    Comme nous avons créer des propriétés nous devons dire à Minecraft que le bloc les utilise et comment convertir les états en métadata sinon on aura un crash. Dans notre cas, le métadata ne dépendra pas des propriétés.
    blocks/BlockPipe.java

    public int getMetaFromState(IBlockState state)
    {
        return 0;
    }
    
    protected BlockStateContainer createBlockState()
    {
        return new BlockStateContainer(this, new IProperty[] {NORTH, EAST, WEST, SOUTH, DOWN, UP});
    }
    

    Ensuite il va falloir créer un fonction pour savoir si notre bloc doit se relier sur tel, ou tel côté.
    blocks/BlockPipe.java

    public boolean canConnectTo(IBlockAccess worldIn, BlockPos pos)
    {
        return worldIn.getBlockState(pos).getBlock() instanceof BlockPipe;
    }
    

    Ici je vérifie si le bloc à la position donnée est un tuyau, j'ai donc instanceof BlockPipe, si vous voulez que le tuyau se relie à d'autres blocs
    vous pouvez simplement rajouter des conditions sépararée par des OU. Si vous avez un grand nombre de blocs auquels votre tuyau peut se relier vous pouvez
    créer une interface que vous implémenterez à ces blocs et votre fonction ressemblera à ça :

    public boolean canConnectTo(IBlockAccess worldIn, BlockPos pos)
    {
        return worldIn.getBlockState(pos).getBlock() instanceof VotreInterface;
    }
    

    Et donc pour finir la classe du bloc on va changer la fonction getActualState pour changer les propriétés en fonction du retour de la fonction
    prédédemment créée.
    blocks/BlockPipe.java

    public IBlockState getActualState(IBlockState state, IBlockAccess worldIn, BlockPos pos)
    {
        return state.withProperty(NORTH, this.canConnectTo(worldIn, pos.north()))
        .withProperty(EAST, this.canConnectTo(worldIn, pos.east()))
        .withProperty(SOUTH, this.canConnectTo(worldIn, pos.south()))
        .withProperty(WEST, this.canConnectTo(worldIn, pos.west()))
        .withProperty(DOWN, this.canConnectTo(worldIn, pos.down()))
        .withProperty(UP,this.canConnectTo(worldIn, pos.up()));
    }
    

    C'est tout pour le bloc. Pensez bien à le déclarer :

    public static final Block blockPipe = new BlockPipe(Material.ROCK).setUnlocalizedName("blockPipe").setRegistryName("blockPipe");
    

    Et à l'enregistrer :

    GameRegistry.<block>register(blockPipe);
    

    Passons à présent aux JSONs, commençons par le JSON d'état du bloc, le bloc votre être composé de deux modèles, le centre qui sera rendu quoi qu'il arrive,
    et le bord que l'on rendra autant de fois qu'il faut et auquel on appliquera une rotation.
    assets/modid/blockstates/blockPipe.json

    {
        "multipart": [
            {   "apply": { "model": "modid:blockPipe_center" }},
            {   "when": { "north": "true" },
                "apply": { "model": "modid:blockPipe_side", "uvlock": true }
            },
            {   "when": { "east": "true" },
                "apply": { "model": "modid:blockPipe_side", "y": 90, "uvlock": true }
            },
            {   "when": { "south": "true" },
                "apply": { "model": "modid:blockPipe_side", "y": 180, "uvlock": true }
            },
            {   "when": { "west": "true" },
                "apply": { "model": "modid:blockPipe_side", "y": 270, "uvlock": true }
            },
            {   "when": { "down": "true" },
                "apply": { "model": "modid:blockPipe_side", "x": 90, "uvlock": true }
            },
            {   "when": { "up": "true" },
                "apply": { "model": "modid:blockPipe_side", "x": 270, "uvlock": true }
            }
        ]
    }
    

    Comme vous pouvez le voir, on a le modèle blockPipe_center qui sera rendu quoi qu'il arrive. Puis suivant l'état des propriétés on rend le
    modèle blockPipe_side avec un rotation.

    Ces modèles sont les suivants, premièrement le centre, qui est un bloc plus petit.
    assets/modid/models/block/blockPipe_center.json

    {
        "textures": {
            "texture": "modid:blocks/basicTexture",
            "particle": "modid:blocks/basicTexture"
        },
        "elements": [
            {   "from": [ 6, 6, 6 ],
                "to": [ 10, 10, 10 ],
                "faces": {
                    "down":  { "texture": "#texture" },
                    "up":    { "texture": "#texture" },
                    "north": { "texture": "#texture" },
                    "south": { "texture": "#texture" },
                    "west":  { "texture": "#texture" },
                    "east":  { "texture": "#texture" }
                }
            }
        ]
    }
    

    Et deuxièmement le modèle qui relie deux blocs, c'est un petit pavé situé sur le côté du bloc.
    assets/modid/models/block/blockPipe_side.json

    {
        "textures": {
            "texture": "modid:blocks/basicTexture",
            "particle": "modid:blocks/basicTexture"
        },
        "elements": [
            {   "from": [ 6, 6, 0 ],
                "to": [ 10, 10, 6 ],
                "faces": {
                    "down":  { "texture": "#texture" },
                    "up":    { "texture": "#texture" },
                    "north": { "texture": "#texture" },
                    "south": { "texture": "#texture" },
                    "west":  { "texture": "#texture" },
                    "east":  { "texture": "#texture" }
                }
            }
        ]
    }
    

    Pour faire l'ItemBlock, c'est comme pour un bloc normal, je ne vais pas le refaire ici.

    Voici le rendu :

    Résultat

    Bah je ne sais pas encore quoi mettre ici, je ne peux pas tout illustrer, les possibilités sont énormes. Dites-moi ce qui conviendrai le mieux.

    Voir le commit sur GitHub



  • Très bon tuto bien rédigé (juste une ou deux fautes ^^) et bien expliqué qui va m'être utile dès que je serais passé de 1.8.9 à 1.9.4 🙂


  • Rédacteurs

    Ce que j'aime bien c'est qu'en faisant le tutoriel j'ai appris des trucs, par exemple le tag overrides est super utile



  • Moi qui est nul en Json je vais me faire plaisir ^^



  • Bonjour,
    J n'arrive pas a obtenir une texture sur mon bloc malgré qu'il est le même nom que celui du GameRegistery et qu'il soit en miniscule.
    Je vais joindre mes classes et mon fichier json (mon image se nomme ore_copper.png)

    Merci d'avance de votre aide !



  • Bonsoir,

    Voilà j'ai un petit souci : Les tag "uv" et "cullface" ne sont pas reconnu :s

    Je suis en 1.12.2 et c'est peut être ça la cause.
    Si c'est le cas, pourriez vous me diriger vers un tuto 1.12 la dessus, une doc forge ou autre. Je n'ai rien trouvé dessus 😞

    Voilà ce qu'ils me disent, "The word 'uv' is not correctly spelled" :

    Merci d'avance de votre aide 🙂


  • Administrateurs

    Bonsoir,
    Ça n'a rien à voir, c'est le correcteur orthographique qui indique ça.
    Ignores cet avertissement.



  • Sur le moment ça ne marchait pas avec et ça marchait sans, du coup j'ai douté. Mais maintenant c'est bon. J'avais dû faire une autre connerie.

    Merci pour ta réponse 🙂