Animer vos rendus TESR.


  • Administrateurs

    Introduction

    Dans l'introduction du tutoriel sur les rendus TESR, il a été dit que le TESR a le désavantage d'être rendu à chaque tick. C'est en effet un désavantage car le rendu est lourd, mais c'est aussi un avantage car il permet de faire des rendus animés !
    Pour ce tutoriel j'ai préparé deux blocs en metadata sur la sculpture précédemment créée dans le tutoriel sur les rendus de blocs TESR.

    Voici les deux blocs en question :
    Bloc à animer
    À gauche, un placard, dont l'objectif va être d'animer les deux portes lorsqu'on ouvre et ferme le gui du bloc (car il s'agit bien sur d'un container que vous pouvez faire en suivant ce tutoriel) et à droite, une sorte de machine, l'objectif va être de faire pivoter la barre (le truc qui ressemble vaguement à un levier) à gauche lorsqu'on fait un clic sur la gauche du bloc, et inversement avec la droite. Le deuxième objectif avec ce bloc va être de faire tourner la partie noire (l'hélice + l'axe) plus ou moins vite en fonction du levier. Et pour ajouter de la difficulté/complexité, les deux blocs sont orientables.

    Attention au placement de l'offset, je rappelle que c'est lui qui défini le point de rotation, s'il est mal placé la rotation ne fonctionnera pas correctement. Si vous souhaitez voir mes modèles (fait avec techne) :
    0_1528886982815_Cupboard.tcn
    0_1528886993322_Machine.tcn

    Prérequis

    Rotation des portes du placard

    Nous allons commencer par le placard. Allez dans la classe de votre tile entity, vous avez surement déjà remarqué qu'il existe les méthodes openChest et closeChest. Nous allons en effet les utiliser. Mais déclarez d'abord dans votre tile entity les 4 variables suivantes :

    public float lidAngle;
    public float prevLidAngle;
    public int numUsingPlayers;
    private int ticksSinceSync;
    

    lidAngle est l'angle de rotation actuel, prevLidAngle l'angle précédent, numUsingPlayers le nombre de joueurs qui utilisent le container, et ticksSinceSync le nombre de tick qui se sont écoulés depuis la dernière vérification. Nous allons définir et utiliser ces variables plus tard, pour l'instant elles ne fond rien.

    Dans la méthode openChest() ajoutez :

            if(this.numUsingPlayers < 0)
            {
                this.numUsingPlayers = 0;
            }
    
            ++this.numUsingPlayers;
            this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
    

    La condition sert à vérifier que le nombre de joueurs n'est pas négatif (on sait jamais, ça peut arriver), si c'est le cas la valeur est remise à 0.
    Ensuite, on augmente le nombre de joueurs qui utilisent le coffre et on va envoyer un event pour signaler que le nombre de joueurs ayant ouvert le coffre à changé.
    Dans la méthode closeChest() ajoutez :

            –this.numUsingPlayers;
            this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
    

    Même chose qu'avant sauf qu'on diminue le nombre de joueurs.

    Maintenant il faut récupérer l'évènement de bloc. Dans la classe de votre bloc, ajoutez ce code :

        public boolean onBlockEventReceived(World world, int x, int y, int z, int eventId, int eventValue)
        {
            super.onBlockEventReceived(world, x, y, z, eventId, eventValue);
            TileEntity tileentity = world.getBlockTileEntity(x, y, z);
            return tileentity != null ? tileentity.receiveClientEvent(eventId, eventValue) : false;
        }
    

    Ce code va lancer la fonction receiveClientEvent dans le tile entity, donc retournez dans la classe de votre tile entity et ajoutez cette méthode :

        public boolean receiveClientEvent(int eventId, int eventValue)
        {
            if(eventId == 1)
            {
                this.numUsingPlayers = eventValue;
                return true;
            }
            else
            {
                return super.receiveClientEvent(eventId, eventValue);
            }
        }
    

    Maintenant le client et le serveur sont synchronisés, le nombre de joueurs ayant ouvert le coffre sera le même sur le WorldClient et le WorldServer.
    Le problème, c'est que les méthodes openChest() et closeChest() ne sont jamais exécutées, il faut donc les appeler. Allez dans la classe de votre container.
    On sait que la classe du container est instanciée à chaque fois qu'on l'ouvre, donc dans le constructeur de cette classe, ajoutez :

        te.openChest();
    

    te étant l'instance de mon tileEntity (donc le nom qui se trouve après l'argument de votre tile entity dans le constructeur). Le constructeur ressemble donc à ça :

        public ContainerCupboard(InventoryPlayer playerInv, TileEntityCupboard te)
        {
            this.tileEntity = te;
            te.openChest();
    
            for(int i = 0; i < 6; i++)
            {
                for(int j = 0; j < 9; j++)
                {
                    this.addSlotToContainer(new Slot(te, j + i * 9, 8 + j * 18, 18 + i * 18));
                }
            }
            this.bindPlayerInventory(playerInv);
        }
    

    Toujours dans cette même classe, ajoutez la méthode suivante :

        public void onContainerClosed(EntityPlayer player)
        {
            super.onContainerClosed(player);
            tileEntity.closeChest();
        }
    

    Voila, maintenant les méthodes openChest() et closeChest() seront exécutées.

    Retournez dans la classe de votre tile entity, et ajoutez la méthode suivante :

        public void updateEntity()
        {
            super.updateEntity();
            ++this.ticksSinceSync;
    
            if(!this.worldObj.isRemote && this.numUsingPlayers != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0)
            {
                this.numUsingPlayers = 0;
                List list = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getAABBPool().getAABB(this.xCoord - 5, this.yCoord - 5, this.zCoord - 5, this.xCoord + 6, this.yCoord + 6, this.zCoord + 6));
                Iterator iterator = list.iterator();
    
                while(iterator.hasNext())
                {
                    EntityPlayer entityplayer = (EntityPlayer)iterator.next();
    
                    if(entityplayer.openContainer instanceof ContainerCupboard)
                    {
                        ++this.numUsingPlayers;
                    }
                }
            }
    
            this.prevLidAngle = this.lidAngle;
    
            if(this.numUsingPlayers > 0 && this.lidAngle == 0.0F)
            {
                this.worldObj.playSoundEffect(((double)this.xCoord + 0.5), (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
            }
    
            if(this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
            {
                float f1 = this.lidAngle;
    
                if(this.numUsingPlayers > 0)
                {
                    this.lidAngle += 0.1F;
                }
                else
                {
                    this.lidAngle -= 0.1F;
                }
    
                if(this.lidAngle > 1.0F)
                {
                    this.lidAngle = 1.0F;
                }
    
                float f2 = 0.5F;
    
                if(this.lidAngle < f2 && f1 >= f2)
                {
                    this.worldObj.playSoundEffect((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
                }
    
                if(this.lidAngle < 0.0F)
                {
                    this.lidAngle = 0.0F;
                }
            }
        }
    

    Il s'agit de la même que celle du coffre, un peu simplifier sans les codes qui vérifient s'il y a deux coffres l'un à côté de l'autre.
    Petite explication : Si le monde n'est pas distant (donc monde serveur) que le nombre de joueur n'est pas égale à 0, et que le que résultat de la division euclidienne de l'addition de toutes les coordonnées + le temps de tick par 200 est égale à 0 (ce code permet de ne pas vérifier à chaque tick, car si c'était le cas le code en dessous surchargerai le jeu et causerai des lags) alors on crée une liste de tous les joueurs sur un rayon de 5 blocs autour du bloc. Si le joueur a ouvert le bloc, on l'ajoute, augmentant le nombre de joueurs qui ont ouvert le coffre.
    Le reste du code sert à jouer le son et à calculer l'angle.

    Pour finir, allez dans le code de votre TileEntitySpecialRenderer. Dans la méthode renderTileEntityCupboardeAt ajoutez avant le "this.model.render(0.0625F);" une condition qui vérifie que le TileEntityCupboard n'est pas null (dans le cas du rendu dans l'inventaire il sera null, donc il faut mettre un "null check" pour ne pas avoir un crash avec un NullPointerException). Si vous avez suivi le bonus pour rendre votre bloc orientable, vous en avez déjà un.

        if(te != null)
        {
        }
    

    Dans cette condition, ajoutez le code suivant :

        float angle = te.prevLidAngle + (te.lidAngle - te.prevLidAngle) * tick;
        angle = 1.0F - angle;
        angle = 1.0F - angle * angle * angle;
        this.model.doorLeft.rotateAngleY = (angle * (float)Math.PI / 2.0F);
        this.model.doorRight.rotateAngleY = -(angle * (float)Math.PI / 2.0F);
    

    Les trois premières lignes vont nous donner une variable angle qui correspond à ce dont nous avons besoin. Une fois de plus, ce code vient du coffre. Ensuite je modifie l'angle de rotation Y (car mes portières sont sur l'axe Y, le coffre tourne sur l'axe X) en fonction de l'angle et de p/2. Ceux qui ont fait de la trigonométrie savent pourquoi on utilise p/2, pour ceux qui en n'ont pas fait, c'est tout simplement car p/2 correspond à 1/4 du périmètre d'un cercle (et que nous voulons faire une rotation de 1/4 de cercle). C'est quelque chose qui se voit en fin seconde, si vous être curieux : http://www.methodemaths.fr/cercle_trigo.php

    Vous avez pu constatez que dans un cas j'utilise une valeur négative (droite) et dans l'autre une valeur positive (gauche), la raison est que les deux portières s'ouvrent à l’opposé. Pour ceux qui n'aurait pas compris doorLeft et doorRight sont les noms de mes morceaux de modèle.

    Pour les réglages d'angles, je vous conseils d'utiliser le debug plutôt que le run normal, ainsi vous pouvez modifier et voir les changements en direct sans relancer le jeu à chaque fois.
    Lancer en debug

    Mon renderTileEntityCupboard ressemble maintenant à ça :

        public void renderTileEntityCupboardeAt(TileEntityCupboard te, double x, double y, double z, float tick)
        {
            GL11.glPushMatrix();
            GL11.glTranslated(x + 0.5F, y + 1.5F, z + 0.5F);
            this.bindTexture(textureLocation);
            GL11.glRotatef(180F, 0.0F, 0.0F, 1.0F);
            if(te != null)
            {
                GL11.glRotatef(90F * te.getDirection(), 0.0F, 1.0F, 0.0F);
                float angle = te.prevLidAngle + (te.lidAngle - te.prevLidAngle) * tick;
                angle = 1.0F - angle;
                angle = 1.0F - angle * angle * angle;
                this.model.doorLeft.rotateAngleY = (angle * (float)Math.PI / 2.0F);
                this.model.doorRight.rotateAngleY = -(angle * (float)Math.PI / 2.0F);
            }
            GL11.glRotatef(90F, 0.0F, 1.0F, 0.0F);
            this.model.render(0.0625F);
            GL11.glPopMatrix();
        }
    

    Et maintenant il ne me reste plus qu'à lancer mon jeu, et voilà le résultat :
    Porte ouverte Porte semi-ouverte
    Voir le commit sur github

    Animation de la machine

    => Cette partie n'a finalement jamais été redigé ...

    Voir le commit sur github



  • Merci, ça marche impec. le seul petit problème est que quand j'ai deux armoires côte à côte, je clique sur l'une, et c'est l'autre qui s'ouvre ^^


  • Administrateurs

    Étrange, je n'ai pas ce problème. Ton bloc est-il plus grand que 1x1x1 ?



  • Non, il est même plus petit. c'est une armoire à pharmacie.
    ___
    Voici mon code

    TileEntityArmoirePh

    
    package _fearZ.mod.tileentity;
    
    import java.util.Iterator;
    import java.util.List;
    
    import _fearZ.mod.ContainerArmoirePh;
    import _fearZ.mod.blocks.BlockPharmacie;
    import _fearZ.mod.blocks.model.ModelArmoirPh;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.inventory.IInventory;
    import net.minecraft.item.ItemStack;
    import net.minecraft.nbt.NBTTagCompound;
    import net.minecraft.nbt.NBTTagList;
    import net.minecraft.network.INetworkManager;
    import net.minecraft.network.packet.Packet;
    import net.minecraft.network.packet.Packet132TileEntityData;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.AxisAlignedBB;
    
    public class TileEntityArmoirePh extends TileEntity implements IInventory
    {
    public float lidAngle;
    public float prevLidAngle;
    public int numUsingPlayers;
    private int ticksSinceSync;
    
    public byte direction;
    private ItemStack[] inventory = new ItemStack[72];
    private String customName;
    public void readFromNBT(NBTTagCompound nbtTag)
    {
    super.readFromNBT(nbtTag);
    direction = nbtTag.getByte("direction");
    
    super.readFromNBT(nbtTag);
    NBTTagList nbttaglist = nbtTag.getTagList("Items");
    this.inventory = new ItemStack[this.getSizeInventory()];
    
    if (nbtTag.hasKey("CustomName"))
    {
    this.customName = nbtTag.getString("CustomName");
    }
    
    for (int i = 0; i < nbttaglist.tagCount(); i++)
    {
    NBTTagCompound nbttagcompound1 = (NBTTagCompound)nbttaglist.tagAt(i);
    int j = nbttagcompound1.getByte("Slot");
    
    if (j >= 0 && j < this.inventory.length)
    {
    this.inventory[j] = ItemStack.loadItemStackFromNBT(nbttagcompound1);
    }
    }
    }
    
    public boolean isUseableByPlayer(EntityPlayer player)
    {
    return worldObj.getBlockTileEntity(xCoord, yCoord, zCoord) == this && player.getDistanceSq(xCoord + 0.5, yCoord + 0.5, zCoord + 0.5) < 64;
    }
    
    public void writeToNBT(NBTTagCompound nbtTag)
    {
    super.writeToNBT(nbtTag);
    nbtTag.setByte("direction", direction);
    
    NBTTagList nbttaglist = new NBTTagList();
    
    for (int i = 0; i < this.inventory.length; i++)
    {
    if (this.inventory* != null)
    {
    NBTTagCompound nbttagcompound1 = new NBTTagCompound();
    nbttagcompound1.setByte("Slot", (byte)i);
    this.inventory*.writeToNBT(nbttagcompound1);
    nbttaglist.appendTag(nbttagcompound1);
    }
    }
    
    nbtTag.setTag("Items", nbttaglist);
    
    if (this.isInvNameLocalized())
    {
    nbtTag.setString("CustomName", this.customName);
    }
    }
    
    public ItemStack decrStackSize(int slotId, int quantity)
    {
    if (this.inventory[slotId] != null)
    {
    ItemStack itemstack;
    
    if (this.inventory[slotId].stackSize <= quantity)
    {
    itemstack = this.inventory[slotId];
    this.inventory[slotId] = null;
    this.onInventoryChanged();
    return itemstack;
    }
    else
    {
    itemstack = this.inventory[slotId].splitStack(quantity);
    
    if (this.inventory[slotId].stackSize == 0)
    {
    this.inventory[slotId] = null;
    }
    
    this.onInventoryChanged();
    return itemstack;
    }
    }
    else
    {
    return null;
    }
    }
    
    public ItemStack getStackInSlotOnClosing(int slotId)
    {
    if (this.inventory[slotId] != null)
    {
    ItemStack itemstack = this.inventory[slotId];
    this.inventory[slotId] = null;
    return itemstack;
    }
    else
    {
    return null;
    }
    }
    
    public void setInventorySlotContents(int slotId, ItemStack stack)
    {
    this.inventory[slotId] = stack;
    
    if (stack != null && stack.stackSize > this.getInventoryStackLimit())
    {
    stack.stackSize = this.getInventoryStackLimit();
    }
    
    this.onInventoryChanged();
    }
    
    public String getInvName()
    {
    return this.isInvNameLocalized() ? this.customName : "container.armoireph";
    }
    
    public int getInventoryStackLimit()
    {
    return 64;
    }
    
    public boolean isInvNameLocalized()
    {
    return this.customName != null && this.customName.length() > 0;
    }
    
    public void setCustomGuiName(String name)
    {
    this.customName = name;
    }
    
    public ItemStack getStackInSlot(int slotId)
    {
    return inventory[slotId];
    }
    
    public int getSizeInventory()
    {
    return inventory.length;
    }
    
    public void setDirection(byte direct)
    {
    direction = direct;
    }
    
    public byte getDirection()
    {
    return direction;
    }
    
    public Packet getDescriptionPacket()
    {
    NBTTagCompound nbttagcompound = new NBTTagCompound();
    this.writeToNBT(nbttagcompound);
    return new Packet132TileEntityData(this.xCoord, this.yCoord, this.zCoord, 4, nbttagcompound);
    }
    
    public boolean isItemValidForSlot(int slotId, ItemStack stack)
    {
    return true;
    }
    
    public void onDataPacket(INetworkManager net, Packet132TileEntityData pkt)
    {
    this.readFromNBT(pkt.data);
    }
    
    @Override
    public void openChest()
    {
    if(this.numUsingPlayers < 0)
    {
    this.numUsingPlayers = 0;
    }
    
    ++this.numUsingPlayers;
    this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
    }
    
    @Override
    public void closeChest()
    {
    –this.numUsingPlayers;
    this.worldObj.addBlockEvent(this.xCoord, this.yCoord, this.zCoord, this.getBlockType().blockID, 1, this.numUsingPlayers);
    }
    
    public boolean receiveClientEvent(int eventId, int eventValue)
    {
    if(eventId == 1)
    {
    this.numUsingPlayers = eventValue;
    return true;
    }
    else
    {
    return super.receiveClientEvent(eventId, eventValue);
    }
    }
    
    public void updateEntity()
    {
    super.updateEntity();
    ++this.ticksSinceSync;
    
    if(!this.worldObj.isRemote && this.numUsingPlayers != 0 && (this.ticksSinceSync + this.xCoord + this.yCoord + this.zCoord) % 200 == 0)
    {
    this.numUsingPlayers = 0;
    List list = this.worldObj.getEntitiesWithinAABB(EntityPlayer.class, AxisAlignedBB.getAABBPool().getAABB(this.xCoord - 5, this.yCoord - 5, this.zCoord - 5, this.xCoord + 6, this.yCoord + 6, this.zCoord + 6));
    Iterator iterator = list.iterator();
    
    while(iterator.hasNext())
    {
    EntityPlayer entityplayer = (EntityPlayer)iterator.next();
    
    if(entityplayer.openContainer instanceof ContainerArmoirePh)
    {
    ++this.numUsingPlayers;
    }
    }
    }
    
    this.prevLidAngle = this.lidAngle;
    
    if(this.numUsingPlayers > 0 && this.lidAngle == 0.0F)
    {
    this.worldObj.playSoundEffect(((double)this.xCoord + 0.5), (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D, "random.chestopen", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
    }
    
    if(this.numUsingPlayers == 0 && this.lidAngle > 0.0F || this.numUsingPlayers > 0 && this.lidAngle < 1.0F)
    {
    float f1 = this.lidAngle;
    
    if(this.numUsingPlayers > 0)
    {
    this.lidAngle += 0.1F;
    }
    else
    {
    this.lidAngle -= 0.1F;
    }
    
    if(this.lidAngle > 1.0F)
    {
    this.lidAngle = 1.0F;
    }
    
    float f2 = 0.5F;
    
    if(this.lidAngle < f2 && f1 >= f2)
    {
    this.worldObj.playSoundEffect((double)this.xCoord + 0.5D, (double)this.yCoord + 0.5D, (double)this.zCoord + 0.5D, "random.chestclosed", 0.5F, this.worldObj.rand.nextFloat() * 0.1F + 0.9F);
    }
    
    if(this.lidAngle < 0.0F)
    {
    this.lidAngle = 0.0F;
    }
    }
    }
    
    }
    
    

    ContainerArmoirePh

    
    package _fearZ.mod;
    
    import _fearZ.mod.tileentity.TileEntityArmoirePh;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.entity.player.InventoryPlayer;
    import net.minecraft.inventory.Container;
    import net.minecraft.inventory.Slot;
    import net.minecraft.item.ItemStack;
    
    public class ContainerArmoirePh extends Container
    {
    private TileEntityArmoirePh tileEntity;
    
    public ContainerArmoirePh(InventoryPlayer playerInventory, TileEntityArmoirePh teArmoirePh)
    {
    this.tileEntity = teArmoirePh;
    
    teArmoirePh.openChest();
    
    for(int i = 0; i < 6; i++)
    {
    for(int j = 0; j < 9; j++)
    {
    this.addSlotToContainer(new Slot(teArmoirePh, j + i * 9, 8 + j * 18, 18 + i * 18));
    }
    }
    this.bindPlayerInventory(playerInventory);
    }
    
    private void bindPlayerInventory(InventoryPlayer playerInventory)
    {
    int i;
    for(i = 0; i < 3; i++)
    {
    for(int j = 0; j < 9; j++)
    {
    this.addSlotToContainer(new Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 103 + i * 18 + 37));
    }
    }
    
    for(i = 0; i < 9; i++)
    {
    this.addSlotToContainer(new Slot(playerInventory, i, 8 + i * 18, 161 + 37));
    }
    }
    
    public void onContainerClosed(EntityPlayer player)
    {
    super.onContainerClosed(player);
    tileEntity.closeChest();
    }
    
    @Override
    public boolean canInteractWith(EntityPlayer player)
    {
    return tileEntity.isUseableByPlayer(player);
    }
    
    public ItemStack transferStackInSlot(EntityPlayer player, int slotId)
    {
    ItemStack itemstack = null;
    Slot slot = (Slot)this.inventorySlots.get(slotId);
    
    if(slot != null && slot.getHasStack())
    {
    ItemStack itemstack1 = slot.getStack();
    itemstack = itemstack1.copy();
    
    if(slotId < 9)
    {
    if(!this.mergeItemStack(itemstack1, 9, this.inventorySlots.size(), true))
    {
    return null;
    }
    }
    else if(!this.mergeItemStack(itemstack1, 0, 9, false))
    {
    return null;
    }
    
    if(itemstack1.stackSize == 0)
    {
    slot.putStack((ItemStack)null);
    }
    else
    {
    slot.onSlotChanged();
    }
    }
    return itemstack;
    }
    }
    
    

    BlockPharmacie

    
    package _fearZ.mod.blocks;
    
    import java.util.List;
    
    import _fearZ.mod.Mod_FearZ;
    import _fearZ.mod.blocks.model.ModelArmoirPh;
    import _fearZ.mod.client.ClientProxy;
    import _fearZ.mod.tileentity.TileEntityArmoirePh;
    
    import net.minecraft.block.Block;
    import net.minecraft.block.BlockContainer;
    import net.minecraft.block.material.Material;
    import net.minecraft.client.Minecraft;
    import net.minecraft.client.renderer.texture.IconRegister;
    import net.minecraft.creativetab.CreativeTabs;
    import net.minecraft.entity.Entity;
    import net.minecraft.entity.EntityLivingBase;
    import net.minecraft.entity.item.EntityItem;
    import net.minecraft.entity.player.EntityPlayer;
    import net.minecraft.item.ItemStack;
    import net.minecraft.nbt.NBTTagCompound;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.Icon;
    import net.minecraft.util.MathHelper;
    import net.minecraft.world.IBlockAccess;
    import net.minecraft.world.World;
    import cpw.mods.fml.common.network.FMLNetworkHandler;
    import cpw.mods.fml.relauncher.Side;
    import cpw.mods.fml.relauncher.SideOnly;
    
    public class BlockPharmacie extends BlockContainer
    {
    private double angle = 0;
    public BlockPharmacie(int id)
    {
    super(id, Material.rock);
    this.setCreativeTab(Mod_FearZ.onglet);
    }
    
    @Override
    public TileEntity createNewTileEntity(World world)
    {
    return null;
    }
    
    @Override
    public TileEntity createTileEntity(World world, int metadata)
    {
    return new TileEntityArmoirePh();
    }
    
    public boolean hasTileEntity(int metadata)
    {
    return true;
    }
    
    public boolean renderAsNormalBlock()
    {
    return false;
    }
    
    public boolean isOpaqueCube()
    {
    return false;
    }
    
    @SideOnly(Side.CLIENT)
    public int getRenderType()
    {
    return ClientProxy.renderInventoryTESRId;
    }
    
    public boolean onBlockActivated(World world, int x, int y, int z, EntityPlayer player, int par6, float par7, float par8, float par9)
    {
    FMLNetworkHandler.openGui(player, Mod_FearZ.modInstance, 0, world, x, y, z);
    return true;
    }
    
    public void onBlockPlacedBy(World world, int x, int y, int z, EntityLivingBase living, ItemStack stack)
    {
    int direction = MathHelper.floor_double((double)(living.rotationYaw * 4.0F / 360.0F) + 2.5D) & 3;
    TileEntity te = world.getBlockTileEntity(x, y, z);
    if(te != null && te instanceof TileEntityArmoirePh)
    {
    ((TileEntityArmoirePh)te).setDirection((byte)direction);
    world.markBlockForUpdate(x, y, z);
    }
    }
    
    public void breakBlock(World world, int x, int y, int z, int side, int metadata)
    {
    dropContainerItem(world, x, y, z);
    super.breakBlock(world, x, y, z, side, metadata);
    }
    
    protected void dropContainerItem(World world, int x, int y, int z)
    {
    TileEntityArmoirePh pharmacie = (TileEntityArmoirePh)world.getBlockTileEntity(x, y, z);
    
    if (pharmacie != null)
    {
    for (int slotId = 0; slotId < pharmacie.getSizeInventory(); slotId++)
    {
    ItemStack stack = pharmacie.getStackInSlot(slotId);
    
    if (stack != null)
    {
    float f = world.rand.nextFloat() * 0.8F + 0.1F;
    float f1 = world.rand.nextFloat() * 0.8F + 0.1F;
    EntityItem entityitem;
    
    for (float f2 = world.rand.nextFloat() * 0.8F + 0.1F; stack.stackSize > 0; world.spawnEntityInWorld(entityitem))
    {
    int k1 = world.rand.nextInt(21) + 10;
    
    if (k1 > stack.stackSize)
    {
    k1 = stack.stackSize;
    }
    
    stack.stackSize -= k1;
    entityitem = new EntityItem(world, (double)((float)x + f), (double)((float)y + f1), (double)((float)z + f2), new ItemStack(stack.itemID, k1, stack.getItemDamage()));
    float f3 = 0.05F;
    entityitem.motionX = (double)((float)world.rand.nextGaussian() * f3);
    entityitem.motionY = (double)((float)world.rand.nextGaussian() * f3 + 0.2F);
    entityitem.motionZ = (double)((float)world.rand.nextGaussian() * f3);
    
    if (stack.hasTagCompound())
    {
    entityitem.getEntityItem().setTagCompound((NBTTagCompound)stack.getTagCompound().copy());
    }
    }
    }
    }
    }
    }
    
    public boolean onBlockEventReceived(World world, int x, int y, int z, int eventId, int eventValue)
    {
    TileEntity tileentity = world.getBlockTileEntity(x, y, z);
    return tileentity != null ? tileentity.receiveClientEvent(eventId, eventValue) : false;
    }
    
    @Override
    public void setBlockBoundsBasedOnState(IBlockAccess blockAccess, int x, int y, int z)
    {
    TileEntity te = blockAccess.getBlockTileEntity(x, y, z);
    if (te != null && te instanceof TileEntityArmoirePh)
    {
    if (((TileEntityArmoirePh)te).getDirection() == 0)
    {
    this.setBlockBounds(0.0F, 0.19F, 0.0F, 1.0F, 0.81F, 0.368F);
    }
    
    else if (((TileEntityArmoirePh)te).getDirection() == 1)
    {
    this.setBlockBounds(0.625F, 0.19F, 0.0F, 1.0F, 0.81F, 1.0F);
    }
    else if (((TileEntityArmoirePh)te).getDirection() == 2)
    {
    this.setBlockBounds(0.0F, 0.19F, 0.625F, 1.0F, 0.81F, 1.0F);
    }
    else if (((TileEntityArmoirePh)te).getDirection() == 3)
    {
    this.setBlockBounds(0.0F, 0.19F, 0.0F, 0.368F, 0.81F, 1.0F);
    }
    else
    {
    this.setBlockBounds(0.0F, 0.0F, 0.0F, 1.0F, 1.0F, 1.0F);
    }
    }
    }
    public Icon getIcon(int side, int metadata)
    {
    return Block.blockIron.getIcon(0, 0);
    }
    }
    
    

    TileEntityArmoirePhSpecialRender

    
    package _fearZ.mod.tileentity.renderer;
    
    import org.lwjgl.opengl.GL11;
    
    import _fearZ.mod.blocks.model.ModelArmoirPh;
    import _fearZ.mod.tileentity.TileEntityArmoirePh;
    import net.minecraft.client.renderer.tileentity.TileEntityRenderer;
    import net.minecraft.client.renderer.tileentity.TileEntitySpecialRenderer;
    import net.minecraft.tileentity.TileEntity;
    import net.minecraft.util.ResourceLocation;
    
    public class TileEntityArmoirePhSpecialRender extends TileEntitySpecialRenderer implements IInventoryRenderer
    {
    private final ModelArmoirPh mdl = new ModelArmoirPh();
    public static final ResourceLocation texture = new ResourceLocation("fearz", "textures/blocks/ArmoirePharmacie.png");
    public TileEntityArmoirePhSpecialRender()
    {
    this.setTileEntityRenderer(TileEntityRenderer.instance);
    }
    
    @Override
    public void renderInventory(double x, double y, double z)
    {
    this.renderTileEntityArmoirePhAt(null, x, y, z, 0.0F);
    }
    
    @Override
    public void renderTileEntityAt(TileEntity te, double x, double y, double z, float f)
    {
    this.renderTileEntityArmoirePhAt((TileEntityArmoirePh)te, x, y, z, f);
    }
    
    public void renderTileEntityArmoirePhAt(TileEntityArmoirePh te, double x, double y, double z, float tick)
    {
    GL11.glPushMatrix();
    GL11.glTranslated(x + 0.5F, y + 1.5F, z + 0.5F);
    this.bindTexture(texture);
    
    GL11.glRotatef(180.0F, -2000000.0F, 0.0F, 1.0F);
    if(te != null)
    {
    GL11.glRotatef(90F * te.getDirection(), 0.0F, 1.0F, 0.0F);
    }
    this.mdl.render(0.0625F);
    GL11.glPopMatrix();
    
    if(te != null)
    {
    float angle = te.prevLidAngle + (te.lidAngle - te.prevLidAngle) * tick;
    angle = 1.0F - angle;
    angle = 1.0F - angle * angle * angle;
    this.mdl.porte1.rotateAngleY = (angle * (float)Math.PI / 2.0F);
    this.mdl.poignee1.rotateAngleY = (angle * (float)Math.PI / 2.0F);
    this.mdl.porte2.rotateAngleY = -(angle * (float)Math.PI / 2.0F);
    this.mdl.poignee2.rotateAngleY = -(angle * (float)Math.PI / 2.0F);
    }
    }
    }
    
    

  • Administrateurs

    public void renderTileEntityArmoirePhAt(TileEntityArmoirePh te, double x, double y, double z, float tick)
    {
    GL11.glPushMatrix();
    GL11.glTranslated(x + 0.5F, y + 1.5F, z + 0.5F);
    this.bindTexture(texture);
    
    GL11.glRotatef(180.0F, -2000000.0F, 0.0F, 1.0F);
    if(te != null)
    {
    float angle = te.prevLidAngle + (te.lidAngle - te.prevLidAngle) * tick;
    angle = 1.0F - angle;
    angle = 1.0F - angle * angle * angle;
    this.mdl.porte1.rotateAngleY = (angle * (float)Math.PI / 2.0F);
    this.mdl.poignee1.rotateAngleY = (angle * (float)Math.PI / 2.0F);
    this.mdl.porte2.rotateAngleY = -(angle * (float)Math.PI / 2.0F);
    this.mdl.poignee2.rotateAngleY = -(angle * (float)Math.PI / 2.0F);
    GL11.glRotatef(90F * te.getDirection(), 0.0F, 1.0F, 0.0F);
    }
    this.mdl.render(0.0625F);
    GL11.glPopMatrix();
    }
    

    Comme ça. Le .render et le GL11.glPopMatrix(); doivent être en dernier.



  • Ah désolé, je suis un peu fatigué en ce moment 😄
    tout marche bien maintenant. Merci 🙂


  • Administrateurs

    C'est aussi de ma faute, j'ai pas précisez ou mettre le null check, j'ai ajouté une indication ;).



  • tu as monter ici comment faire un mouvement de "portière" mais pourrais tu donner d'autre exemple comme un mouvement circulaire ou un blokc qui tourne sur lui même.


  • Administrateurs

    C'est le même principe, il faut utilise pi. Regarde le code pour le deuxième bloc, l'axe centrale tourne en rond :
    https://github.com/FFMT/ModTutoriel/commit/38671521bc9b537e484cc299400cac8414fc4947
    Il me semblait avoir rédigé la deuxième partie, mais visiblement non x)



  • J'ai du mal a comprendre d'accord il faut passer par l'autre tuto mais je n'ai rien compris le rapport avec celui ci est t'il possible de faire le tuto depuis le début car la je suis completement perdu. Dommage c'est un sujet que je voulait bien comprendre


  • Administrateurs

    Heu non ça serait beaucoup trop long. Commence d'abord par avoir un bloc avec un rendu TESR fonctionnel.



  • daccord mais pour avoir le rendu TESR il faut faire tout l'autre tuto?


  • Administrateurs

    Oui. Si tu veux animer quelque chose il faut déjà avoir ce quelque chose 😉



  • Pour ouvrir le block il faut absolument avoir le gui?


  • Administrateurs

    Non, pas forcement, tu peux faire qu'il s'ouvre suite à une autre action (genre quand tu fais un clic droit ça passe une boolean et true, et du-coup ça l'ouvre).



  • Il faut mieux commencer par faire le coffre ou le rendu TESR d'abord ? car je m'embrouille un peu


  • Administrateurs

    Coffre, car sans bloc tu peux pas faire de tesr.



  • J'ai une question, comment faire pour avoir un mouvement verticale ou horizontale pour seulement une seul partie du model?


  • Administrateurs

    Il faut gérer ça au niveau du model.render() en fait ce qu'il faudrait faire, c'est exclure la partie voulu de la fonction render() et créer une autre fonction pour la partie voulu. Et du-coup tu as juste a faire une translation avec openGL avant de rendre la partie voulu.



  • J'ai mis ceci

    GL11.glPushMatrix();

    GL11.glTranslated(x, y + 1f, z);
    this.model.renderDoor_1(0.0625F);
    GL11.glPopMatrix();

    Mais la "porte" reste à la même place!