Jump to content
×
×
  • Create New...

Créer un Mod sur Minecraft


Nicosti
 Share

Recommended Posts

  • Moderator

Bonjour,

 

Voici un petit (ah ?) tutoriel qui a pour but de vous apprendre à créer un mod Minecraft relativement basique.

 

I. Quelques mises au point

 

Tout d’abord, je tiens à dire que ce tutoriel ne fera pas de vous LE super moddeur. Non il est destiné à apprendre les bases du modding comme je les ai abordées.

Ensuite, en suivant bien et en comprenant, vous devriez obtenir de bons résultats mais connaître les bases et la POO, voire plus, en Java peut s’avérer assez utile pour créer des systèmes plus élaborés.

Et finalement, je ne suis pas un expert, ni en Java ni en modding, donc toute critique (tant qu’elle est constructive), correction ou suggestion est la bienvenue.

 

II. Créer un mod avec ModLoader

 

Mise en place

Révélation

Vous aurez besoin : 
 
• De Java 
• Du JDK (Java Developement Kit) 
• Du Minecraft Coder Pack correspondant à la version de votre client.
• Du dossier « bin » de Minecraft sans modifications. Pour cela soit vous le télécharger soit vous le supprimer dans le dossier « .minecraft » et relancer Minecraft pour en obtenir un nouveau.
• Comme nous sommes dans la partie ModLoader il vous faudra télécharger ce dernier.
• D’eclipse (Choisissez Eclipse IDE for Java EE Developpers), un IDE pour Java
 
Une fois tout ceci téléchargé/installé, créer un dossier et mettez y le Minecraft Coder Pack. Celui-ci étant une archive utiliser un logiciel d’archivage (Winrar, Winzip, 7zip…) pour en extraire son contenu.
 
Allez ensuite dans le dossier jars et placez ici le dossier « bin ». Il faut maintenant installer ModLoader : pour ca aller dans « bin » ouvrez « minecraft.jar » et copier coller dedans ce qui est dans ModLoader.zip.
 
Après ca fermez « minecraft.jar », sortez du dossier jars et lancez decompile.bat. Attendez juste qu’à ce qu’il affiche « Appuyer sur une touche pour continuer…».
Remarque : Ne prêter pas attention au ERRORS FOUND, il semble qu’il soit « normal » qu’il en trouve une.
 
Comme vous pouvez voir de nouveaux dossiers ont été créés dont notamment « src », où sont placées les sources et eclipse. Allez dans ce dernier, copier en l’adresse d’emplacement et lancez eclipse lequel va vous demander je choisir un Workspace (espace de travail) : coller l’adresse et appuyer sur ok.
 
Vous devriez obtenir la même chose que sur l’image ci-dessous, il ne reste qu’à tester que ca fonctionne en lançant Minecraft via la flèche dans un rond vert.
 
301058creer-un-mod-1.png
 
Si le jeu démarre et est jouable, vous êtes maintenant prêt à passer à la suite.

 

Créer la base du mod

Révélation

Nous allons ici créer la classe principale du mod. Pour cela développer « Client » puis « src » puis « net.minecraft.src », faîtes un clic droit et sélectionner « New » puis « Class ».
 
Complétez maintenant le champ « Name » en le commençant par mod_ comme dans l’exemple ci-dessous. J’utiliserais cette même classe tout au long du tutoriel.
 
303404creer-un-mod-2.png
 
Complétez ensuite votre classe comme suit, en adaptant évidemment :
 

package net.minecraft.src;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "0.0.1";
}

public void load() 
{	

}
}


La méthode getVersion() retourne la version du mod et c’est dans la méthode load() que nous écrirons les instructions. Voilà, la classe principale de votre mod est créée. 

 

Créer un block

Révélation

Pour appuyer mes explications je vais chaque fois les développer autour d’un exemple, qui ici sera la création d’un block de minerai de saphir. 
 
Tout d’abord il va falloir déclarer le block via « ModLoader.registerBlock(nomObjetBlock) ; » puis lui ajouter un nom via « ModLoader.addName(nomObjetBlock, "Nom du block") ; » 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
     ModLoader.addName(sapphireOre, "Sapphire Ore");	
}

C’est le nom entre guillemets qui apparaitra en jeu. Pour ce qui est de nomObjetBlock, c’est un objet que nous allons maintenant créer hors de la fonction load et il va nous permettre de définir quelques premières propriétés :

 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");		
}

public static final Block sapphireOre = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

Explications :
• « public static final Block nomObjetBlock = new NomClasseDuBlock(idDuBlock, nomVariableTexture) » on instancie un objet Block nommé nomObjetBlock de type NomClasseDuBlock (classe que nous créerons plus tard) en lui indiquant entre paranthèses son ID et le nom de la variable correspondant à se texture.
• « .setBlockName("nomObjetBlock") » définit le nom « brut » du block
• « .setHardness(X.XF) » définit la résistance au « minage » du block. Pour vous donnez une idée la stone est à 1.5F, la dirt à 0.5F, le diamant à 3F, … Cette propriété n’est pas seule en cause pour définir la résistance, il y a également le « Material » que nous verrons plus tard.
• « .setResistance(X.XF) » définit la résistance aux explosions. Pour exemple l’obsidienne est à 2000F tandis que les autres blocks sont généralement inférieurs à 10F.
 
Ces propriétés sont le minimum à mettre, j’en rajouterai par la suite.
 
Il faut maintenant placer la texture du block. Pour cela rendez vous là où vous avez mit ModLoader puis allez dans le dossier « jars » et là il y a deux méthodes : 
 
La première est de mettre la texture dans une archive zip que vous placerez dans le dossier « mods » :
 
304056creer-un-mod-3.png
 
La deuxième est de mettre la texture dans « minecraft.jar » qui se trouve dans « bin » :
 
304210creer-un-mod-4.png
 
Le dossier « mod » est un dossier que j’ai créé et pour ceux qui veulent voici ma texture pour le saphir (clic droit enregistrer sous…) :
304244sapphireOre.png
 
Maintenant, déclarons la texture : 

 

	public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");		
}

private static final int sapphireOreTexture = ModLoader.addOverride("/terrain.png", "/mod/sapphireOre.png");
public static final Block sapphire = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

Remarque : La variable pour la texture doit ABSOLUMENT se trouver au dessus de l’objet.
 
« private static final int nomVariableTexture = ModLoader.addOverride("/terrain.png", "/chemin vers votre image/image.png") ; »
 
Dernière étape, il faut créer la classe correspondant à l’objet Block sapphire que vous avez instancié. Pour cela clic droit sur « net.minecraft.src », puis « New » puis « Class ». Remplissez le champ « Name » avec NomClasseDuBlock puis cliquez sur « Finish » et remplissez comme ceci : 

 

package net.minecraft.src;

public class BlockSapphireOre extends Block
{
   protected BlockSapphireOre(int par1, int par2)
   {
       super(par1, par2, Material.stone);
   }
}

Vous pouvez changez le Material.stone pour cela, supprimer le .stone et remettez un . après le Material. Eclipse va alors vous donnez la liste des possibilités.
Cette petite propriété sert à déterminer la résistance aux outils notamment. Avec Material.stone il vous faudra une pioche, avec Material.ground vous pourrez ramassez à la main, etc. A vous de faire des tests.
 
La classe du block que nous venons de créer permettra de faire des blocks plus complexes en y ajoutant des fonctions.
 

 

Créer un item

Révélation

Pour les items, il n’y a pas d’instruction équivalent à « ModLoader.registerBlock(nomObjetBlock) ; » on peut directement passer au « ModLoader.addName(nomObjetItem, "Nom de l’item") ; » :

 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");

       	ModLoader.addName(sapphire, "Sapphire");                                 
}

Et maintenant, créons la variable, et les quelques premières propriétés avec quelques adaptations :
 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");

       	ModLoader.addName(sapphire, "Sapphire");                                  
}

private static final int sapphireOreTexture = ModLoader.addOverride("/terrain.png", "/mod/sapphireOre.png");
public static final Block sapphireOre = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

public static final Item sapphire = new ItemSapphire(500).setItemName("sapphire").setIconIndex(sapphireIcon).setMaxStackSize(64);

Explications :
• « public static final Item nomObjetItem = new NomClasseItem(idItem) » on instancie un objet Item nommé nomObjetItem de type NomClasseItem en lui indiquant entre paranthèses son ID.
• « .setItemName("nomObjetBlock") » définit le nom « brut » de l’item
• « .setIconIndex(nomVariableIcon) » définit la texture de l’item. Attention ici on ne parle plus de Texture mais d’Icon.
• « .setMaxStackSize(X) » définit le nombre d’items maximum dans un « tas » ou autrement dit une case de l’inventaire.
 
Le placement de la texture est le même que pour les blocks, les deux méthodes que je vous ai expliqué plus haut fonctionnent. Passons donc à la déclaration de la texture : 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");

       	ModLoader.addName(sapphire, "Sapphire");                                  	}

private static final int sapphireOreTexture = ModLoader.addOverride("/terrain.png", "/mod/sapphireOre.png");
public static final Block sapphireOre = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

private static final int sapphireIcon = ModLoader.addOverride("/gui/items.png", "/mod/sapphire.png");
public static final Item sapphire = new ItemSapphire(500).setItemName("sapphire").setIconIndex(sapphireIcon).setMaxStackSize(64);

Même fonctionnement qu’avec les blocks juste quelques adaptations : « private static final int nomVariableIcon = ModLoader.addOverride("/gui/items.png", "/chemin vers votre image/image.png") ; »
 
Et pour ceux qui suivent mon exemple, la texture du saphir : 
314144sapphire.png
 
Créez maintenant la classe correspondante : 

 

package net.minecraft.src;

public class ItemSapphire extends Item
{
public ItemSapphire(int i) 
{
	super(i);
}
}

Ayant maintenant créé la pierre correspondant à mon minerai, pourquoi ne pas vous apprendre comment faire pour que les blocks donnent un block/objet de votre choix ? 
Nous allons faire ça grâce à la fonction idDropped que nous placerons dans la classe correspondant au block : 

 

package net.minecraft.src;

import java.util.Random;

public class BlockSapphireOre extends Block
{
   protected BlockSapphireOre(int par1, int par2)
   {
       super(par1, par2, Material.rock);
   }

   public int idDropped(int par1, Random par2Random, int par3)
   {
       return mod_Tutorial.sapphire.shiftedIndex;
   }
}

Si vous voulez que l’objet donne un Item ou un Block de base du jeu, il suffit de remplacer mod_Tutorial par Item ou Block, selon ce que vous voulez. 
Un item devra être suivit de .shiftedIndex; comme c’est le cas ici tandis qu’un block devra être suivit de .blockID;

Créer une recette

Révélation

Les recettes sont très pratiques elle vont vous permettre notamment d’obtenir rapidement vos blocks/items pour les tester (en faisant une recette 1 dirt = votre block/item). Je vais séparer les recettes en 3 types :
 Le premier : les recettes en 4 cases dans l’inventaire.
 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");

       	ModLoader.addName(sapphire, "Sapphire");

	ModLoader.addRecipe(new ItemStack(sapphire, 4), new Object[] {"#", Character.valueOf('#'), sapphireOre} );
}

Donc la première partie de l’instruction est : « ModLoader.addRecipe(new ItemStack(résultatRecette, quantité), new Object[] » Ici on définit entre les paranthèses ce que va donner la recette et le nombre qu’elle va donner.
 
La deuxième partie est celle qui définit quels blocks/items sont utilisés et dans quelles positions.
Les blocks/items sont symbolisés par des caractères dont on définit ensuite la valeur (via Character.valueOf('c'), item/blockReprésenté), une case vide est symbolisée par un espace et une rangée est représentée entre guillemets ("").
 
Autre exemple pour clarifier un peu : la recette de la table de crafting s’écrirait « ModLoader.addRecipe(new ItemStack(Block.workbench, 1), new Object[] {"##", "##", Character.valueOf('#'), Block.planks} ); »
 
Deuxième type de recette : celles avec une table de crafting. Ce sont exactement les mêmes que celles en inventaire à la différence que le craft peut se faire dans un carré de 3 au lieu d’un carré de 2. Il y a donc 3 rangées et 3 colonnes. 
Exemple avec la pioche en bois : « ModLoader.addRecipe(new ItemStack(Item.woodenPickaxe, 1), new Object[] {"###", " I ", " I ", Character.valueOf('#'), Block.planks, Character.valueOf('I'), Item.stick} ); »
 
Le dernier type que j’aborderai est la recette dans un four : 

 

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       	ModLoader.addName(sapphireOre, "Sapphire Ore");

       	ModLoader.addName(sapphire, "Sapphire");

	ModLoader.addSmelting(sapphireOre.blockID, new ItemStack(sapphire));
}

« ModLoader.addSmelting(objetACuire, new ItemStack(objetObtenu)) ; » L’objet à cuire s’il est un block devra être suivi de .blockID et si c’est un item il devra être suivi de .shiftedIndex

 

Créer une créature

Révélation

Pour commencer, nous allons créer la classe de l’entité : « EntityNomEntité ». Pour pouvoir vous expliquer autour d’un exemple, je vais également créer une entité qui sera un homme sauvage. Donc en avant pour la création de classe et des premières fonctions :

 

package net.minecraft.src;

public class EntityWildMan extends EntityCreature
{
public EntityWildMan(World par1World)
   {
       super(par1World);
       texture = "/mod/wildman.png";
       moveSpeed = 0.23F;
       tasks.addTask(0, new EntityAISwimming(this));
       tasks.addTask(1, new EntityAIPanic(this, 0.38F));
       tasks.addTask(2, new EntityAIWander(this, moveSpeed));
       tasks.addTask(3, new EntityAIWatchClosest(this, net.minecraft.src.EntityPlayer.class, 6F));

   }

   public int getMaxHealth()
   {
       return 20;
   }

   protected boolean isAIEnabled()
   {
       return true;
   }

   protected String getLivingSound()
   {
       return "random.breath";
   }

   protected String getHurtSound()
   {
       return "random.hurt";
   }

   protected String getDeathSound()
   {
       return "random.hurt";
   }

   protected int getDropItemId()
   {
       return Item.stick.shiftedIndex;
   }
}

Pour la texture c’est le même principe pour l’installer qu’avec les blacks et items et voici la mienne :
143222wildman.png
 
Explications :
• « texture = "/chemin vers votre image/image.png" ; » Chemin vers la texture de l’entité
• « moveSpeed = X.XF ; » vitesse de l’entité
• Les lignes suivantes sont des parties de l’IA de l’entité :
« EntityAISwimming(this) » permet à l’entité de savoir nager
« EntityAIPanic(this, X.XF) » l’entité va paniquer si on la tape et le nombre entre parenthèses est la vitesse à laquelle il va courir durant sa panique
« EntityAIWander(this, moveSpeed) » permet à l’entité de savoir marché à la vitesse moveSpeed
« EntityAIWatchClosest(this, chemin.vers.entité.à.regarder.class, XF) » permet à l’entité de regarder vers une autre entité à partir d’une certaine distance qui est le dernier éléments des paranthèses
• « public int getMaxHealth() » cette fonction retourne la vie maximum de l’entité
• « protected boolean isAIEnabled() » cette fonction retourne si l’IA est activée (true) ou pas (false)
• « protected getLivingSound() » cette fonction retourne le son que l’entité fera tant qu’elle est en vie
• « protected getHurtSound() » cette fonction retourne le son que fera l’entité quand elle prend des dommages
• « protected getDeathSound() » cette fonction retourne le son que fera l’entité si elle meurt
• « protected int getDropItemId() » cette fonction retourne l’objet que l’entité laissera à sa mort
 
Pour les sons, ici, random est un dossier dans jars\resources\newsound. A vous de choisir vos sons et bien en mettre le chemin.
 
Passons maintenant à sa modélisation. Tout d’abord créez la classe, pour moi ModelWildMan, ensuite selon la créature que vous allez créer vous pouvez choisir entre les Model déjà existants qui se trouvent dans net.minecraft.src et reprendre la fonction public void setRotationAngles. Ici je vais reprendre la fonction de ModelZombie pour avoir une apparence humaine et ca donne ça :

 

package net.minecraft.src;

public class ModelWildMan extends ModelBiped
{
   public ModelWildMan()
   {
   }

   public void setRotationAngles(float par1, float par2, float par3, float par4, float par5, float par6)
   {
       super.setRotationAngles(par1, par2, par3, par4, par5, par6);
       float f = MathHelper.sin(onGround * (float)Math.PI);
       float f1 = MathHelper.sin((1.0F - (1.0F - onGround) * (1.0F - onGround)) * (float)Math.PI);
       bipedRightArm.rotateAngleZ = 0.0F;
       bipedLeftArm.rotateAngleZ = 0.0F;
       bipedRightArm.rotateAngleY = -(0.1F - f * 0.6F);
       bipedLeftArm.rotateAngleY = 0.1F - f * 0.6F;
       bipedRightArm.rotateAngleX = -((float)Math.PI / 2F);
       bipedLeftArm.rotateAngleX = -((float)Math.PI / 2F);
       bipedRightArm.rotateAngleX -= f * 1.2F - f1 * 0.4F;
       bipedLeftArm.rotateAngleX -= f * 1.2F - f1 * 0.4F;
       bipedRightArm.rotateAngleZ += MathHelper.cos(par3 * 0.09F) * 0.05F + 0.05F;
       bipedLeftArm.rotateAngleZ -= MathHelper.cos(par3 * 0.09F) * 0.05F + 0.05F;
       bipedRightArm.rotateAngleX += MathHelper.sin(par3 * 0.067F) * 0.05F;
       bipedLeftArm.rotateAngleX -= MathHelper.sin(par3 * 0.067F) * 0.05F;
   }
}

Mais bon, ca fait pas très humain de se balader avec les bras tendus donc voici la fonction setRotationAngles pour avoir les bras le long du corps et qu’ils bougent comme votre personnage :

 

public void setRotationAngles(float f, float f1, float f2, float f3, float f4, float f5)
   {
       bipedHead.rotateAngleY = f3 / 57.29578F;
       bipedHead.rotateAngleX = f4 / 57.29578F;
       bipedHeadwear.rotateAngleY = bipedHead.rotateAngleY;
       bipedHeadwear.rotateAngleX = bipedHead.rotateAngleX;
       bipedRightArm.rotateAngleX = MathHelper.cos(f * 0.6662F + 3.141593F) * 2.0F * f1 * 0.5F;
       bipedLeftArm.rotateAngleX = MathHelper.cos(f * 0.6662F) * 2.0F * f1 * 0.5F;
       bipedRightArm.rotateAngleZ = 0.0F;
       bipedLeftArm.rotateAngleZ = 0.0F;
       bipedRightLeg.rotateAngleX = MathHelper.cos(f * 0.6662F) * 1.4F * f1;
       bipedLeftLeg.rotateAngleX = MathHelper.cos(f * 0.6662F + 3.141593F) * 1.4F * f1;
       bipedRightLeg.rotateAngleY = 0.0F;
       bipedLeftLeg.rotateAngleY = 0.0F;
       bipedRightArm.rotateAngleY = 0.0F;
       bipedLeftArm.rotateAngleY = 0.0F;
       bipedRightArm.rotateAngleZ += MathHelper.cos(f2 * 0.09F) * 0.05F + 0.05F;
       bipedLeftArm.rotateAngleZ -= MathHelper.cos(f2 * 0.09F) * 0.05F + 0.05F;
       bipedRightArm.rotateAngleX += MathHelper.sin(f2 * 0.067F) * 0.05F;
       bipedLeftArm.rotateAngleX -= MathHelper.sin(f2 * 0.067F) * 0.05F;
   }

Pour ce qui est des modélisations personnalisées, je ferais plus tard un partie sur l’utilisation d’un logiciel prévu pour (Techne pour ceux qui voudraient déjà y regarder par eux-mêmes). Il reste une dernière classe à créer, le Render, ici RenderWildMan.

 

package net.minecraft.src;

public class RenderWildMan extends RenderLiving
{
   public RenderWildMan(ModelBase par1ModelBase, float par2)
   {
       super(par1ModelBase, par2);
   }

   public void renderWildMan(EntityWildMan par1EntityWildMan, double par2, double par4, double par6, float par8, float par9)
   {
       super.doRenderLiving(par1EntityWildMan, par2, par4, par6, par8, par9);
   }

   public void doRenderLiving(EntityLiving par1EntityLiving, double par2, double par4, double par6, float par8, float par9)
   {
       renderWildMan((EntityWildMan)par1EntityLiving, par2, par4, par6, par8, par9);
   }

   public void doRender(Entity par1Entity, double par2, double par4, double par6, float par8, float par9)
   {
       renderWildMan((EntityWildMan)par1Entity, par2, par4, par6, par8, par9);
   }
}

Dernière étape, tout déclarer dans la classe principale du mod :

package net.minecraft.src;

import java.util.Map;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "0.0.1";
}

public void load() 
{	
	ModLoader.registerEntityID(EntityWildMan.class, "Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityWildMan.class, 1, 1, 1, EnumCreatureType.creature);                                             		
}

public void addRenderer(Map map)
{
	map.put(EntityWildMan.class, new RenderWildMan(new ModelWildMan(), 0.5F));
}	
}

Explications :
• « ModLoader.registerEntityID(Entity.class, "Nom de l’entité", ID); » On enregistre l’entité via un nom et un ID. Pour l’ID vous pouvez mettre le nombre que vous voulez (tant qu’il n’est pas déjà utilisé) ou utiliser la fonction ModLoader.getUniqueID() qui va choisir un ID inutilisé pour vous. 
• « ModLoader.addSpawn(Entity.class, rareté, nombreMinimum, nombreMaximum, EnumCreatureType.type); » On ajoute le spawn de l’entité on définit la rareté du spawn (1 étant plus rare que 1000), le nombre minimum qu’il en spawnera en coup, le nombre maximum qu’il en spawnera en un coup et son type à savoir .creature pour une creature amicale, .monster pour un monstre et .waterCreature pour une creature aquatique.
 
Vous devez également créer la fonction public void addRenderer via laquelle vous allez mettre en relation les différentes parties de votre entité :
« map.put(Entity.class, new RenderEntity(new ModelEntity(), 0.5F)); » où le 0.5F définit la taille de l’ombre sous l’entité.
 
Remarque : Il faut importer Map (import java.util.Map)

 

Créer un monstre

Révélation

Pour créer un monstre, le principe est le même que pour les créatures (et les classes aussi d’ailleurs), il a juste une partie du .addSpawn qui change et il faut rajouter quelques éléments à la classe de l’entité. En avant, donc pour un mob agressif très célèbre : 
225405herobrine.png

 

package net.minecraft.src;

public class EntityHerobrine extends EntityMob
{
public EntityHerobrine(World par1World)
   {
       super(par1World);
       texture = "/mod/herobrine.png";
       moveSpeed = 0.45F;
       attackStrength = 8;

       tasks.addTask(0, new EntityAISwimming(this));
       tasks.addTask(2, new EntityAIAttackOnCollide(this, net.minecraft.src.EntityPlayer.class, moveSpeed, false));
       tasks.addTask(3, new EntityAIAttackOnCollide(this, net.minecraft.src.EntityWildMan.class, moveSpeed, true));
       tasks.addTask(4, new EntityAIWander(this, moveSpeed));
       tasks.addTask(5, new EntityAIWatchClosest(this, net.minecraft.src.EntityPlayer.class, 8F));
       targetTasks.addTask(1, new EntityAIHurtByTarget(this, false));
       targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, net.minecraft.src.EntityPlayer.class, 16F, 0, true));
       targetTasks.addTask(2, new EntityAINearestAttackableTarget(this, net.minecraft.src.EntityWildMan.class, 16F, 0, false));
   }

public void onLivingUpdate()
   {
       super.onLivingUpdate();
   }

public EnumCreatureAttribute getCreatureAttribute()
   {
       return EnumCreatureAttribute.UNDEFINED;
   }

   public int getMaxHealth()
   {
       return 20;
   }

   protected boolean isAIEnabled()
   {
       return true;
   }

   protected String getHurtSound()
   {
       return "random.hurt";
   }

   protected String getDeathSound()
   {
       return "random.hurt";
   }

   protected int getDropItemId()
   {
       return Item.stick.shiftedIndex;
   }
}

Explications :
• « attackStrenght = X ; » définit la puissance d’attaque du monstre
• Passons maintenant aux éléments d’IA :
« AIAttackOnCollide(this, Entity.class, moveSpeed, true) » permet à l’entité d’attaquer l’entité qui l’attaque (on peut comme j’ai fait ici mettre plusieurs entités et pas forcément le joueur)
« AIHurtByTarget(this,false) » cette partie de l’IA est complémentaire de AIAttackOnCollide : AIHurtByTarget va analyser quelle entité heurte le monstre et si c’est une entité déclarée avec AIAttackOnCollide le monstre va attaquer
• « AINearestAttackableTarget » détecte une entité à attquer dans un rayon (ou diametre) de XF (ici aussi on peut mettre plusieurs entités et pas forcément le joueur)
• « public void onLivingUpdate() » cette fonction s’actualise tant que l’entité est en vie. C’est cette fonction qui fait prendre feu aux zombies et squelettes durant le jour :

 

public void onLivingUpdate()
   {
       if (worldObj.isDaytime() && !worldObj.isRemote)
       {
           float f = getBrightness(1.0F);

           if (f > 0.5F && worldObj.canBlockSeeTheSky(MathHelper.floor_double(posX), MathHelper.floor_double(posY), MathHelper.floor_double(posZ)) && rand.nextFloat() * 30F < (f - 0.4F) * 2.0F)
           {
               setFire(8);
           }
       }

       super.onLivingUpdate();
   }

• « public EnumCreatureAttribute getCreatureAttribute() » cette fonction retourne la type de la créature, ce qui permet à l’entité de réagir face à certains enchantements .UNDEFINED pour indéfini .UNDEAD pour les morts-vivants .ARTHROPOD pour les arthropodes (http://fr.wikipedia.org/wiki/Arthropode)
 
Remarque : la classe n’est plus étendue à EntityCreature mais à EntityMob
 
Pour ce qui est de RenderHerobrine et ModelHerobrine remplissez les exactement comme pour une créature. Et maintenant reste à enregistrer le monstre et lui définir un spawn :

 

package net.minecraft.src;

import java.util.Map;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "0.0.1";
}

public void load() 
{	
	ModLoader.registerEntityID(EntityHerobrine.class, "Herobrine", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityHerobrine.class, 1000, 1, 1, EnumCreatureType.monster);
}

public void addRenderer(Map map)
{
	map.put(HerobrineMan.class, new RenderBiped(new ModelWildMan(), 0.5F));
}
}

Ici il n’y a qu’une seule chose qui change par rapport aux créatures : le EnumCreatureType doit être .monster.
Comme vous pouvez le constatez j’ai réutilisé la modélisation de l’homme sauvage pour Herobrine, ce qui peut être pratique si vous faites des monstres/creatures ayant la même modélisation : si comme ici vous comptez faire seulement des créatures humanoïdes, vous n’avez qu’a faire un ModelHuman et relier toutes vos créatures à cette unique modélisation

 

 

Créer un combustible

Révélation

Avant de commencer, qu’est ce que j’entends par combustible ? Un bloc ou un item qui va permettre d’alimenter un four.
 C’est assez simple à faire, nous n’allons utiliser qu’une seule fonction. Pour illustrer, je vais faire un combustible à partir des feuilles, qui après tout brûlent tout autant que du bois ou des petits arbres. Here we go :

 

package net.minecraft.src;

import java.util.Map;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "1.0.0.0";
}

public void load() 
{
}

public int addFuel(int i, int j)
{
	if(i == Block.leaves.blockID)
	{
		return 100;
	}
	else
	{
		return 0;
	}		
}
} 

Explication :
• Comme je le disait, une seule fonction est requise : « public int addFuel(int i, int j) » : cette fonction retourne la durabilité de la flamme pour un bloc/item d’id i. 
 
Cette fonction sera appelée lorsque l’on introduira un item ou un bloc dans la zone réservée aux combustibles du four afin de voir si le bloc/item introduit produit une flamme.
 
Donc pour ajouter un combustible, il faut ajouter une condition dans la fonction : if(i == idDeVotreCombustible). 
 
Ensuite, il faut retourner la durabilité de la flamme. Il faut une durabilité de 200 pour cuire/fondre entièrement un bloc/item. 
 
Remarque : On peut évidemment mettre des valeurs inférieures à 200 comme ici 100, où il faudra donc 2 blocs de feuilles pour cuire/fondre un bloc/item ou même 50 pour que la cuisson nécessite 4 blocs, etc.
 
Ce qui donne, dans notre cas : 

 

if(i == Block.leaves.blockID)
	{
		return 100;
	}

Remarque : N’oubliez pas de mettre le else{return 0;} sinon vous aurez une erreur.

 

III. "Exporter" son mod

Révélation

Une fois votre mod fini et testé, retournez dans le dossier où vous avez placé et extrait le MCP.

 

Là, lancez recompile.bat et attendez jusqu’à ce que « Appuyez sur une touche pour continuer… » s’affiche puis lancez reobfuscate.bat et attendez une nouvelle fois d’avoir « Appuyer sur une touche pour continuer… »

 

Rendez-vous ensuite dans le dossier « reobf » puis « minecraft » : Toutes les nouvelles classes et classes que vous aurez modifié se trouveront dans ce dossier.

 

Créez ensuite une archive zip et placez ces classes dedans.

 

Remarque : Il faut également placer les images que votre mod utilisera dans l’archive ! Un mod ne peut fonctionner correctement sans ses images !

 

271457creer-un-mod-5.png

 

Voilà, votre mod est exporté.

 

Au niveau de l’installation de votre mod, il faut :

 

1) Installer ModLoader sur votre client normal

2) Installer votre mod. Vous pouvez l’installer de deux manières : Soit vous placez l’archive que vous venez de créer dans le dossier « mods » de votre « .minecraft » soit vous copiez le contenu de cette archive dans votre minecraft.jar

 

Précisez également ces étapes aux personnes à qui vous distribuerez votre mod.

 

IV. Organiser un peu son code

Révélation

Vous l’avez sans doute remarqué, dès qu’on ajoute un certain nombre d’éléments à un mod, le code devient vite assez désorganisé. Dans cette partie, je vais donc vous expliquer comment faire pour que le code de la classe principale de votre mod soit plus agréable, plus propre au niveau de la fonction load() et des déclarations de variables.

 

Commençons avec la méthode load() : le principe pour mieux organiser l’intérieur de cette fonction est de créer d’autres fonctions où les ajouts seront classés.

 

Donc en dehors de notre fonction load() créons d’autres fonctions :

• private void addBlocks() : où nous placerons les déclarations relatives aux blocs.

• private void addItems() : où nous placerons les déclarations relatives aux items.

• private void addRecipes() : où nous placerons les déclarations relatives aux recettes.

• private void addEntitiesAndSpawns() : où nous placerons les déclarations relatives aux entités et à leur spawn.

 

Remarque : Vous pouvez créer d’autres fonctions si vous le souhaitez comme addSmelting() ou addSpawn() plutôt que de réunir comme moi avec respectivement les recettes et les entités. Il faut juste mettre private void leNomDeVotreFonction() pour créer votre propre fonction.

 

Ensuite il va falloir dire à notre fonction load() de charger les autres fonctions que nous venons de créer. Pour ca rien de plus simple, il suffit d’écrire le nom de la fonction suivit d’un ; dans la fonction load().

 

Voyons maintenant ce que tout ca nous donne sur un point de vue plus pratique. On passe de ça :

package net.minecraft.src;

import java.util.Map;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "1.0.0.0";
}

public void load() 
{	
	ModLoader.registerBlock(sapphireOre);
       ModLoader.addName(sapphireOre, "Sapphire Ore");

       ModLoader.addName(sapphire, "Sapphire");   

       ModLoader.registerBlock(blueFlower);
       ModLoader.addName(blueFlower, "Blue Flower");

       ModLoader.addRecipe(new ItemStack(sapphireOre, 4), new Object[]
                           {"#", Character.valueOf('#'), Block.dirt} );

       ModLoader.addRecipe(new ItemStack(blueFlower, 10), new Object[]
                           {"#", Character.valueOf('#'), Block.grass} );


       ModLoader.addSmelting(sapphireOre.blockID, new ItemStack(sapphire));

       ModLoader.registerEntityID(EntityWildMan.class, "Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityWildMan.class, 1000, 1, 1, EnumCreatureType.creature);

       ModLoader.registerEntityID(EntityAgressiveWildMan.class, "Agressive Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityAgressiveWildMan.class, 1000, 1, 1, EnumCreatureType.monster);
   }

public void addRenderer(Map map)
{
	map.put(EntityWildMan.class, new RenderWildMan(new ModelWildMan(), 0.5F));
	map.put(EntityAgressiveWildMan.class, new RenderBiped(new ModelWildMan(), 0.5F));
}

public int addFuel(int i, int j)
{
	if(i == Block.leaves.blockID)
	{
		System.out.println(j);
		return 50;
	}else if(i == 150)
	{
		return 12800;
	}
	return 0;
}

private static final int sapphireOreTexture = ModLoader.addOverride("/terrain.png", "/mod/sapphireOre.png");
public static final Block sapphireOre = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

private static final int blueFlowerTexture = ModLoader.addOverride("/terrain.png", "/mod/blueFlower2.png");
public static final Block blueFlower = new BlockBlueFlower(152, blueFlowerTexture).setBlockName("blueFlower").setHardness(0.0F);

private static final int sapphireIcon = ModLoader.addOverride("/gui/items.png", "/mod/sapphire.png");
public static final Item sapphire = new ItemSapphire(500).setItemName("sapphire").setIconIndex(sapphireIcon).setMaxStackSize(64);
}

A ça :

 

package net.minecraft.src;

import java.util.Map;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "1.0.0.0";
}

public void load() 
{
	addBlocks();
	addItems();
	addRecipes();
	addEntitiesAndSpawns(); 
}

private void addBlocks()
{
	ModLoader.registerBlock(sapphireOre);
       ModLoader.addName(sapphireOre, "Sapphire Ore");

       ModLoader.registerBlock(blueFlower);
       ModLoader.addName(blueFlower, "Blue Flower");        
}

private void addItems()
{
	ModLoader.addName(sapphire, "Sapphire");        
}

private void addRecipes()
{
	ModLoader.addRecipe(new ItemStack(sapphireOre, 4), new Object[]
	                     {"#", Character.valueOf('#'), Block.dirt} );

	ModLoader.addRecipe(new ItemStack(blueFlower, 10), new Object[]
	                     {"#", Character.valueOf('#'), Block.grass} );		                                          

	ModLoader.addSmelting(sapphireOre.blockID, new ItemStack(sapphire));
}

private void addEntitiesAndSpawns()
{
	ModLoader.registerEntityID(EntityWildMan.class, "Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityWildMan.class, 1000, 1, 1, EnumCreatureType.creature);

       ModLoader.registerEntityID(EntityAgressiveWildMan.class, "Agressive Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityAgressiveWildMan.class, 1000, 1, 1, EnumCreatureType.monster);
   }

public void addRenderer(Map map)
{
	map.put(EntityWildMan.class, new RenderWildMan(new ModelWildMan(), 0.5F));
	map.put(EntityAgressiveWildMan.class, new RenderBiped(new ModelWildMan(), 0.5F));
}

public int addFuel(int i, int j)
{
	if(i == Block.leaves.blockID)
	{
		System.out.println(j);
		return 50;
	}else if(i == 150)
	{
		return 12800;
	}
	return 0;
}

private static final int sapphireOreTexture = ModLoader.addOverride("/terrain.png", "/mod/sapphireOre.png");
public static final Block sapphireOre = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

private static final int blueFlowerTexture = ModLoader.addOverride("/terrain.png", "/mod/blueFlower2.png");
public static final Block blueFlower = new BlockBlueFlower(152, blueFlowerTexture).setBlockName("blueFlower").setHardness(0.0F);

private static final int sapphireIcon = ModLoader.addOverride("/gui/items.png", "/mod/sapphire.png");
public static final Item sapphire = new ItemSapphire(500).setItemName("sapphire").setIconIndex(sapphireIcon).setMaxStackSize(64);

Intéressons nous maintenant aux déclarations de variables. Ici encore, le principe est d’essayer de trier en les mettant dans des catégories. Cette fois-ci, nous allons utiliser les commentaires.

 

Pour faire un commentaire, il faut placer un double slash « // » au début de la ligne et ce qui sera écrit derrière ne sera pas interprété comme faisant partie du code. On peut donc y écrire des indications qui seront ici les catégories auxquelles appartiennent les variables :

 

// Blocks =====================

// Items ======================

Cette réorganisation n’est pas frappante mais peut aider à se retrouver. Au final ces petites réorganisations nous donnent :

 

package net.minecraft.src;

import java.util.Map;

public class mod_Tutorial extends BaseMod
{
public String getVersion() 
{
	return "1.0.0.0";
}

public void load() 
{
	addBlocks();
	addItems();
	addRecipes();
	addEntitiesAndSpawns(); 
}

private void addBlocks()
{
	ModLoader.registerBlock(sapphireOre);
       ModLoader.addName(sapphireOre, "Sapphire Ore");

       ModLoader.registerBlock(blueFlower);
       ModLoader.addName(blueFlower, "Blue Flower");        
}

private void addItems()
{
	ModLoader.addName(sapphire, "Sapphire");        
}

private void addRecipes()
{
	ModLoader.addRecipe(new ItemStack(sapphireOre, 4), new Object[]
	                     {"#", Character.valueOf('#'), Block.dirt} );

	ModLoader.addRecipe(new ItemStack(blueFlower, 10), new Object[]
	                     {"#", Character.valueOf('#'), Block.grass} );		                                          

	ModLoader.addSmelting(sapphireOre.blockID, new ItemStack(sapphire));
}

private void addEntitiesAndSpawns()
{
	ModLoader.registerEntityID(EntityWildMan.class, "Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityWildMan.class, 1000, 1, 1, EnumCreatureType.creature);

       ModLoader.registerEntityID(EntityAgressiveWildMan.class, "Agressive Wild Man", ModLoader.getUniqueEntityId());
       ModLoader.addSpawn(EntityAgressiveWildMan.class, 1000, 1, 1, EnumCreatureType.monster);
   }

public void addRenderer(Map map)
{
	map.put(EntityWildMan.class, new RenderWildMan(new ModelWildMan(), 0.5F));
	map.put(EntityAgressiveWildMan.class, new RenderBiped(new ModelWildMan(), 0.5F));
}

public int addFuel(int i, int j)
{
	if(i == Block.leaves.blockID)
	{
		System.out.println(j);
		return 50;
	}else if(i == 150)
	{
		return 12800;
	}
	return 0;
}

// Blocks =====================

private static final int sapphireOreTexture = ModLoader.addOverride("/terrain.png", "/mod/sapphireOre.png");
public static final Block sapphireOre = new BlockSapphireOre(150, sapphireOreTexture).setBlockName("sapphireOre").setHardness(1.5F).setResistance(5F);

private static final int blueFlowerTexture = ModLoader.addOverride("/terrain.png", "/mod/blueFlower2.png");
public static final Block blueFlower = new BlockBlueFlower(152, blueFlowerTexture).setBlockName("blueFlower").setHardness(0.0F);

// Items ======================

private static final int sapphireIcon = ModLoader.addOverride("/gui/items.png", "/mod/sapphire.png");
public static final Item sapphire = new ItemSapphire(500).setItemName("sapphire").setIconIndex(sapphireIcon).setMaxStackSize(64);

 

 

Je rappelle que toute suggestion pour des explications sur des choses plus complexes ou plus approfondie sur ce qui est déjà fait sont les bienvenues et que toute critique/correction constructive aussi.

  • J'adore 4

java style =)

Link to comment
Share on other sites

  • Replies 28
  • Created
  • Last Reply

Top Posters In This Topic

  • 2 weeks later...

Très bon tutoriel d'un point de vue quantitatif et qualitatif.

 

En revanche essaye de le rendre plus simple à lire en utilisant des balises "BBCodes" comme les listes ou encore l'affichage de différentes couleurs ou de différentes tailles :)

XIII

Link to comment
Share on other sites

  • 2 weeks later...
  • 6 months later...
  • Moderator

Tu reprend ce post?

Merci!

 

A vrai dire je sais pas. Il faudrait voir si des gens sont vraiment intéressés.

 

Moi je peux encore apprendre 2-3 trucs à ceux qui veulent ou alors voir comment faire des cas particuliers mais pour ca il faut me faire des suggestions. Le modding c'est large :P

java style =)

Link to comment
Share on other sites

J'aimerais savoir comment cela marche avec les différentes versions de minecraft ? Puisque, quand minecraft change de version les mods doivent être modifiés aussi, non ? Alors, comment on fait ? A part si j'ai mal lu... :)

Link to comment
Share on other sites

  • Moderator

Bas ce que j'aimerai bien faire c'est créer une nouvelle dimension avec c'est propre caractéristique et sont propre moyen d'y accédé :D.

 

Ahah, du lourd tout de suite :D

 

J'y avais déjà regardé : malheureusement il faudrait DimensionAPI, mais celui ci n'a pas été adapté à la 1.4.X

 

Je vais voir si je peux trouver une autre API pour les dimensions

 

J'aimerais savoir comment cela marche avec les différentes versions de minecraft ? Puisque, quand minecraft change de version les mods doivent être modifiés aussi, non ? Alors, comment on fait ? A part si j'ai mal lu... :)

 

Les mods ne doivent pas spécialement être modifiés. En fait le code de Minecraft est obfusqué, afin de le protéger. Mais pour chaque version, l'obfuscation change. Il faut donc recompiler son mod avec la nouvelle obfuscation.

 

Ca doit pas être très clair donc petit exemple : pour mon block j'utilise la fonction .setName(). Dans une version, cette fonction serait obfusquée en .aa() mais dans une autre version .setName() pourrait être obfusqué en .zf().

Donc si je remet mon mod sur cette nouvelle version mon block va appeler la fonction .aa() qui ne sera plus ma fonction .setName() mais une fonction totalement différente de celle voulue d'où erreurs et plantage

java style =)

Link to comment
Share on other sites

  • 3 weeks later...
  • Moderator

Bon alors, j'ai un peu regardé mais je n'ai pas vu d'API pour les dimensions plus ou moins équivalente à DimensionAPI. Apparemment pour les dimensions mieux vaudrait faire ca avec Forge, mais je n'ai encore jamais touché à Forge donc la dessus je ne saurais pas t'aider.

 

Sinon je pourrais encore vous apprendre 2-3 trucs intéressants :

* Comment générer aléatoiremment des structures dans le monde

* Comment générer des minerais dans le sol

* Comment faire un animal domesticable

* Comment faire un combustible

 

Voire plus mais la, j'ai pas vraiment d'idées. Qu'en dites vous ?

java style =)

Link to comment
Share on other sites

  • Moderator

Le dernier mod que j'ai créé, qui était d'ailleurs le seul que j'avais rendu public, Mojang me l'a piqué donc sur le coup je suis assez démotivé. Cela dit j'ai une petite idée derrière la tête que je vais peut-etre développer.

 

Bref revenons en au sujet : j'aimerais savoir si ca vaut la peine que je continue le tutoriel sur les quelques points cités plus haut. Oui ou non ?

java style =)

Link to comment
Share on other sites

  • Moderator

Les structure et les animaux domestique serait intéressant :D

J'épingle le post car il mérite :D.

 

*.* c'est trop d'honneur merci :)

 

Bon ben alors j'aborderai ces quelques points dès que j'aurai un peu de temps

java style =)

Link to comment
Share on other sites

  • 3 weeks later...
  • Moderator

Partie comment créer un combustible ajoutée.

 

Il est possible que ce ne sois pas super clair, j'ai eu assez de mal à écrire cette partie. Si cette partie pose problème, dites-moi où et j'améliorerai le contenu.

java style =)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share



Important Information

Terms of Use / Privacy Policy / Guidelines / We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.