Professeur

Membre
  • Compteur de contenus

    35
  • Inscription

  • Dernière visite

  • Days Won

    2

Tout ce qui a été posté par Professeur

  1. Les entités Sommaire Introduction Mécaniques des entités Types d'entités Création et suppression Déplacements Collisions Avec les autres entités Avec le monde Intéraction Synchronisation client/serveur Rendu des entités Introduction sur l'organisation du système de rendu Les modèles Les animations Vous pouvez proposer des améliorations sur ce topic
  2. 2. Mécaniques des entités 2.1 Types d'entités Il existe, bien sûr, plusieurs types d'entités, chacun représenté par une classe qui étend Entity. De manière générale, on pourrait les classer en 3 catégories : Les effets visuels (météo, particules, etc.) ; Les joueurs ; Le reste (monstres, animaux, flèches, items au sol, etc.). Ce découpage n'est pas fruit du hasard, il est effectué en fonction de la gestion client/serveur dont bénéficient les entités de chaque catégorie (les effets visuels ne sont pas synchronisés, les créatures et les joueurs apparaissent grâce à des packets différents) -- nous y reviendrons. Ces types d'entités sont enregistrés dans une classe nommée EntityList, qui enregistre, pour chaque type d'entité : Un numéro (unique) ; Un nom ; La classe de ce type. Chaque entité est une instance d'une des classes enregistrées dans cette liste, et ce côté client comme serveur, à l'exception des joueurs. 2.1.1 Le cas des joueurs Le modèle se complexifie un peu pour le joueurs. Alors que toutes les entités sont instance d'une classe portant le même nom et ayant sensiblement les même effets côté client comme serveur, on s'aperçoit que, côté client, les joueurs sont traités différemment ; il existe 2 classes filles d'AbstractClientPlayer : EntitityPlayerSP et EntityOtherPlayerMP. La nomination des classes est assez explicite : côté client, un joueur peut être celui qui est contrôlé par l'utilisateur, ou un autre joueur présent dans le monde. Côté serveur, c'est plus simple : tous les joueurs sont instances de EntityPlayerMP. Note : la présence dans les sources du client d'une classe EntityPlayerMP n'est due qu'à celle du serveur intégré, le client proprement dit ne s'en sert pas. 2.2 Création et suppression Comme nous l'avons dit, un numéro, un nom et une classe qui étend Entity sont liés dans la classe EntityList. Lors du chargement d'un monde, côté serveur donc, les données des entités sont lues depuis des fichiers au format NBT compressés. Une entité est instanciée, en utilisant le constructeur prenant en argument un monde (qui doit obligatoirement être présent dans toutes les classes filles d'Entity), puis ses informations (position, vie, déplacement, inventaire, etc.) sont lues depuis un NBTTagCompound récupéré du fichier de stockage. Lorsqu'une entité meurt (information relayée par le booléen Entity::isDead), elle est automatiquement supprimée du monde par le serveur. Pour toute question, avis, suggestion, merci de vous rendre sur ce topic Merci à Wytrem pour sa contribution.
  3. 1. Introduction Les entités, au même titre que les blocs, constituent un système absolument central de Minecraft. Tout, dans dans un monde, est soit une entité soit bloc (en considérant les TileEntities comme des blocs). On pourra par citer, comme exemples d'entités : Joueurs Cochons Particules Foudre Flèches Tableaux ... La compréhension des mécaniques qui régissent le système d'entités est donc fondamentale au modding (on pourra sauvagement faire le parallèle suivant : si les entités représentent la moitié des objets présents dans un monde, alors vous allez passer 50% de votre temps de moddeur à en coder). Pour toute question, avis, suggestion, merci de vous rendre sur ce topic Merci à Wytrem pour sa contribution.
  4. Le professeur y ramènera sa science à travers chacun de vous.
  5. Sommaire Par quoi je commence ? Dossier : Tout savoir sur l'installation. Premier mod Les meilleurs mods commencent comme ça. Allo ? Est-ce que ça marche ? Premier build et distribution. Vous pouvez proposer des améliorations sur le topic de discussion.
  6. Le build n'est clairement pas la chose la plus compliquée, il faut cependant modifier le fichier build.gradle se trouvant à la racine du projet. version = "1.0" group= "fr.zeamateis.tutoriel_ic" // http://maven.apache.org/guides/mini/guide-naming-conventions.html archivesBaseName = "tutoriel_ic" Rien de bien méchant, simplement la version, le domaine et le MODID à définir. Note #1 : Si vous avez des dépendances c'est également dans ce fichier qu'il faut les déclarer, je vous laisse chercher un peu si vous en avez besoin. Lorsque vous avez terminé, lancez un terminal / un invité de commande. Entrez la commande suivante : gradlew build Votre mod compilé se trouve dans le dossier build/bin Astuce : Si vous souhaitez lancer le jeu en mode débug avec vos identifiants Mojang, rien de plus simple ! Dans les arguments de lancement du jeu, et dans l'encadré "Program arguments" déclarez votre username et votre password comme ceci: --username=Pseudo/Mail --password=VotreMDP Pour toute question, avis, suggestion, merci de vous rendre sur le topic d'aide. Merci à Dren pour sa contribution.
  7. Voici à quoi ressemble le projet par défaut. La première chose à faire c'est de renommer le package"com.example.examplemod"(raccourci Alt + Shift + R). Par convention, les nom de package doivent avoir un sens et se forment par l'abréviation de votre pays puis votre pseudo / organisation suivi par le nom du projet, le tout séparé par des points (exemple fr.zeamateis.xxx). Pensez également à renommer la classe principale afin qu'elle colle à votre projet et à sa fonction. 1]La Classe Principale La classe principale est comme son nom l'indique la classe principale, elle sert de "cerveau" aux "mods", chacun d'eux en ont un, seulsles "coremods" dérogent à cette règle mais nous y reviendront dans un autre tutoriel. Ouvrons la et "zieutons" son contenu : @Mod(modid = TutorielCore.MODID, version = TutorielCore.VERSION) public class TutorielCore { public static final String MODID = "exemplemod"; public static final String VERSION = "1.0"; @EventHandler public void init(FMLInitializationEvent event) { // some example code System.out.println("DIRT BLOCK >> "+Blocks.dirt.getUnlocalizedName()); } } La première chose que vous allez changer c'est le MODID, c'est l'identifiant unique de votre mod, il sert à éviter les conflits avec les autres et défini le dossier dans lequel ranger vos assets, ici nous allons utiliser"tutoriel_ic". Note #1: Le MODID doit toujours être en minuscules, aucunes majuscules et ne doit pas contenir d'espaces Pensez à ajouter un nom à votre projet, c'est plus sympa. Notez que le nom du mod accepte les espaces, accents et majuscules, tout le contraire du MODID. La valeur deVERSIONdépends de vos mises à jours, vous l'organisez comme vous le voulez, le but étant que ce soit clair pour identifier chaque versions. 2] Le MCMod.info Pour modifier les informations du mod (auteur(s), description, etc...), il faut modifier le fichier mcmod.info, qui ce trouve dans le dossier "src/main/resources" Le contenu est assez explicite mais je vous laisse quand même un exemple de fichier complété : [ { "modid": "tutoriel_ic", "name": "Tutoriel IronCraft", "description": "Oh la la , je suis une description pour le mod tutoriel d'IronCraft", "version": "1.0", "mcversion": "1.7.10", "url": "", "updateUrl": "", "authorList": ["ZeAmateis", "IronCraft", "MinecraftForgeFrance"], "credits": "Ironcraft pour accepter ce tutoriel", "logoFile": "/assets/tutoriel_ic/logoDuMod.png", "screenshots": [], "dependencies": [] } ] 3]Les Proxies Les proxies, sont utilisés pour permettre "l'universalité" des mods, pour qu'ils puissent fonctionner côté client ou côté serveur, et c'est là le gros avantage de forge. Créez un nouveau package, je l'ai personnellement nommé proxy et créez deux classes. La classe CommonProxy : package fr.zeamateis.tutoriel_ic.proxy; public class CommonProxy { public void init() { } } La classe ClientProxy : package fr.zeamateis.tutoriel_ic.proxy; public class ClientProxy extends CommonProxy { public void init() { registerRender(); } private void registerRender() { // some code } } Ajoutez ensuite une nouvelle annotation dans la classe principale : @SidedProxy(clientSide = "fr.zeamateis.tutoriel_ic.proxy.ClientProxy", serverSide = "fr.zeamateis.tutoriel_ic.proxy.CommonProxy") public static CommonProxy proxy; Appelez ensuite votre fonction init du proxydans la fonction init de la classe principale: public void init(...) { proxy.init(); } A l'heure actuelle les proxys sont fonctionnels, mais ne seront utiles que lorsque vous ajouterez des entités par exemple. 4]Les Assets La gestion des ressources est très simple ! Il suffit de créer un dossier assetspuis un dossier portant votre modid. Création du dossier assets via Eclipse: > Clic droit sur le dossier src/main/resources, New, Other, General, Folder: Entrez simplement le nom du dossierdans le champ de texte Folder name Répétez cette opération sur le nouveau dossier assets: Vous vous retrouverez ensuite avec quelque chose ressemblant à ceci : Répétez encore cette opération pour le dossier textures et lang Note: Cette opération remplace le traditionnel Clic droit > Nouveau Dossier de l'explorateur Windows par exemple. Vous devez vous retrouver avec ceci : Vous pourrez créer d'autres dossiers pour différents types d'assets comme les sounds par exemple. 4.1]Les Langues Dans le dossier lang il vous faudra créer des fichiers de traductions avec l'extension .lang. Voici une petite liste des définitions des langues : en_US : Anglais (USA) en_GB : Anglais (United Kingdom) fr_FR : Français (France) fr_CA : Français (Canadien/Québécois ?) es_ES : Espagne (Espagnol) it_IT : Italie (Italien) Vous trouverez les autres ici : http://minecraft.gamepedia.com/Language Donc pour les futures traduction en français et en anglais par exemple, il vous faudra les fichier fr_FR.lang et en_US.lang Pour toute question, avis, suggestion, merci de vous rendre sur le topic d'aide. Merci à MinecraftForgeFrance et TheAmateiis pour leurs contributions.
  8. Avant de commencer, je vais considérer que si vous utilisez un système UNIX, vous savez ce qu'est un terminal, naviguer dans les dossier et comment lancer un programme via celui-ci. Par conséquent les explications seront décrites pour Windows et uniquement si nécessaire sur d'autres systèmes. Ceci dit, rien ne vous empêche de préparer un paragraphe spécifique que j'ajouterais au tutoriel. 1]Le JDK Rendez-vous sur la page de téléchargement d'Oracle, choisissez la version appropriée puis télécharger là. Lancez le programme ainsi obtenu, décompressez le avant si nécessaire, et suivez les étapes jusqu'au bout. Ajoutez les lignes ci-dessous dans la variable d'environnementpath si elles n'existent pas C:\ProgramData\Oracle\Java\javapath; C:\Program Files\Java\jdk1.7.0_75\bin; Créez également les variables utilisateur JAVA et JAVA_HOME comme décrites sur l'image Note #1: Bien entendu vous devez changer la version du jdk et celle du jre par celle que vous avez installé, si vous avez un doute, parcourez le chemin manuellement. 2]Eclipse Pour installer eclipse, rien de plus simple, rendez-vous sur le site d'eclipse, choisissez la version que vous avez besoin, téléchargez la puis installez là. 3]Minecraft Forge Je vous invite à vous rendre sur le site de Minecraft Forge, choisissez votre version et téléchargez le formatsrc de celle-ci. Décompressez l'archive où vous souhaitez et placez vous dans le dossierfraîchement obtenu. Lancez un terminal / un invité de commande Entrez la commande suivante : gradlew setupDecompWorkspace & gradlew eclipse Attention, si le dossier est amené à bouger ou à être renommé, vous devrez relancer la commande gradlew eclipse. Choisissez le dossier eclipse contenu dans votre projet comme étant votre workspace soit au lancement d'eclipse, soit via file->switch workspace. Pour toute question, avis, suggestion, merci de vous rendre sur le topic d'aide. Merci à Dren pour sa contribution.
  9. Un peu de théorie Le code de Minecraft peut être divisé en 2 « côtés » : le Client et le Serveur. Le côté Serveur est chargé de la gestion du monde partagé par les clients connectés : rafraîchissement des blocs et des entités basé sur les packets qu'il reçoit du client, et envoi des informations mises à jour à tous les clients.Le côté Client est là pour être à l'écoute du des actions du joueur (clics souris, clavier, etc.) et pour dessiner la fenêtre.Il n'y a qu'un seul serveur, et plusieurs clients qui se connectent dessus. Même en mode solo, le serveur et le client tournent simultanément (dans des Threads séparés). Certaines parties du code sont « communes » : elles sont utilisées à la fois par le client et par le serveur. Mais alors, comment savoir depuis un code qu'on écrit à la fois pour le Client et le Serveur qui l'a appelé ? Depuis presque n'importe où dans le jeu, on peut avoir accès à un objet World (passé en field ou paramètre dans la plupart des cas). Dans ce cas là, il suffira de tester le booléen World.isRemote : Si World.isRemote vaut true : on est côté Server.Si World.isRemote vaut false : on est côté Client.Quand le Client et le Serveur ont besoins d'être synchronisés, ils échangent des informations par le réseau (LAN ou non).Même quand on est en mode solo, les deux côtés du jeu sont complètement séparés et n'accèdent pas aux objets de l'autre. Si le code executé (par exemple) côté Serveur tente d'accéder à des objets du côté Client, vous aurez affaire à des crash aléatoires ou des comportements étranges. Et de toute manière, votre mod ne pourra pas du tout fonctionner sur un serveur dédié (en multijoueur). Le communication Client <--> Server en vanilla passe par des « packets », qui sont envoyés et reçus depuis les classe NetHandlerPlayClient et NetHandlerPlayServer, respectivement côté Client et Serveur. Un packet est un ensemble de données, qui peuvent être échangées entre le Client et le Serveur. Il en existe beaucoup, de différents types, et qui servent à des choses très différentes : Les packets envoyé depuis le Client vers le Serveur servent à :Transmettre la position du joueur et ses mouvements, ainsi que ses intéractions avec l'environnement (casser un bloc, clic sur une entité, etc.)Envoyer des messages chatLes packets envoyé depuis le Serveur vers le Client sont beaucoup plus nombreux, et servent à :Informer le client des mouvements des entitésInformer le client des changements de blocsGérer les interfaces graphiques pour les containersEt plein d'autres trucs utilesIl est essentiel de noter que :Les packets envoyés depuis le Client vers le Serveur commencent avec un C, par exemple C07PacketPlayerDigging.Les packets envoyés depuis le Server vers le Client commencent avec un S, par exemple S06PacketUpdateHealth.Il est possible de créer des packets déjà présents dans Minecraft à la main et de les envoyer directement en utilisant le NetHandler, mais ce n'est presque jamais nécessaire. Si on connais les bonnes méthodes vanilla, elles enverront les packets pour nous (par exemple, appeler ServerConfigurationManager.sendChatMsg() à la place de S02PacketChat).Communiquer via le réseau avec nos propres packets Pour envoyer un packet, l'entité réseau (Client ou Serveur) utilise les sockets. Elle écrit le packet sur le flux de sortie en utilisant la classe PacketBuffer, comme nous le renseigne l'interface Packet : package net.minecraft.network; import java.io.IOException; public interface Packet { /** * Utilisé pour lire le contenu d'un packet que l'on reçoit. */ void readPacketData(PacketBuffer data) throws IOException; /** * Utilisé pour écrire le contenu d'un packet que l'on envoie. */ void writePacketData(PacketBuffer data) throws IOException; /** * Appelé lors de la réception du packet. * @param handler Objet INetHandler qui contient toutes les méthodes de déléguation (ex : handleChat, handleEntity...). */ void processPacket(INetHandler handler); } Chaque packet reçu (ou envoyé) est identifié par son numéro ID unique. Depuis la version 1.7, le système de packets a été beaucoup amélioré, de fait que maintenant, on ne manipule plus directement les IDs.Dans le thread d'écoute d'une entité réseau, seulement l'ID du packet reçu est lue directement. Ensuite, le système récupère une instance du packet correspondant grâce à la méthode EnumConnectionState.getPacket(EnumPacketDirection, int). Ensuite est appelée Packet.readPacketData(PacketBuffer) sur cette instance fraîchement obtenue, puis le packet est délégué. L'enregistrement de toutes les sortes de packets se fait dans la classe EnumConnectionState, qui représente le stade de connexion d'un client à son serveur. Chaque stade correspond à une phase du jeu : Le « handshaking », qui vérifie que la version du client est compatible avec celle du serveur ;Le « play » (lorsque le client est en jeu), qui sert à envoyer les informations évoquées ci-dessus (entités, changements de map, etc.)Le « status », qui sert à effectuer des requêtes sur le serveur, comme le ping ou l'information du nombre de connectés ;Le « login », qui sert à s'authentifier lors de la connexion au serveur.Nous allons, pour notre exemple, enregistrer un packet qui enverra au client un texte à afficher sur son écran. Ceci se passera dans la phase de jeu, on ajoute donc l'enregistrement de notre packet dans EnumConnectionState, en dessous de la déclaration du « play » : // [...] this.registerPacket(EnumPacketDirection.CLIENTBOUND, S48PacketResourcePackSend.class); this.registerPacket(EnumPacketDirection.CLIENTBOUND, S49PacketUpdateEntityNBT.class); // L'enum EnumPacketDirection sert à définir de quel côté le packet arrive. Ici, le client. this.registerPacket(EnumPacketDirection.CLIENTBOUND, S50PacketDisplayText.class); // [...] La classe S50PacketDisplayText n'est pas trouvée ? Alors créons là ! package net.minecraft.network.play.server; import java.io.IOException; import net.minecraft.network.INetHandler; import net.minecraft.network.Packet; import net.minecraft.network.PacketBuffer; public class S50PacketDisplayText implements Packet { public S50PacketDisplayText() {} // L'implémentation de Packet nous oblige à implémenter ses méthodes @Override public void readPacketData(PacketBuffer data) throws IOException { } @Override public void writePacketData(PacketBuffer data) throws IOException { } @Override public void processPacket(INetHandler handler) { } } Nous avons expliqué ci-dessus que la réception des packets se fait en en créant une instance nouvelle. C'est pourquoi il faut laisse un constructeur sans arguments ! Nous pouvons maintenant ajouter les fields qui vont contenir les informations qu'on souhaite faire transiter. Dans notre cas, il n'y en aura qu'un : private String displayText; Et pour la commodité, ajouter un constructeur qui permette directement de renseigner la chaîne de caractères : public S50PacketDisplayText(String text) { this.displayText = text; } Ainsi qu'une méthode pour récupérer le texte : public String getDisplayText() { return this.displayText; } Puis, il faut remplir les méthodes writePacketData et readPacketData, respectivement en y écrivant et lisant nos fields : @Override public void readPacketData(PacketBuffer data) throws IOException { // Le 256 est la taille maximum de la chaîne de caractères this.displayText = data.readStringFromBuffer(256); } @Override public void writePacketData(PacketBuffer data) throws IOException { data.writeString(this.displayText); } Notre packet est presque terminé ! Il manque l'appel de la délégation lors de son arrivée. Dans l'interface INetHandlerPlayClient, on créée donc la méthode : void handleDisplayText(S50PacketDisplayText packetDisplayText); Nous serons obligés de l'ajouter également dans les classes qui l'implémentent, c'est à dire NetHandlerPlayClient : @Override public void handleDisplayText(S50PacketDisplayText packetDisplayText) { System.out.println("Packet reçu avec le texte : " + packetDisplayText.displayText); } Et enfin, il nous reste à modifier la méthode processPacket(INetHandler) de notre S50PacketDisplayText : @Override public void processPacket(INetHandler handler) { ((INetHandlerPlayClient) handler).handleDisplayText(this); } Pour toute question, avis, suggestion, merci de vous rendre sur Merci à Wytrem pour sa contribution.
  10. Sommaire la folie des blocs ? Dossier : Tout savoir sur ces petites bêtes. Les modèles Petits, Grands, Musclés, vous allez les aimer. Les textures Ellessont belles, elles sont chaudes, elles vont vous faire craquer. Les textures transparentes Ellessont belles, elles sont chaudes et elles découvrent le haut. Le rendu Terre en vu ! Que la lumière soit ! Il fait beau, il fait chaud, l'été arrive. Vous pouvez proposer des améliorations sur ce topic
  11. Le rendu dans le monde Le rendu des blocs a considérablement changé par rapport aux versions antérieures. Cela a rendu la compréhension un peu plus compliquée mais a introduit beaucoup plus de flexibilité pour personnaliser l'apparence des blocs sans utiliser le code. Il y a quelques points clés à comprendre : Lorsque Minecraft rend un paysage, il faut d'abord rassembler les informations sur les blocs (emplacement, apparence, etc) dans une liste d'instructions que le moteur graphique peut exécuter. Typiquement, cela signifie une itération sur chaque bloc que le joueur pourrait voir et une conversion de chaque partie du bloc en une série de rectangles texturées. Un simple bloc comme la pierre dispose de six places, une pour chaque face. Les blocs plus complexes telles qu'une torche ou un piston sont constitués d'un plus grand nombre de faces rectangulaires, peut-être à des angles différents. Une fois la phase de rendu principale complète, Minecraft finit avec une liste d'instructions contenant la couleur, les coordonnées de texture et les informations d'éclairage qui peut être exécutée par le moteur graphique pour dessiner les blocs. La plupart des blocs dans un paysage sont statiques, c'est à dire qu'ils ne changent pas de forme ou de position au fil du temps. Cela permet à Minecraft de pré-compiler une liste d'instructions de rendu puis d'appeler le moteur graphique pour l'exécuter à plusieurs reprises, une fois par trame. C'est beaucoup plus rapide que la compilation des instructions de rendu à partir de zéro chaque trame. En général, les instructions ne sont recompilées que lorsque des blocs changent. Les blocs dans un paysage sont rendus de quatre manières différentes (voir photo ci-dessous). SOLID - La texture est totalement opaque, la valeur alpha est ignorée. CUTOUT_MIPPED - (test alpha) - Les textures du bloc ont des trous et le mip mapping est activé (Voir sur Google pour plus d'informations). CUTOUT - (test alpha) - Les textures du bloc ont des troussauf que le mip mapping est éteint. TRANSLUCENT- (mélange alpha) - Ces textures sont partiellement transparentes de sorte à voir d'autres blocs à travers. Si l'option graphique est sur "FAST", elles deviennent opaques. Les blocs blocs translucides sont relativement coûteux à rendre parce qu'ils doivent être pré-triés dans l'ordre de leur distance par rapport à la caméra, sinon ils ne rendront pas correctement. Un paysage est rendu en couches - tous les blocs SOLID sont établis en premier, puis les CUTOUT_MIPPED, puis CUTOUT, et enfin les TRANSLUCENT. Un bloc donné ne peut être rendu que dans l'une de ces quatre couches, spécifiée par Block.getBlockLayer(). Chaque bloc possède également un type de rendu, spécifié par Block.getRenderType() : -1 = Ne rend rien du tout, par exemple le bloc 'air'. 1 = Rend comme un liquide en écoulement (texture animée) en utilisant BlockFluidRenderer.genderFluid. 2 = N'utilise aucune couche de rendu des blocs maisuneTileEntitySpecialRenderer associé, par exemple le bloc 'chest'. 3 = Utilise unIBakedModel pour le rendu, BlockModelRenderer.renderModel. Ceci est décrit plus en détail ci-dessous. Il y a deux classes qui mettent en uvre l'interfaceIBakedModel,SimpleBakedModel et WeightedBakedModel. Elles sont toutes deux essentiellement identiques, sauf que la seconde choisit un modèle aléatoire à partir d'une liste de modèles alternatifs pondéré, en utilisant la position du bloc en tant que 'seed'. Cela permet pour certains blocs d'avoir une apparence différente selon l'endroit où ils sont placés. Le format BakedModel (utilisé par Minecraft pour presque tous ses blocs) est décrit ici. Il y a quelques considérations spéciales pour rendre les blocs transparents comme décrit ici. L'éclairage des blocs est décritici. Note : Certains blocs comme les fleurs utilisent la méthode Block.getOffsettype() pour déplacer de façon aléatoire la position du modèle (jusqu'à +/- 0,25 blocs). Pour toute question, avis, suggestion, merci de vous rendre sur Merci à Dren pour sa contribution.
  12. Textures les faces avec transparence A FAIRE
  13. Tuuuuutttooooosss

  14. Texturer les faces (quads) Les models des blocs sont utilisés par Minecraft pour relier les textured quads. La texture suit un système relativement strict, ça peut être difficile à maîtriser mais ce n'est vraiment pas difficile. Dans le diagramme ci-dessous, le bloc rouge pointe vers le sud (axe Z) et le bloc bleu pointe vers l'est (axe X). Texturer en utilisant les variables UV Les variables UV servent à découper l'image que nous souhaitons appliquer à une face. Le format [u1, V1, U2, V2] est utiliser pour sélectionner la zone à appliquer, U étant les coordonnées du point de départ etVcelles du point d'arrivé. Attention, Les faces sont toujours rendues dans le même sens, la direction dans laquelle vous regardez n'y change rien. Il vous faudra inverser U et V pour avoir une face opposée. La texture sera toujours affichée en direction du ciel pour les faces 2 à 5, du nord pour la 1 et du sud pour la 0. Je vous invite vivement à tester ce pack de ressourcesafin de bien visualiser le fonctionnement, la Stone et la Cobblestone sont utilisés. Il va de soit que le faire à la main pour un bloc complexe est difficilement réalisable, l'alternative consiste à utiliser un programme pour vous aider à faire ces models, BDcraft Cubik me semble le plus complet pour effectuer cette tâche mais il en existe d'autres. Pour toute question, avis, suggestion, merci de vous rendre sur Merci à TheShark34 pour sa contribution.
  15. Les modèles. La méthode de rendu des blocs dans Minecraft a énormément évoluée lors de la mise à jour 1.8. La forme des blocs a toujoursétédéfinie dans le code, mais ce n'est qu'un lointain souvenir.Désormais, Minecraft utilise un fichier de modèle en JSON pour définir la forme ainsi que la texture.Qu'est ce que ça change ?Nous pouvons maintenant avoir plusieurs modèles pour un seul et unique bloc mais également lesmodifier depuis un pack de ressource. 1- Le fichier des BlockStates Chaque bloc a un fichier de configuration de ses états, portant le nom utilisé lors de son enregistrement et situé dans assets/votreModID/blockstates,votreModIDétant minecraft en Vanilla. Extrait de blockstates/torch.json { "variants": { "facing=up": { "model": "normal_torch" }, "facing=east": { "model": "normal_torch_wall" }, "facing=south": { "model": "normal_torch_wall", "y": 90 }, "facing=west": { "model": "normal_torch_wall", "y": 180 }, "facing=north": { "model": "normal_torch_wall", "y": 270 } } } La torche utilise deux fichiers de modèles différents : normal_torchetnormal_torch_wall, le premier lorsqu'elle est posée au sol (sur la partie haute du bloc en dessous) et le second lorsqu'elle se trouve sur un mur. N'oubliez pas de préciser le modid lorsqu'il s'agit d'un mod. "model": "modid:nomdufichiermodel" 2 - Le fichier de modèle Les modèles sont fait d'un certain nombre de faces rectangulaires, appelées quads, basées sur des éléments cuboïdes.Un cuboïde est défini par les coordonnées de deux coins opposés, "de" [x,y,z], "à" [x,y,z]. Par défaut, un bloc se présente sous la forme [0, 0, 0], [16, 16, 16]. Chaque quad est ensuite recouvert par une texture. Les coordonnées UV sont les coordonnées de la texture à appliquer, dans l'ordre [u1, v1, u2, v2], où [0, 16, 0, 16] correspond à la texture entière. On notera que la coordonnée 0 estle coin en haut à gauche de la texture. Chacun des modèles se situent dansassets/votreModID/models/block/,votreModIDétantminecraftenVanilla. Extrait de models/block/half_slab.json: "elements": [ { "from": [ 0, 0, 0 ], "to": [ 16, 8, 16 ], "faces": { "down": {"uv": [ 0, 0, 16, 16 ], "texture": "#bottom", "cullface": "down" }, "up": {"uv": [ 0, 0, 16, 16 ], "texture": "#top" }, "north":{"uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "north" }, "south":{"uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "south" }, "west": {"uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "west" }, "east": {"uv": [ 0, 8, 16, 16 ], "texture": "#side", "cullface": "east" } } } ] L'ordre des coordonnées est très important, si vous les inversez, par exemple [16, 0, 0, 16] au lieu de [0, 0, 16, 16], la texture sera alors elle aussi inversée (de la gauche à la droite dans notre exemple). Les quads ne sont visible que d'un seul coté, par exemple, la face Est d'un bloc sera invisible si vous êtes derrière elle (si vous regardez le bloc de l'Ouest). Chacune des textures est définie dans une variable initialisée dans le champs texture. Vous ne pouvez pas définir le chemin directement sur la face concernée. Je vous invite à lire la partie sur la texturisation des blocs. Une valeurcullfaceest parfois présente, vous pouvez y définir une direction. Si un bloc est collé dans la direction désignée alors la face ne s'affichera pas. Quelques petites notes : ambientocclusionactive/désactive l'occlusion ambiante lors du rendu de ce bloc. L'occlusion ambiante permet d'améliorer le rendu de la lumière et des ombres, elle est adaptée pour les blocs pleins, mais pas pour les petits ou pour les formes compliquées. tintindexpermet de modifier la couleur du rendu via la méthode Block.colorMultiplier(). C'est utilisé par l'herbe pour changer sa couleur dans les différents biomes. shadepermet de contrôler la luminosité du bloc. S'il estfalse, l'éclairage du bloc ne sera pas dépendant de la luminosité de la face. particleest la représentation 2D du bloc, utilisé pour les particules volantes lorsque le bloc est cassé. Minecraft vanilla a quelques blocs "tout fait", qui n'ont pas de fichiers de modèle, comme l'air, les coffres, ... définis dans BlockModelShapes.registerAllBlocks(). Il faut aussi définir les blocs qui ont des états qui affectent le rendu dans BlockModelShapes.registerAllBlocks(). Pour les blocs customs avec des états spéciaux, vous devez appeler BlockModelShapes.registerBlockWithStateMapper() pour définir leur rendus. Pour plus d'informations, je vous invite à consulter la documentation de Minecraft(Anglais). Pour toute question, avis, suggestion, merci de vous rendre sur Merci à Zetsyog pour sa contribution.
  16. Cette partie n'est qu'un brouillon, pour vous montrer que l'on ne fait pas que se prélasser au bord de la piscine, je vous met en ligne une petite partie de notre travail actuel. Il est fort probable que vous ne compreniez pas tout, vous pouvez proposer des améliorations sur C'est quoi ? Le concepts des Blocs dans Minecraft : Le monde sur Minecraft est composé d'une grille tridimensionnelle de blocs. La grille du monde présente une hauteur de 256 blocs seulement, mais elle s'étend indéfiniment sur les deux axes horizontaux. Les blocs sont regroupés en "Chunks". Ce sont des parallélépipède de 16x256x16 blocs. Les chunks sont regroupés en région; chaque région est composé de 32x32 chunks. Les régions sont stockées sur le disque, un fichier par région. L'objectif de tous ces regroupements est avant tout de réduire l'espace de stockage (Les parties du monde complètements vide ne sont pas stocké sur le disque) et d'un autre coté pour permettre au ckunks d'être créées et chargés en mémoire individuellement, en fonction des besoins, sans avoir à stocker les zone que le joueur n'a pas encore exploré. Le système de coordonné utilisé dans Minecraft est présenté en image juste dessous: Regardez la classe EnumFacing pour découvrir un certain nombre de constantes et des fonctons utiles liés au six directions (Par exemple getOpposite(), rotateX(), getFrontOffsetX(), etc). Au minimum, chaque emplacement de grille [x, y,z] contient les informations suivantes: Un IBlockState, qui spécifie le type de bloc à cet emplacement (par exemple un BlockDoor) ainsi que son état actuel (par exemple ouvert ou fermé). Une valeur entière "SkyLight" de 0 à 15, qui spécifie la quantité de lumière ce bloc reçoit du ciel. Une valeur entière "BlockLight" de 0 à 15, qui spécifie la quantité de lumière ce bloc reçoit des blocs voisins Ces données ne sont pas stockées dans la classe Block. Ils sont stockés dans un tableau à l'intérieur des objets ExtendedBlockStorage, à l'intérieur des objets Chunk. Ils sont accessibles via diverses méthodes monde tels que : IBlockState blockAtXYZ = world.getBlockState( {x,y,z}); int lightFromSky = world.getLightFor(SKY, {x,y,z}); int lightFromBlocks = world.getLightFor(BLOCK, {x,y,z}); L'aspect le plus important pour le moddeur à comprendre est le IBlockState. Un IBlockState est composé de deux éléments, le Bloc et ses propriétés (son état) La classe Block et les classes dérivées ne stockent pas des informations uniques sur chaque position dans le monde, ils ne stockent que des informations de type bloc. Les classes bloc sont essentiellement des classes composées de méthodes pour déterminer le comportement du bloc et comment il est dessiné à l'écran. Elles sont généralement indépendante et uniques, mais pas toujours (par exemple, différents types de bois provienne de la même instance de BlockOldLog). Les instances des blocs peuvent être trouvés dans la classe Blocks; par exemple Blocks.dirt, Blocks.sand, Blocks.water. Chaque bloc peut aussi avoir une ou plusieurs propriétés qui décrivent les différents états que le bloc peut présenter. Par exemple BlockLadder possède une propriété appelée EXPOSITION qui a quatre valeurs admissibles (NORTH, SOUTH, EST ou WEST) en fonction de sa direction. BlockOldLog possède une propriété appelée VARIANT avec pour valeurs admissibles OAK, SPRUCE, BIRCH, JUNGLE. Certains blocs possèdent plusieurs propriétés, telles que BlockBed qui présente plusieurs proprietés, comme FACING, avec pour valeurs admissibles NORTH, SOUTH, EAST, WEST; PART avec pour valeurs admissibles HEAD or FOOT afin de determiner la partie du lit; OCCUPIED, avec pour valeurs admissibles TRUE or FALSE; La combinaison d'un Bloc et de son état actuel est représenté par un IBlockState. Par exemple, un état possible de BlockBed est block_bed [facing=east, part=head, occupied=false]; Cet état peut être manipulé en utilisant : IBlockState.getValue(property) pour obtenir la valeur d'une proprieté particulière, par exemple bedFacingDirection = currentBedState.getValue(FACING); qui retourne EAST. IBlockState.withProperty(property, new value) pour retourner un nouvel état avec une des proprieté modifié dans la nouvelle valeur stocké, exemple IBlockState newBedState = oldBedState.withProperty(FACING, NORTH); Si vous souhaitez determiner l'état par une valeur particulière, vous avez besoin de partir de l'état base(default), par exemple IBlockState bedFacingNorth = Blocks.bed.getDefaultState().withProperty(FACING, NORTH); C'est un peu verbeux, mais c'est comme ça que Minecraft fait. Un bon nombre d'autres structures de données peuvent être utilisée pour stocker plus d'informations à propos de l'emplacement d'une grille particulière : TileEntity,qui stocke des informations personnalisées telles que le texte sur les panneaux, la liste des éléments dans un coffre, etc. La plupart des emplacements de grille n'ont pas TileEntity correspondante. Chaque chunk contient une liste de TileEntities, avec une entrée pour chaque TileEntity et sa position [x, y, z] dans le chunk. La TileEntity à un endroit précis est accessible en utilisants TileEntity tileEntityAtXYZ = world.getTileEntity({x,y,z}); L'entité (Entity) est utilisé pour certains "blocs" spéciaux comme les blocs en chute libre (Sable, gravier), les peintures et les items dans les cadres. Plus de détails : Les principales IProperties utilisés par Minecraft sont : PropertyEnum comme VARIANT (OAK, SPRUCE, BIRCH, JUNGLE) pour BlockOldLogs PropertyDirection comme FACING (N, S, E, W) pour BlockDoor PropertyInteger comme AGE de 0 à 7 pour BlockCrops (Exemple la maturation du blé) PropertyBool comme OCCUPIED (true/false) pour BlockBed. Le code de base de Minecraft contient des helpers pour créer vos propres propriétés avec des noms et des valeurs personnalisées; voir les blocs de base mentionnés ci-dessus pour l'inspiration. Pour chaque bloc donné, une instance BlockState est utilisé pour définir les états valides que le bloc correspondant peut avoir. (Le nom est trompeur car BlockState n'implémente pas IBlockState !). Le BlockState peut être accessible à l'aide de Block.getBlockState(). Par exemple, pour la BlockState BlockBed contient trois propriétés FACING, avec pour valeurs possibles NORTH, SOUTH, EAST, WEST. PART, avec pour valeurs possibles HEAD ou FOOT. OCCUPIED, avec pour valeurs possibles TRUE ou FALSE. Le BlockState pour votre bloc personnalisé est créé lors de la construction de bloc en remplaçant(Override) le Block.createBlockState(). Vous devez être prudent pour définir l'état par défaut dans le constructeur de votre bloc - voir BlockBed pour un exemple. Comme pour les versions 1.7 et antérieures, minecraft utilise seulement un total de 16 bits pour IBlockState - 12 bits pour une BlockID plus 4 bits pour les informations d'état (précédemment appelé métadonnées). En général, cela signifie qu'un bloc doit pas avoir plus de 16 états uniques. Bien que certains blocs de base ait plus de 16 combinaisons de propriétés (par exemple BlockDoor en a 64), au plus 16 d'entre eux sont effectivement stockée par emplacement de la grille. Dans l'exemple de BlockBed, bien qu'il existe 16 combinaisons possibles de valeurs, seulement 12 sont utilisé- voir le tableau ci-dessous. Block.getMetaFromState() et Block.getStateFromMeta() sont utilisés pour convertir la valeur IBlockState en la valeur de métadonnées entière (de 0 à 15 inclus) correspondante, et vice-versa. Quand vous implementez des blocs avec des propriétés, vous devez faire attention à retourner des valeurs de métadonnées strictements comprise entre 0 et 15, sinon vous allez remplacer les correspondances d'autres blocs. De même, vous devez vous assurer que votre getMetaFromState() et getStateFromMeta() sont équivalents. Certaines structures de multiblocs, comme BlockDoor (La porte) ou BlockBed (Le lit), utilisent une troisième méthode appelée Block.getActualState() qui vérifie le IBlockState des autres blocs dans le multibloc, ce qui permet au multibloc de : synchroniser l'état de tous les blocs dans le multibloc (Exemple BlockBed - OCCUPIED) stocker une valeur d'infos superieur à 4 bits, par exemple BlockDoor: - le bloc supérieur de la porte stocke le HINGE position (LEFT/RIGHT), et si la porte est POWERED ou pas. - le bloc inférieur de la porte stocke le FACING (NORTH, SOUTH, EAST, WEST) et si la porte est OPEN ou pas. La même méthode Block.getActualState() est égalements utilisée par les blocs qui changent d'aparence en fonction de leurs voisins. Par exemple BlockFence, BlockRedstoneWire. Block.getActualState() est généralement appelé juste avant un rendu, pour être sur que le bloc présente la bonne apparence. Pendant la phase d'initialisation du jeu, une instance unique est créé pour chaque bloc, voir Block.registerBlocks() et la classe Blocks. Cela signifie que vous pouvez tester l'égalité en utilisant l'opérateur == au lieu d'avoir à utiliser .equals(), par exemple boolean thisBlockIsDirt = (blockAtCursor == Blocks.dirt); De même, il existe une instance unique pour chaque IBlockState, ce qui permet par exemple boolean stateHasChanged = (newIBlockState != currentIBlockState); au lieu de boolean stateHasChanged = !newIBlockState.equals(currentIBlockState); Pour toute question, avis, suggestion, merci de vous rendre sur Merci à Nopoza973 pour sa contribution.