Créer un gâteau



  • Introduction

    Salut ! Bon, si t'a cliquer ici c'est que t'a déjà vu ce que j'allait expliquer dans ce tuto … comment faire un gâteau ! Si vous avez besoin d'aide, j'essaierais de répondre à vos problèmes/besoin d'explication le plus souvent possible, c'est mon premier tutoriel, donc je compte le tenir un minimum.

    Je ne sais pas si ce tutoriel marche en 1.5.2; si non, je mettrais des spoilers pour les plus anciens codes.

    Pré-requis

    • Les bases du mod, bon c'est normal ça ^_^

    • Déclarer un bloc, lui mettre ses réglages ( uniquement Hardness, Resistance, et son nom non localizé, pas la texture ) et l'enregistrer auprès de forge ( si vous ne savez pas comment faire, allez voir ce tutoriel sur les blocs basiques )

    • Déclarer un item, lui mettre ses réglages et l'enregistrer auprès de forge ( si vous ne savez pas comment faire, allez voir ce tutoriel sur les items basiques )

    By the way, une minimum connaissance est requise, histoire que votre mod ne sois pas uniquement du bête copier/coller de ce tutoriel et que vous ayez compris ce code, ma foi, plutôt long.

    La classe principale

    Déjà, comme tout blocs, on commence par déclarer celui-ci ;

        public static Block BlockTutoGateau;
    

    Puis on définie ces réglages ;

        BlockTutoGateau = new BlockTutoGateau(1407).setHardness(0.5F).setStepSound(Block.soundClothFootstep).setUnlocalizedName("BlockTutoGateau");
    

    Et enfin on l'enregistre ;

        GameRegistry.registerBlock(BlockTutoGateau, "BlockTutoGateau");
    

    Enfin bref, ça, c'est les bases, allez voir le tutoriel cité plus haut pour commencer, si vous débarquez seulement.

    La classe du bloc

    Cette partie est la plus longue, c'est dans cette classe qu'on définie;

    • la forme du gâteau
    • combien de part fait-il
    • sa texture
    • son matériau, sa place dans le menu créatif, ect

    Alors, pour commencer, il vas, bien-sûr en toutes évidence, étendre la classe Block puis l'importer ;

    package MagicalApple.common;
    
    import net.minecraft.block.Block;
    
    public class BlockTutoGateau extends Block
    {
    
    }
    

    Maintenant, on a une erreur sur BlockTutoGateau, on vas dessus et on crée le constructeur ;

        public BlockTutoGateau(int par1, Material par2Material)
        {
            super(par1, par2Material);
            // TODO Auto-generated constructor stub
        }
    

    Que l'on transforme en ( et on en profite pour ajouter deux fonctions ) ;

        public BlockTutoGateau(int id)
        {
            super(id, Material.cake);
            this.setTickRandomly(true);
            this.setCreativeTab(ModMagicalApple.MAMCreativeTabs);
        }
    

    Material.cake vas nous permettre de charger quelques propriétés, et bien-sûr on crée un gâteau, donc on met ceci.

    this.setCreativeTab(VotreMainClass.VotreCreativeTab); permet de mettre votre bloc dans un onglet créatif, dans mon cas j'utilise le mien (vous pouvez trouver un tuto pour crée des onglets créatifs ici)

    Si vous voulez utiliser un onglet créatif de Minecraft :

        this.setCreativeTab(CreativeTabs.ongletAuChoix);
    

    Bon, on passe à quelque chose de plus sérieux : Les "block bounds" , ils vont nous permettre de donner cette forme si particulière à notre gâteau … Je vous demanderais de faire très attention à cette partie, c'est quand même la "base" du gâteau.

    Il existe 3 bounds ; le bound de collision, le bound visuel et un autre bound visuel autrement appelé traits noirs autours du bloc 😉

    Pour les travailler, je vous conseille d'utiliser la fonction Debug d'eclipse, cette fonction vas vous permettre de modifier quelques fonctions sans devoir relancer le jeu à chaque fois, les bounds en font partie.

    Voici le premier bound :

        public void setBlockBoundsBasedOnState(IBlockAccess par1IBlockAccess, int dir012, int dir3, int y)
        {
            int l = par1IBlockAccess.getBlockMetadata(dir012, dir3, y);
            float f = 0.0625F;
            float f1 = (float)(1 + l * 2) / 16.0F;
            float f2 = 0.5F;
            this.setBlockBounds(f1, 0.0F, f, 1.0F - f, f2, 1.0F - f);
        }
    

    Celui-ci permet de régler la forme du gâteau et sa hitbox, ses réglages sont ceux d'un gâteau par défaut.

    Comment modifier un Bound :

    A vrai dire je ne connait pas vraiment la logique des bounds, mais pour les travailler, je vous conseille le mode Debug d'eclipse comme dit plus haut. Dès que vous modifierais un bound, il vous faudra replacer in-game le bloc sur lequel vous travailler.

    La première valeur (f) correspond à la face Sud, Ouest et Nord.
    La deuxième valeur (f1) correspond à la face Est. (float)(1 + l * 2) / est relié à une fonction que nous verrons plus tard, il faut modifier ces valeurs en fonctions du nombre de parts qu'auras notre gâteau ! Je vous laisse calculer, mais vous devrez uniquement changer le nombre 2 😉 Exemples :

    avec 1 + l * 1 / 16 (1 plus l fois 1 divisé par 16) je pourrais avoir 12 part sur mon gâteau !
    l correspond à une valeur que l'on définiras plus tard, moi je la mettrais sur 12.

    avec 1 + l * 05 / 16 (1 plus l fois 0,5 divisé par 16) je pourrais avec 3 parts sur mon gâteau !
    l correspond cette fois ci à 3.

    La troisième valeur (f2) correspond à là hauteur.
    this.setBlockBounds(f1, 0.0F, f, 1.0F -f, f2, 1.0F -f) définie les bounds visuel dans la classe, les valeurs float qui sont dans cette ligne correspondent à la position du gâteau sur le bloc ou il est posé.

    On passe au deuxième bound, le bound de collision :

        public AxisAlignedBB getCollisionBoundingBoxFromPool(World world, int dir012, int dir3, int y)
        {
            int l = world.getBlockMetadata(dir012, dir3, y);
            float f = 0.0625F;
            float f1 = (float)(1 + l * 2) / 16.0F;
            float f2 = 0.5F;
            return AxisAlignedBB.getAABBPool().getAABB((double)((float)dir012 + f1), (double)dir3, (double)((float)y + f), (double)((float)(dir012 + 1) - f), (double)((float)dir3 + f2 - f), (double)((float)(y + 1) - f));
        }
    

    Pour ce bound, simplement recopier les valeurs float que vous avez mis plus haut 😉

    On passe au troisième bound, le bound des traits noirs :

        @SideOnly(Side.CLIENT)
        public AxisAlignedBB getSelectedBoundingBoxFromPool(World world, int dir012, int dir3, int y)
        {
            int l = world.getBlockMetadata(dir012, dir3, y);
            float f = 0.0625F;
            float f1 = (float)(1 + l * 2) / 16.0F;
            float f2 = 0.5F;
            return AxisAlignedBB.getAABBPool().getAABB((double)((float)dir012 + f1), (double)dir3, (double)((float)y + f), (double)((float)(dir012 + 1) - f), (double)((float)dir3 + f2), (double)((float)(y + 1) - f));
        }
    

    Pareillement que pour la troisième, recopiez les valeurs float.

    Nous avons un dernier bound à placer, je n'en ai pas parler plus tôt car il est moin important. Ce bound concerne uniquement l'item que vous tenez.

        public void setBlockBoundsForItemRender()
        {
            float f = 0.0625F;
            float f1 = 0.5F;
            this.setBlockBounds(f, 0.0F, f, 1.0F - f, f1, 1.0F - f);
        }
    

    Comme pour certains autres bounds, recopiez les valeurs float; Attention, dans la valeur f1 de ce bound, il faut recopier la valeur f2 des autres bounds, pas la f1 !

    Bon, on en a fini avec les bounds, enfin 😑
    Cependant il nous reste quand même un bout de code, aussi grand que ce que l'on a déjà.
    En premier, on vas dire au jeu que le rendu de notre bloc n'est pas un rendu normal ;

        public boolean renderAsNormalBlock()
        {
            return false;
        }
    

    Ensuite, on vas lui dire si le bloc est opaque ou non :

        public boolean isOpaqueCube()
        {
            return false;
        }
    

    Maintenant, on arrive dans la partie qui fait que le bloc est un gâteau.
    On dit donc au jeu que, quand on active le bloc (clique droit), on mange du gâteau ;

        public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer par5EntityPlayer, int par6, float par7, float par8, float par9)
        {
            this.eatCakeSlice(world, x, y, z, par5EntityPlayer);
            return true;
        }
    

    Puis maintenant quand le bloc est cliquer gauche ;

        public void onBlockClicked(World world, int x, int y, int z, EntityPlayer entityplayer)
        {
            this.eatCakeSlice(world, x, y, z, entityplayer);
        }
    

    La fonction suivante est une des plus importantes, elle définie combien de faim et de saturation on récupère et combien de part à le gâteau :

        private void eatCakeSlice(World world, int x, int y, int z, EntityPlayer entityplayer)
        {
            if (entityplayer.canEat(false))
            {
                entityplayer.getFoodStats().addStats(5, 0.1F);
                int l = world.getBlockMetadata(x, y, z) + 1;
    
                if (l >= 6)
                {
                    world.setBlockToAir(x, y, z);
                }
                else
                {
                    world.setBlockMetadataWithNotify(x, y, z, l, 2);
                }
            }
        }
    

    La ligne par5EntityPlayer.getFoodStats().addStats(5, 0.1F) est la ligne qui définie la saturation et la faim que l'on récupère ;
    5 correspond à 2,5 gigots, 0.1F correspond à 0,5 pts de saturation récupéré.

    Ah, au faite, c'est ici que l'on défini la valeur l dont je vous ais parler plus haut, à la ligne if (l >= 6) !
    Remontez à l'explication des bounds si vous ne savez pas de quoi je parle 😉

    Ensuite on ajoute une nouvelle fonction ;

        public boolean canPlaceBlockAt(World world, int x, int y, int z)
        {
            return !super.canPlaceBlockAt(world, x, y, z) ? false : this.canBlockStay(world, x, y, z);
        }
    

    Cette fonction permet au bloc de savoir si il peut se poser là ou on veut le poser... Je sais pas si vous m'avez compris ^^

    Oh on arrive aux 5 dernières fonctions de cette classe .

    Ajoutez cette fonction ;

        public void onNeighborBlockChange(World world, int x, int y, int z)
        {
            if (!this.canBlockStay(world, x, y, z))
            {
                world.setBlockToAir(x, y, z);
            }
        }
    

    Cette fonction sert à la même chose que la précédente, mais à l'update des blocs adjacent.

    Plus que quatre ... ;

        public boolean canBlockStay(World world, int x, int y, int z)
        {
            return world.getBlockMaterial(x, y - 1, z).isSolid();
        }
    

    Cette fonction est similaire à canPlaceBlockAt.

    On arrive dans les 3 dernières fonctions de ce gâteau de malheur ç_ç

    Cette fonction dit au jeu que, quand je casse le gâteau, rien ne doit tomber !

        public int quantityDropped(Random random)
        {
            return 0;
        }
    

    Pourquoi utiliser cette fonction ? Tout simplement, si on commence à manger le gâteau et qu'on le casse, il vas drop avec toutes ses parts, en gros, il serais infini.

    Celle-ci est la même que la précédente, mais pour l'id drop ;

        public int idDropped(int id, Random random)
        {
            return 0;
        }
    

    C'est la dernière fonction du bloc gâteau ! Youpi ! x) Il reste encore l'item après, désolé.

        public int idPicked(World world, int x, int y, int z)
        {
            return ModTutoForge.ItemTutoGateau.itemID;
        }
    

    Cette fonction défini quel item/bloc vas être pris quand, en créatif, on vas faire un clique molette. On changeras plus tard cette fonction, car, comme vous le savez peut-être, quand on a un gâteau dans Minecraft, on a jamais le bloc gâteau directement ! Donc on vas tout de suite s'occuper de ça dans la partie de l'item gâteau !

    La classe de l'item

    On commence par le déclarer dans la classe principale, comme tout objet, puis lui mettre ses réglages, sa texture, son nom non localisé et ensuite l'enregistrer auprès du jeu.

    On crée ensuite sa classe, on l'étend en Item et on y met tout ça ;

        public boolean onItemUse(ItemStack ItemStack, EntityPlayer entityplayer, World world, int x, int y, int z, int par7, float par8, float par9, float par10)
        {
            if (world.getBlockId(x, y, z) != Block.snow.blockID)
            {
                if (par7 == 0)
                {
                    --y;
                }
    
                if (par7 == 1)
                {
                    ++y;
                }
    
                if (par7 == 2)
                {
                    --z;
                }
    
                if (par7 == 3)
                {
                    ++z;
                }
    
                if (par7 == 4)
                {
                    --x;
                }
    
                if (par7 == 5)
                {
                    ++x;
                }
    
                if (!world.isAirBlock(x, y, z))
                {
                    return false;
                }
            }
    
            if (!entityplayer.canPlayerEdit(x, y, z, par7, ItemStack))
            {
                return false;
            }
            else
            {
                if (Block.redstoneWire.canPlaceBlockAt(world, x, y, z))
                {
                    --ItemStack.stackSize;
                    world.setBlock(x, y, z, VotreMainClass.VotreBlockGateau.blockID);
                }
    
                return true;
            }
        }
    

    Oui, ceci est long 🙂
    La seule chose que vous aurez à modifier, c'est cette ligne ;

        world.setBlock(par4, par5, par6, VotreMainClass.VotreBlockGateau.blockID);
    

    Remplacez tout simplement VotreMainClass par votre classe principale et VotreGateau par votre bloc de gâteau.
    Après ceci, nous avons presque fini, il nous reste encore les textures.

    Les textures

    Comme vous le savez le gâteau a plusieurs textures, donc il vas falloir les préparer. Si vous ne voulez pas faire vos textures, je vous donne des textures très... Rose et noir.
    http://www.mediafire.com/folder/zmq9x7ezb9miz/Tuto_Cake

    Ensuite, mettez les textures de bloc dans VotreDossierForge>mcp>src>assets>VotreNomDeDossier>blocks
    Pareille pour la texture d'items VotreDossierForge>mcp>src>assets>VotreNomDeDossier>items

    Avoir la texture, c'est pas tout ! Pour le bloc en tout cas; il vas falloir les définir.
    Pour ça, on retourne dans notre classe bordélique du gâteau. En haut, comme pour des items ou des blocs, vous allez devoir déclarer des Icon ;

        @SideOnly(Side.CLIENT)
        private Icon cakeTopIcon, cakeBottomIcon, cakeInnerIcon;
    

    Puis, ajouter ça ;

        public Icon getIcon(int par1, int par2)
        {
            return par1 == 1 ? this.cakeTopIcon : (par1 == 0 ? this.cakeBottomIcon : (par2 > 0 && par1 == 4 ? this.cakeInnerIcon : this.blockIcon));
        }
    

    Ceci défini sur telles cotés vont être telles textures.

    Ensuite, on ajoute ce qui défini les textures ;

        @SideOnly(Side.CLIENT)
        public void registerIcons(IconRegister par1IconRegister)
        {
            this.blockIcon = par1IconRegister.registerIcon("VotreNomDeDossier:VotreNomD'Image_side");
            this.cakeInnerIcon = par1IconRegister.registerIcon("VotreNomDeDossier:VotreNomD'Image_inner");
            this.cakeTopIcon = par1IconRegister.registerIcon("VotreNomDeDossier:VotreNomD'Image_top");
            this.cakeBottomIcon = par1IconRegister.registerIcon("VotreNomDeDossier:VotreNomD'image_bottom");
        }
    

    this.blockIcon est la texture du coté
    this.cakeInnerIcon est la texture intérieure
    this.cakeTopIcon est la texture du dessus
    this.cakeBottomIcon est la texture de dessous

    Normalement les textures devrait s'afficher in-game.

    Screenshots

    Voici quelques screenshots de création de gâteau ;


    Avec la texture que j'ai donné 😛


    Un gâteau bleu à l'os ...


    Ou encore une pizza !

    Outroduction

    Merci d'avoir lu tout ça, je sais que c'est dur 🙂
    Si vous avez des problèmes dites le moi, passez moi votre code, etc ...
    De même si vous avez vu une faute, un sottise, une faute d'orthographe/grammaire ou encore que vous savez à quoi sert quelque chose, alors je pourrais le rajouter à mon tutoriel.
    J'essaierais de répondre à tout les messages.

    Voir sur github

    Bye.
    ~Artnerm


  • Administrateurs

    Il a été posté au mauvais endroit non ?

    Le tutoriel semble ok, essaye juste de revoir le nom des paramétrès dans les codes (j'aime pas les par1World, par2 etc .. je préfère world, x, y, z, c'est plus pratique pour savoir à quoi ça correspond ;))



  • Tutoriel mis à jour; les paramètres on changés de nom (je ne suis pas sûr que touts les noms correspondent à ce qu'ils définissent), screenshots ajoutés et peut-être github bientôt, je l’espère.
    Je test tout les codes, il ce peut que j'ai effectuer une faute de frappe ( j'ai essayer de faire un gâteau à partir de mon tuto, il marche mais je ne peut pas le manger en utilisant clique droit )

    PS: Pourquoi a-t-il été placé au mauvais endroit ?


  • Administrateurs

    Je sais pas, quand j'ai vu le tutoriel il était dans tutoriel non validé à la place d'être dans en attente de validation.



  • O_o C'est pas normal, maintenant tu le vois au bon endroit ? Car moi je l'ai toujours vu dans Tutoriels en attente de validation. Enfin, bref, j'espère que ce tuto vas être accepter :p___
    Bug du clique droit normalement régler ^^



  • Bon tuto !


  • Administrateurs

    Je valide, ça fait des nouveaux tutoriels comme je suis en pause en attendant la 1.7.



  • GG pour le tuto, même si une balise Java a foirée (4ème avant la partie sur la classe de l'item)


  • Administrateurs

    Corrigé, merci d'avoir signalé.


  • Administrateurs

    https://github.com/FFMT/ModTutoriel/commit/29281f3993cb5ce03c3ff8fedb9a4b097bc61abe
    J'ai corrigé quelques petits problèmes de noms (il y avait le mauvais modid dans l'enregistrement d'un item, ça faisait crash), j'ai renommé les variables en anglais, et j'ai ajouté un son lors du placement du bloc.
    Si tu pouvais adapter le tutoriel, ça serait cool (sinon je le ferai moi).


Log in to reply