Créer un mob basique
-
Ce tutoriel est également disponible en vidéo.
Sommaire
Introduction
Une entité est quelque chose qui peut se déplacer dans le monde (ou pas pour certaines). Elles peuvent spawn et mourir. Il existe plusieurs types d’entités, les EntityItem qui servent pour les items au sol, les minecart et les bateaux, les tableaux et les items frames, les tnt activées, et de nombreuses autres sont des entités non vivantes (même si elles ont aussi des points de vie et une fonction setDeath()). Ensuite il y a les EntityLiving, qui héritent de EntityLivingBase tout comme les joueurs qui sont des entités vivantes. Les pnj, les mobs agressifs ou passifs héritent tous d’EntityLiving. Toutes les entités vivantes ont une IA (sauf le joueur, puisque l’IA qu’il possède est la votre :p) c’est ce qui différencie les entités vivantes des autres. Comme il faut bien commencer par quelque chose de simple nous allons créer une entité vivante agressive (EntityMob).
Pré-requis
Code
La classe de l’entité :
Commencez par créer une nouvelle classe dans votre package common, (clic droit, new class). Dans Superclass, mettez : net.minecraft.entity.monster.EntityMob
Vous allez avoir une erreur sur la classe, c’est normal, il faut ajouter un constructeur. Passer la souris sur l’erreur, et faites “Add constructor nom de votre class(world)”. Changez le nom du paramètre si vous le souhaitez, personnellement je préfère world plutôt que par1World. La classe du mob ressemble à ça :package fr.minecraftforgefrance.tutoriel.common; import net.minecraft.entity.monster.EntityMob; import net.minecraft.world.World; public class EntityMobTutoriel extends EntityMob { public EntityMobTutoriel(World world) { super(world); } }
Ajoutez ensuite cette fonction :
public void applyEntityAttributes() { super.applyEntityAttributes(); // la suite va ici }
Elle va permettre d’appliquer les attributs, ces derniers gèrent : la vie, la force d’attaque, la vitesse de déplacement, la résistance à l’enchantement recul et la distance de détection d’autres entités.
En dessous ou à la place du commentaire “la suite va ici” mettez :this.getEntityAttribute(SharedMonsterAttributes.maxHealth).setBaseValue(20D);
Cela définira la vie par défaut. Elle est exprimée en demi-cœurs, donc 20D = 10 cœurs. Si vous ne mettez pas cette fonction, la vie sera 20 par défaut.
Quelques autres fonctions que vous pouvez mettre à la suite :this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6D); this.getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValue(1D); this.getEntityAttribute(SharedMonsterAttributes.movementSpeed).setBaseValue(0.8D);
attackDamage = nombre de dégât, si vous ne mettez pas cette fonction, la valeur sera 1D.
knockbackResistance = la résistance à l’enchantement recul, 0D si vous ne mettez pas cette fonction.
movementSpeed = la vitesse de déplacement, 0.699999988079071D si vous ne mettez pas cette fonction.Voilà, c’est tout pour la classe de l’entité.
Le rendu :
Dans le package client, créez une nouvelle classe nommée “Render<le nom de votre mod>”, mettez net.minecraft.client.renderer.entity.RenderBiped en superClass.
À nouveau vous allez avoir une erreur sur la classe, il faut ajouter un constructeur (prenez le premier de la liste : ModelBiped,float)
Une fois de plus, pour une question de lisibilité du code, je vais changer les noms des paramètres. Ce qui donne :package fr.minecraftforgefrance.tutoriel.client; import net.minecraft.client.model.ModelBiped; import net.minecraft.client.renderer.entity.RenderBiped; public class RenderMobTutoriel extends RenderBiped { // ici la déclaration du ResourceLocation public RenderMobTutoriel(ModelBiped model, float shadow) { super(model, shadow); } // ici les méthodes }
À la place de la déclaration du ResourceLocation, mettez :
public final ResourceLocation texture = new ResourceLocation(ModTutoriel.MODID, "textures/entity/mob_tutoriel.png");
Ce field va définir la texture, si vous n’avez pas fait de field MODID dans votre classe principale comme montré dans le tutoriel sur les items, remplacez directement par votre modid :
public final ResourceLocation texture = new ResourceLocation("tutoriel", "textures/entity/mob_tutoriel.png");
Ma texture sera dans forge/src/main/resources/assets/tutoriel/textures/entity/mob_tutoriel.png.
Attention, contrairement aux blocs et aux items, il faut mettre le chemin complet et le .png.Maintenant nous allons ajouter deux méthodes pour indiquer au rendu qu’il faut utiliser pour cette texture :
protected ResourceLocation getEntityTexture(EntityLiving living) { return this.getMobTutorielTexture((EntityMobTutoriel)living); } private ResourceLocation getMobTutorielTexture(EntityMobTutoriel mobTutoriel) { return texture; }
Vous pouvez appeler la fonction getMobTutorielTexture comme vous le souhaitez, ça n’a pas d’importance, il faut juste que la classe de votre mod soit en argument. EntityMobTutoriel correspond donc à la classe de votre mob.
La classe principale :
Votre mob ne risque pas de fonctionner s’il est enregistré nulle part.
Dans la classe principale, dans la fonction init, ajoutez :EntityRegistry.registerGlobalEntityID(EntityMobTutoriel.class, "mobTutoriel", EntityRegistry.findGlobalUniqueEntityId(), new Color(0, 255, 0).getRGB(), new Color(255, 0, 0).getRGB()); EntityRegistry.registerModEntity(EntityMobTutoriel.class, "mobTutoriel", 420, this.instance, 40, 1, true);
La première ligne enregistre l’entité dans le système global de minecraft. EntityMobTutoriel.class est la classe de mon mob, “mobTutoriel” le nom non localisé de l’entité, EntityRegistry.findGlobalUniqueEntityId() sert à avoir un id libre dans le système global de minecraft, et les deux derniers arguments sont pour la couleur du bas et du haut de l’œuf de votre mob. Vous pouvez utiliser directement un int, mais il est plus simple d’utiliser new Color(rouge, vert, bleu).getRGB(). Cependant, n’oubliez pas d’importer Color en plus de EntityRegistry (ctrl + shift + o).
La deuxième enregistre le mob dans FML. le première argument est à nouveau la classe du mob, le second à nouveau le nom non localisé, le troisième est l’id du mob, this.instance est l’instance du mod, si vous enregistrez votre mob à un autre endroit que dans votre classe principale, il faut remplacer par NomDeLaClassePrincipale.instance. Vous pouvez mettre juste this si l’enregistrement est dans la classe principale. 40 est un rayon autour du mob qui définit tous les joueurs qui doivent avoir les informations de ce mob. Ne mettez pas une valeur trop petite, sinon vous devrez vous approcher de très près du mob pour avoir ses données (je n’ai pas testé, mais a mon avis vous risquer de ne pas le voir de loin). 1 correspond à la vitesse de tick, avec 1 le mob est tické à chaque tick, avec 20 tous les 20 ticks, donc une fois par seconde. Le dernier boolean sert pour les paquets, mettez-le sur true.
Voilà, notre mob est enregistré, dans la foulée nous allons lui donner un nom.
Dans le fichier lang (forge/src/main/resources/assets/votre id de mod/lang/en_US.lang et fr_FR.lang et éventuellement d’autres, ajoutez :entity.nom non localisé du mob.name=Nom localisé
Dans mon cas :
entity.mobTutoriel.name=Tutorial Mob
Le proxy client :
Il ne reste plus qu’à enregistrer le rendu, ce qui se fait dans le client proxy puisque les rendus ne se font que dans le client.
Dans la méthode registerRender de votre client proxy, ajoutez :RenderingRegistry.registerEntityRenderingHandler(EntityMobTutoriel.class, new RenderMobTutoriel(new ModelBiped(), 0.5F));
EntityMobTutoriel.class correspond à la classe de mon entité,
RenderMobTutoriel à la classe de mon render
new ModelBiped() à une nouvelle instance de ModelBiped (nous verrons plus tard comment créer son propre modèle).
0.5F est la taille de l’ombre. Et voilà, le mob est opérationnel !Bonus
Retournez dans la classe de votre mob, et ajoutez cette fonction :
public Item getDropItem() { return Item.getItemFromBlock(ModTutoriel.blockTutoriel); }
Elle va définir le drop du mob lors de sa mort. Pour drop, mettez :
- Pour un item de votre mod : ClassePrincipale.field_de_l’item (le field est le nom dans public static Item nom;)
- Pour un bloc de votre mod : Item.getItemFromBlock(ClassePrincipale.field_du_bloc) (le field est le nom dans public static Block nom;)
- Pour un item de minecraft : Items.field_de_l’item
- Pour un bloc de minecraft : Item.getItemFromBlock(Blocks.field_de_l’item).
Résultat
Voir le commit sur github
Le commit sur github montre clairement où ont été placés les fichiers, ainsi que ce qui a été ajouté et retiré dans le fichier.Crédits
Rédaction :
Vidéo :
Correction :
Ce tutoriel de Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International -
Premièrement, merci pour le tuto
Mais j’ai un tout petit problème… Mon mob m’attaque, et j’aimerais qu’il soit passif… De cette façon il ne t’attaquerais jamais, même quand tu l’attaques… Si possible j’aimerais aussi savoir comment faire pour qu’il n’apparaisse que dans un biome spécifique. Merci beaucoup d’avance ! -
Essaie de remplacer SharedMonsterAttributes par SharedCreatureAttributes
-
@‘isador34’:
Essaie de remplacer SharedMonsterAttributes par SharedCreatureAttributes
Non ça me dit SharedCreatureAttributes cannot be resolved to a variable. J’ai aucun choix d’import… :s
Merci pour l’aide ! -
Ton mob extend à quoi jimkick3v
-
@‘elias54’:
Ton mob extend à quoi jimkick3v
Il extends EntityMob, ce qui d’ailleurs est peut-être le problème après réflexion… Sauf que dans le code j’ai pas trouvé EntityCreature ou EntityPassive…
-
public Entity getEntityToAttack() { return null; }
Il existe aussi la fonction pour que le mob target une cible :
public void setTarget(Entity par1Entity){ }
-
@‘Patatoufet’:
public Entity getEntityToAttack() { return null; }
Il existe aussi la fonction pour que le mob target une cible :
public void setTarget(Entity par1Entity){ }
Merci à tous pour l’aide mais j’ai trouvé la solution ! Merci elias pour m’avoir ouvert l’oeil ! Suffisait de faire extends EntityCreature, de l’importer, et d’enlever les 2 lignes suivantes :
this.getEntityAttribute(SharedMonsterAttributes.attackDamage).setBaseValue(6D);
this.getEntityAttribute(SharedMonsterAttributes.knockbackResistance).setBaseValu?e(1D);Pour la simple et bonne raison qu’elles ne sont pas utiles à une entité qui n’attaque pas
J’ai fait le test sans les enlever, et le mob ne voulait pas spawner, donc enlever ces 2 lignes était primordial…
Quoique après réflexion, peut-être que la ligne qui set le knockback resistance peut rester, des tests seraient à faire…Merci à tous !
N.B. Si quelqu’un a la solution pour le faire apparaître dans un biome spécifique, j’apprécierais J’ai regardé dans le code EntityHorse, puisque en théorie il n’apparaissent que dans les plaines, mais j’ai pas trouvé… -
EntityRegistry.addSpawn(entityClass, weightedProb, min, max, typeOfCreature, biomes);
-
@‘jimkick3v’:
J’ai regardé dans le code EntityHorse, puisque en théorie il n’apparaissent que dans les plaines, mais j’ai pas trouvé…
M’étonnerait qu’ils aient un biome spécifique… Je viens d’aller en solo et ils spawn par groupe en plein désert… ^^
-
Bonjour.
Tout d’abord, merci pour ce tutoriel. J’aurais une ou deux questions basiques.
Je chercher un moyen de contrôler le nombre de drops du monstre… pour avoir une valeur fixe (et non pas seulement entre 0 et 2 comme semble fonctionner la fonction getDropItem() ). J’ai regardé du coté de dropFewItems, mais je n’ai abouti à rien et tenté de rajouter ceci dans la classe de mon mob au niveau du constructeur :this.dropItem(monmod.monobjet, 5);
Mais cela ne change absolument rien… J’obtiens toujours une valeur aléatoire inférieure à ce nombre.
Faut-il jouer avec l’event onLivingDrops de Forge ?
Et une autre petite question, est-il possible de faire dropper deux items différents à un même mob ?Merci pour le coup de main !
-
Dans la classe de ton entité, ajoute :
public void dropFewItems(boolean b, int looting) { this.dropItem(ClassePrincipale.item, quantity); }
public void dropFewItems(boolean b, int looting) { this.dropItem(ClassePrincipale.item, quantity); this.dropItem(ClassePrincipale.item2, quantity); this.dropItem(ClassePrincipale.item3, quantity); }
Pour plusieurs items. (et this.dropItem(Item.getItemFromBlock(ClassePrincipale.bloc), quantity); pour un bloc)
-
Arf… j’avais les deux bonnes pistes mais pas agencées comme il faut. Merci, ça fonctionne nickel !
-
Question ici aussi… Est-il possible que la vitesse d’un monstre diffère selon le monde ?
Je m’explique.
Je me suis créer un serveur test en créatif. Et tout d’un coup, la vitesse du mob que j’ai créer à diminuer à un lancement (elle est passée à 0.10000000149011612 quand je lui fais écrire) mais je n’avais fait aucune modif dans le code… Comme je n’avais pas précisé la vitesse du monstre pour la laisser de base, j’ai essayé de la remettre, mais même résultat…
Du coup je me suis fait un autre monde en créa et là le mob marche nickel, la vitesse et nickel même si je ne la précise pas. Et avec un code identique.
Est-ce que ça vient simplement de mon pc qui lag sur un monde (je code avec un pc portable par forcément très puissant) ? Ou alors d’un autre truc que je ne contrôle pas ? -
Ça vient des attributs des mobs. Je sais pas trop comment expliquer ça, je vais prendre comme exemple la vie. Il y a un attribut pour la vie maximum et la vie réel du mob. Ces deux valeurs sont enregistrés dans le tag nbt du mob. Donc si dans le code tu change l’attribut de la vie, (que tu mets plus par exemple) une fois que tu ira en jeu, si tu recharge un monde ou tu avais déjà fait spawner un mob, ça vie sera comme avant car il va prendre la vie maximum qui se trouve dans son attribue de la vie maximum. Par contre si tu en fais spawner un nouveau, lui aura la nouvelle valeur.
C’est la même chose pour la vitesse.Donc si tu veux faire une vitesse maximum en fonction du monde, il suffit de changer l’attribut de la vitesse en fonction de :
this.worldObj.provider.dimensionId
Cette int te donne l’id du monde.
Normalement ça devrait fonctionner, par contre si un mob travers le portail, ça vitesse ne changera pas, il gardera celle qu’il avait dans le monde où il a spawner. Si tu veux changer ça, il faudrait changer son attribut de la vitesse dans la fonction onLivingUpdate (ou un truc comme ça, je ne connais pas par cœurs les fonctions des mobs). -
Bah en fait à la base je cherchais pas du tout à faire des valeurs différentes, j’ai créer un autre monde pour voir si c’était le mob ou si c’était le monde.
Je me demandait juste pourquoi, dans un même monde, la vitesse de mon monstre a changer entre deux démarrage. Sachant que sans rien modifier, maintenant, il a retrouvé la vitesse “par défaut” d’un mob… Je me dis que c’était peut-être du à un lag pour tel ou telle raison sur le serveur local de ce monde, bref. C’est un phénomène qui ne m’étais encore jamais arrivé.
Merci pour les explications en tout cas !
-
Surement un lag oui, regarde avec la commande /forge tps.
Si tu as 20, c’est bon, sinon c’est que ça lag. -
alors voila super tuto mais il m’est apparue un probleme avec les couleurs des oeufs ils me mettent que je deoit rajouter un float pour les couleurs et quèant je le fais c est le point .getRGB() qui ne marche pas
ouvez vous m aider svp -
Tu peux nous donner l’import que tu as utilisé pour Color.
-
oui c est : import com.sun.prism.paint.Color;