{"version":"1.1","schema_version":"1.1.0","plugin_version":"1.1.2","url":"https://tutos-gameserver.fr/2020/02/09/creation-dun-editeur-javascript-minecraft-3d-sitepoint-un-bon-serveur-minecraft/","llm_html_url":"https://tutos-gameserver.fr/2020/02/09/creation-dun-editeur-javascript-minecraft-3d-sitepoint-un-bon-serveur-minecraft/llm","llm_json_url":"https://tutos-gameserver.fr/2020/02/09/creation-dun-editeur-javascript-minecraft-3d-sitepoint-un-bon-serveur-minecraft/llm.json","manifest_url":"https://tutos-gameserver.fr/llm-endpoints-manifest.json","language":"fr-FR","locale":"fr_FR","title":"Création d&#39;un éditeur JavaScript Minecraft 3D &#8211; SitePoint\n &#8211; Un bon serveur Minecraft","site":{"name":"Tutos GameServer","url":"https://tutos-gameserver.fr/"},"author":{"id":1,"name":"Titanfall","url":"https://tutos-gameserver.fr/author/titanfall/"},"published_at":"2020-02-09T21:57:24+00:00","modified_at":"2020-02-09T21:57:24+00:00","word_count":4515,"reading_time_seconds":1355,"summary":"Cet article a été révisé par des pairs par Paul O’Brien. Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible! J&#39;ai toujours voulu créer un jeu 3D. Je n&#39;ai tout simplement jamais eu le temps et l&#39;énergie pour apprendre les subtilités de la programmation 3D. J&#39;ai découvert [&hellip;]","summary_points":["Cet article a été révisé par des pairs par Paul O’Brien.","Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible!","J&#39;ai toujours voulu créer un jeu 3D.","Je n&#39;ai tout simplement jamais eu le temps et l&#39;énergie pour apprendre les subtilités de la programmation 3D."],"topics":["Serveur minecraft"],"entities":[],"entities_metadata":[{"id":13,"name":"Serveur minecraft","slug":"serveur-minecraft","taxonomy":"category","count":2786,"url":"https://tutos-gameserver.fr/category/serveur-minecraft/"}],"tags":["Serveur minecraft"],"content_hash":"cbba476f6d0b93d0fd479f8debaaf903","plain_text":"Cet article a été révisé par des pairs par Paul O’Brien. Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible!\n\nJ&#39;ai toujours voulu créer un jeu 3D. Je n&#39;ai tout simplement jamais eu le temps et l&#39;énergie pour apprendre les subtilités de la programmation 3D. J&#39;ai découvert que je n&#39;avais pas besoin de &#8230;\nEn bricolant un jour, j&#39;ai pensé que je pourrais peut-être simuler un environnement 3D en utilisant des transformations CSS. Je suis tombé sur un vieil article sur la création de mondes 3D avec HTML et CSS.\nJe voulais simuler un monde Minecraft (ou une infime partie de celui-ci au moins). Minecraft est un jeu de bac à sable, dans lequel vous pouvez casser et placer des blocs. Je voulais le même type de fonctionnalité, mais avec HTML, JavaScript et CSS.\nVenez décrire ce que j&#39;ai appris et comment cela peut vous aider à être plus créatif avec vos transformations CSS!\n\nRemarque: La plupart du code de ce didacticiel se trouve sur Github. Je l&#39;ai testé dans la dernière version de Chrome. Je ne peux pas vous promettre que cela aura exactement la même apparence dans d&#39;autres navigateurs, mais les concepts de base sont universels.\n\nCe n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.\nLes choses que nous faisons déjà\nJ&#39;ai écrit ma juste part de CSS et j&#39;en suis venu à bien le comprendre, dans le but de créer des sites Web. Mais cette compréhension repose sur l&#39;hypothèse que je vais travailler dans un espace 2D.\nPrenons un exemple:\n.outils \r\n  position: absolue;\r\n  gauche: 35px;\r\n  haut: 25px;\r\n  largeur: 200 px;\r\n  hauteur: 400px;\r\n  indice z: 3;\r\n\r\n\r\n.Toile \r\n  position: absolue;\r\n  gauche: 0;\r\n  en haut: 0;\r\n  largeur: 100%;\r\n  hauteur: 100%;\r\n  indice z: 2;\r\n\r\n\nIci, nous avons un élément canvas, commençant dans le coin supérieur gauche de la page et s&#39;étendant jusqu&#39;en bas à droite. En plus de cela, nous ajoutons un élément outils. Il commence 25px de gauche et 35px en haut de la page et les mesures 200px large par 400px haute.\nSelon la commande div.tools et div.canvas sont ajoutés au balisage, il est tout à fait possible que div.canvas pourrait se chevaucher div.tools. C&#39;est à l&#39;exception de la z-index styles appliqués à chacun.\nVous êtes probablement habitué à considérer les éléments ainsi conçus comme des surfaces 2D susceptibles de se chevaucher. Mais ce chevauchement est essentiellement une troisième dimension. la gauche, Haut, et z-index peut aussi bien être renommé X, y, et z. Tant que nous supposons que chaque élément a une profondeur fixe de 1px, et z-index a un implicite px unité, nous pensons déjà en termes 3D.\nCertains d&#39;entre nous ont tendance à avoir du mal avec les concepts de rotation et de traduction dans cette troisième dimension…\nLa théorie des transformations\nLes traductions CSS dupliquent cette fonctionnalité familière, dans une API qui dépasse les limites Haut, la gauche, et z-index placer sur nous. Il est possible de remplacer certains de nos styles précédents par des traductions:\n.outils \r\n  position: absolue;\r\n  fond: vert;\r\n  / *\r\n    gauche: 35px;\r\n    haut: 25px;\r\n  * /\r\n  transform-origin: 0 0;\r\n  transformer: traduire (35px, 25px);\r\n  largeur: 200 px;\r\n  hauteur: 400px;\r\n  indice z: 3;\r\n\r\n\nAu lieu de définir la gauche et Haut compensations (avec une origine supposée de 0px de gauche et 0px par le haut), nous pouvons déclarer une origine explicite. Nous pouvons effectuer toutes sortes de transformations sur cet élément, pour lesquelles utiliser 0 0 comme centre. traduire (35px, 25px) déplace l&#39;élément 35px à droite et 25px vers le bas. Nous pouvons utiliser des valeurs négatives pour déplacer l&#39;élément vers la gauche et / ou vers le haut.\nAvec la possibilité de définir une origine pour nos transformations, nous pouvons également commencer à faire d&#39;autres choses intéressantes. Par exemple, nous pouvons faire pivoter et mettre à l&#39;échelle des éléments:\ntransform-origin: centre;\r\ntransformer: l&#39;échelle (0,5) tourne (45 degrés);\r\n\nChaque élément commence par un défaut transform-origin de 50% 50% 0, mais une valeur de centre ensembles X, y, et z à l&#39;équivalent de 50%. Nous pouvons adapter notre élément à une valeur comprise entre 0 et 1et faites-le pivoter (dans le sens horaire) de degrés ou de radians. Et nous pouvons convertir entre les deux avec:\n\n45deg = (45 * Math.PI) / 180 ≅ 0.79rad\n0.79rad = (0,79 * 180) / Math.PI ≅ 45deg\n\nPour faire pivoter un élément dans le sens inverse des aiguilles d&#39;une montre, il suffit d&#39;utiliser un négatif deg ou rad valeur.\nCe qui est encore plus intéressant, à propos de ces transformations, c&#39;est que nous pouvons en utiliser des versions 3D.\n\nLes navigateurs Evergreen prennent assez bien en charge ces styles, bien qu&#39;ils puissent nécessiter des préfixes de fournisseurs. CodePen a une option &quot;autoprefix&quot; soignée, mais vous pouvez ajouter des bibliothèques comme PostCSS à votre code local pour obtenir la même chose.\n\nLe premier bloc\nCommençons à créer notre monde 3D. Nous allons commencer par créer un espace dans lequel placer nos blocs. Créez un nouveau fichier, appelé index.html:\n\r\n\r\n  \r\n    \r\n  \r\n  \r\n    \r\n    \r\n    \r\n    \r\n  \r\n\r\n\nIci, nous étirons le corps sur toute la largeur et la hauteur, réinitialisant le rembourrage 0px. Ensuite, nous créons un petit div.scene, que nous utiliserons pour contenir différents blocs. Nous utilisons 50% la gauche et Haut, ainsi qu&#39;une gauche et un haut négatifs marge (égal à la moitié du largeur et la taille) pour le centrer horizontalement et verticalement. Ensuite, nous l&#39;inclinons légèrement (en utilisant la rotation 3D) afin d&#39;avoir une vue en perspective de l&#39;emplacement des blocs.\n\nRemarquez comment nous définissons style de transformation: préserver-3d. C&#39;est ainsi que les éléments enfants peuvent également être manipulés dans un espace 3D.\n\nLe résultat devrait ressembler à ceci:\nVoir la scène vide du stylo par SitePoint (@SitePoint) sur CodePen.\nMaintenant, commençons à ajouter une forme de bloc à la scène. Nous devons créer un nouveau fichier JavaScript, appelé block.js:\n&quot;utiliser strictement&quot;\r\n\r\nbloc de classe \r\n  constructeur (x, y, z) \r\n    this.x = x;\r\n    this.y = y;\r\n    this.z = z;\r\n\r\n    this.build ();\r\n  \r\n\r\n  construire() \r\n    // TODO: construire le bloc\r\n  \r\n\r\n  createFace (type, x, y, z, rx, ry, rz) \r\n    // TODO: retourne une face de bloc\r\n  \r\n\r\n  createTexture (type) \r\n    // TODO: obtenez la texture\r\n  \r\n\r\n\nChaque bloc doit avoir une forme 3D à 6 faces. Nous pouvons diviser les différentes parties de la construction en méthodes pour (1) construire le bloc entier, (2) construire chaque surface et (3) obtenir la texture de chaque surface.\nChacun de ces comportements (ou méthodes) est contenu dans une classe ES6. C’est une bonne façon de regrouper les structures de données et les méthodes qui les opèrent ensemble. Vous connaissez peut-être la forme traditionnelle:\nbloc fonction (x, y, z) \r\n  this.x = x;\r\n  this.y = y;\r\n  this.z = z;\r\n\r\n  this.build ();\r\n\r\n\r\nvar proto = Block.prototype;\r\n\r\nproto.build = function () \r\n  // TODO: construire le bloc\r\n;\r\n\r\nproto.createFace = fonction (type, x, y, z, rx, ry, rz) \r\n  // TODO: retourne une face de bloc\r\n\r\n\r\nproto.createTexture = fonction (type) \r\n  // TODO: obtenez la texture\r\n\r\n\nCela peut sembler un peu différent, mais c&#39;est à peu près la même chose. En plus d&#39;une syntaxe plus courte, les classes ES6 fournissent également des raccourcis pour étendre les prototypes et appeler les méthodes remplacées. Mais je m&#39;égare…\nTravaillons de bas en haut:\ncreateFace (type, x, y, z, rx, ry, rz) \r\n  retourner $ (`&#39;)\r\n    .css (\r\n      transformer: `\r\n        translateX ($ x px)\r\n        translateY ($ y px)\r\n        translateZ ($ z px)\r\n        rotationX ($ rx deg)\r\n        rotationY ($ ry deg)\r\n        rotationZ ($ rz deg)\r\n      &quot;,\r\n      fond: this.createTexture (type)\r\n    );\r\n\r\n\r\ncreateTexture (type) \r\n  retour `rgba (100, 100, 255, 0,2)`;\r\n\r\n\nChaque surface (ou face) est constituée d&#39;un div tourné et translaté. Nous ne pouvons pas rendre les éléments plus épais que 1px, mais nous pouvons simuler la profondeur en couvrant tous les trous et en utilisant plusieurs éléments parallèles les uns aux autres. On peut donner au bloc l&#39;illusion de profondeur, même s&#39;il est creux.\nÀ cette fin, le createFace prend un ensemble de coordonnées:  X, y, et z pour la position du visage. Nous fournissons également des rotations pour chaque axe, afin que nous puissions appeler createFace avec n&#39;importe quelle configuration et il traduira et fera pivoter le visage comme nous le voulons.\nConstruisons la forme de base:\nconstruire() \r\n  taille const = 64;\r\n  const x = this.x * taille;\r\n  const y = this.y * taille;\r\n  const z = this.z * taille;\r\n\r\n  const block = this.block = $ (`&#39;)\r\n    .css (\r\n      transformer: `\r\n        translateX ($ x px)\r\n        translateY ($ y px)\r\n        translateZ ($ z px)\r\n      &quot;\r\n    );\r\n\r\n  $ (`&#39;)\r\n    .appendTo (bloquer)\r\n    .css (\r\n      transformer: `\r\n        rotationX (90deg)\r\n        rotationY (0deg)\r\n        rotationZ (0deg)\r\n      &quot;\r\n    );\r\n\r\n  $ (`&#39;)\r\n    .appendTo (bloquer)\r\n    .css (\r\n      transformer: `\r\n        rotationX (0deg)\r\n        rotationY (90deg)\r\n        rotationZ (0deg)\r\n      &quot;\r\n    );\r\n\r\n  $ (`&#39;)\r\n    .appendTo (block);\r\n\r\n\nNous avons l&#39;habitude de penser en termes de positions d&#39;un pixel, mais un jeu comme Minecraft fonctionne à plus grande échelle. Chaque bloc est plus grand et le système de coordonnées traite de la position du bloc, pas des pixels individuels qui le composent. Je veux transmettre le même genre d&#39;idée ici…\nLorsque quelqu&#39;un crée un nouveau bloc, à 1 × 2 × 3, Je veux que cela signifie 0px × 64px × 128px. Nous multiplions donc chaque coordonnée par la taille par défaut (dans ce cas 64px, car c&#39;est la taille des textures du pack de textures que nous utiliserons).\nEnsuite, nous créons un conteneur div (que nous appelons div.block). À l&#39;intérieur, nous plaçons encore 3 divisions. Ceux-ci nous montreront l&#39;axe de notre bloc &#8211; ils sont comme des guides dans un programme de rendu 3D. Nous devons également ajouter de nouveaux CSS pour notre bloc:\n.bloquer \r\n  position: absolue;\r\n  gauche: 0;\r\n  en haut: 0;\r\n  largeur: 64px;\r\n  hauteur: 64px;\r\n  transformer-style: préserver-3d;\r\n  origine de transformation: 50% 50% 50%;\r\n\r\n\r\naxe .x,\r\naxe-y,\r\n.Axe z \r\n  position: absolue;\r\n  gauche: 0;\r\n  en haut: 0;\r\n  largeur: 66px;\r\n  hauteur: 66px;\r\n  origine de transformation: 50% 50% 50%;\r\n\r\n\r\nAxe .x \r\n  bordure: solide 2px rgba (255, 0, 0, 0,3);\r\n\r\n\r\nAxe .y \r\n  bordure: solide 2px rgba (0, 255, 0, 0,3);\r\n\r\n\r\n.Axe z \r\n  bordure: solide 2px rgba (0, 0, 255, 0,3);\r\n\r\n\nCe style est similaire à ce que nous avons vu auparavant. Nous devons nous rappeler de définir style de transformation: préserver-3d sur le .bloquer, afin que les axes soient rendus dans leur propre espace 3D. Nous leur donnons une couleur différente et les rendons légèrement plus grands que le bloc dans lequel ils sont contenus. C&#39;est pour qu&#39;ils soient visibles même lorsque le bloc a des côtés.\nCréons un nouveau bloc et ajoutons-le au div.scene:\nlet first = new Block (1, 1, 1);\r\n\r\n$ (&quot;. scene&quot;). append (first.block);\r\n\nLe résultat devrait ressembler à ceci:\nVoir le Pen Basic 3D Block de SitePoint (@SitePoint) sur CodePen.\nMaintenant, ajoutons ces visages:\ncette\r\n  .createFace (&quot;top&quot;, 0, 0, size / 2, 0, 0, 0)\r\n  .appendTo (block);\r\n\r\ncette\r\n  .createFace (&quot;side-1&quot;, 0, taille / 2, 0, 270, 0, 0)\r\n  .appendTo (block);\r\n\r\ncette\r\n  .createFace (&quot;side-2&quot;, taille / 2, 0, 0, 0, 90, 0)\r\n  .appendTo (block);\r\n\r\ncette\r\n  .createFace (&quot;côté-3&quot;, 0, taille / -2, 0, -270, 0, 0)\r\n  .appendTo (block);\r\n\r\ncette\r\n  .createFace (&quot;side-4&quot;, taille / -2, 0, 0, 0, -90, 0)\r\n  .appendTo (block);\r\n\r\ncette\r\n  .createFace (&quot;bas&quot;, 0, 0, taille / -2, 0, 180, 0)\r\n  .appendTo (block);\r\n\nJ&#39;ai trouvé ce code un peu d&#39;essai et d&#39;erreur (en raison de mon expérience limitée avec la perspective 3D). Chaque élément commence exactement dans la même position que le div.z-axe élément. Autrement dit, dans le centre vertical de la div.block et face au sommet.\nDonc, pour l&#39;élément &quot;top&quot;, j&#39;ai dû le traduire &quot;vers le haut&quot; de la moitié de la taille du bloc, mais je n&#39;ai pas eu à le faire pivoter de quelque façon que ce soit. Pour l&#39;élément «bas», j&#39;ai dû le faire pivoter de 180 degrés (le long de l&#39;axe x ou y), et le déplacer de la moitié de la taille du bloc.\nEn utilisant une pensée similaire, j&#39;ai tourné et traduit chacun des côtés restants. J&#39;ai également dû leur ajouter du CSS correspondant:\n.side \r\n  position: absolue;\r\n  gauche: 0;\r\n  en haut: 0;\r\n  largeur: 64px;\r\n  hauteur: 64px;\r\n  visibilité arrière: cachée;\r\n  contour: 1px rgba solide (0, 0, 0, 0,3);\r\n\r\n\nAjouter Visibilité arrière: caché empêche le rendu du côté «bas» des éléments. Habituellement, ils semblaient juste les mêmes (seulement en miroir), peu importe la façon dont ils étaient tournés. Avec les faces arrière cachées, seul le côté «supérieur» est rendu. Soyez prudent lors de l&#39;activation: vos surfaces doivent être tournées dans le bon sens ou les côtés du bloc disparaîtront. C’est la raison des rotations 90/270 / -90 / -270 que j’ai données aux côtés.\nVoir le Pen 3D Block Sides by SitePoint (@SitePoint) sur CodePen.\nRendons ce bloc un peu plus réaliste. Nous devons créer un nouveau fichier, appelé block.dirt.jset remplacer la createTexture méthode:\n&quot;utiliser strictement&quot;\r\n\r\nconst DIRT_TEXTURES = \r\n  &quot;Haut&quot;: [\r\n    \"textures/dirt-top-1.png\",\r\n    \"textures/dirt-top-2.png\",\r\n    \"textures/dirt-top-3.png\"\r\n  ],\r\n  &quot;côté&quot;: [\r\n    \"textures/dirt-side-1.png\",\r\n    \"textures/dirt-side-2.png\",\r\n    \"textures/dirt-side-3.png\",\r\n    \"textures/dirt-side-4.png\",\r\n    \"textures/dirt-side-5.png\"\r\n  ]\r\n;\r\n\r\nclasse Dirt étend le bloc \r\n  createTexture (type)  type === &quot;bottom&quot;) \r\n      const texture = DIRT_TEXTURES.top.random ();\r\n\r\n      return `url ($ texture)`;\r\n    \r\n\r\n    const texture = DIRT_TEXTURES.side.random ();\r\n\r\n    return `url ($ texture)`;\r\n  \r\n\r\n\r\nBlock.Dirt = Dirt;\r\n\n\nNous allons utiliser un pack de texture populaire, appelé Sphax PureBDCraft. Il est gratuit à télécharger et à utiliser (à condition que vous n&#39;essayiez pas de le vendre), et il existe en différentes tailles. J&#39;utilise le x64 version.\n\nNous commençons par définir une table de correspondance pour les textures des côtés et du haut du bloc. Le pack de textures ne spécifie pas les textures à utiliser pour le bas, nous allons donc simplement réutiliser les textures du haut.\nSi le côté ayant besoin d&#39;une texture est «haut» ou «bas», alors nous récupérons une texture aléatoire dans la liste «haut». La méthode aléatoire n&#39;existe pas jusqu&#39;à ce que nous la définissions:\nArray.prototype.random = function () \r\n  retourner ceci[Math.floor(Math.random() * this.length)];\r\n;\r\n\nDe même, si nous avons besoin d&#39;une texture pour un côté, nous en récupérons une au hasard. Ces textures sont sans couture, donc la randomisation fonctionne en notre faveur.\nLe résultat devrait ressembler à ceci:\nVoir les textures de bloc 3D Pen par SitePoint (@SitePoint) sur CodePen.\nFaire une scène\nComment rendons-nous cela interactif? Eh bien, un bon endroit pour commencer est avec une scène. Nous avons déjà placé des blocs dans la scène, il ne nous reste plus qu&#39;à activer le placement dynamique!\nPour commencer, nous pouvons rendre une surface plane de blocs:\nconst $ scene = $ (&quot;. scene&quot;);\r\n\r\npour (var x = 0; x &lt;6; x ++) \r\n  pour (var y = 0; y &lt;6; y ++) \r\n    let next = new Block.Dirt (x, y, 0);\r\n    next.block.appendTo ($ scene);\r\n  \r\n\r\n\nGénial, cela nous donne une surface plane pour commencer à ajouter des blocs. Maintenant, mettons en surbrillance les surfaces lorsque nous les survolons avec notre curseur:\n.block: hover .side \r\n  contour: 1px rgba solide (0, 255, 0, 0,5);\r\n\r\n\nIl se passe cependant quelque chose d&#39;étrange:\n\nEn effet, les surfaces se coupent de manière aléatoire. Il n&#39;y a pas de bon moyen de résoudre ce problème, mais nous pouvons l&#39;empêcher de se produire en redimensionnant légèrement les blocs:\nconst block = this.block = $ (`&#39;)\r\n  .css (\r\n    transformer: `\r\n      translateX ($ x px)\r\n      translateY ($ y px)\r\n      translateZ ($ z px)\r\n      échelle (0.99)\r\n    &quot;\r\n  );\r\n\n\nBien que cela améliore l&#39;apparence, cela affectera les performances plus il y aura de blocs dans la scène. Marchez légèrement lors de la mise à l&#39;échelle de nombreux éléments à la fois…\nÉtiquetons chaque surface avec le bloc et le type qui lui appartiennent:\ncreateFace (type, x, y, z, rx, ry, rz) \r\n  retourner $ (`&#39;)\r\n    .css (\r\n      transformer: `\r\n        translateX ($ x px)\r\n        translateY ($ y px)\r\n        translateZ ($ z px)\r\n        rotationX ($ rx deg)\r\n        rotationY ($ ry deg)\r\n        rotationZ ($ rz deg)\r\n      &quot;,\r\n      fond: this.createTexture (type)\r\n    )\r\n    .data (&quot;bloquer&quot;, ceci)\r\n    .data (&quot;type&quot;, type);\r\n\r\n\nEnsuite, en cliquant sur une surface, nous pouvons dériver un nouvel ensemble de coordonnées et créer un nouveau bloc:\nfunction createCoordinatesFrom (côté, x, y, z) \r\n  if (side == &quot;top&quot;) \r\n    z + = 1;\r\n  \r\n\r\n  si (côté == &quot;côté-1&quot;) \r\n    y + = 1;\r\n  \r\n\r\n  si (côté == &quot;côté-2&quot;) \r\n    x + = 1;\r\n  \r\n\r\n  si (côté == &quot;côté-3&quot;) \r\n    y - = 1;\r\n  \r\n\r\n  si (côté == &quot;côté-4&quot;) \r\n    x - = 1;\r\n  \r\n\r\n  si (côté == &quot;bas&quot;) \r\n    z - = 1;\r\n  \r\n\r\n  revenir [x, y, z];\r\n\r\n\r\nconst $ body = $ (&quot;corps&quot;);\r\n\r\n$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \r\n  const $ this = $ (this);\r\n  const previous = $ this.data (&quot;block&quot;);\r\n\r\n  const coordonnées = createCoordinatesFrom (\r\n    $ this.data (&quot;type&quot;),\r\n    previous.x,\r\n    previous.y,\r\n    previous.z\r\n  );\r\n\r\n  const suivant = nouveau Block.Dirt (... coordonnées);\r\n\r\n  next.block.appendTo ($ scene);\r\n);\r\n\ncreateCoordinatesFrom a une tâche simple mais importante. Compte tenu du type de côté et des coordonnées du bloc auquel il appartient, createCoordinatesFrom devrait renvoyer un nouvel ensemble de coordonnées. C&#39;est là que le nouveau bloc sera placé.\nEnsuite, nous avons joint un écouteur d&#39;événements. Il sera déclenché pour chaque côté div. qui obtient cliqué. Lorsque cela se produit, nous obtenons le bloc auquel appartient le côté et dérivons un nouvel ensemble de coordonnées pour le bloc suivant. Une fois que nous les avons, nous créons le bloc et l&#39;ajoutons à la scène.\nLe résultat est merveilleusement interactif:\nVoir la scène pré-remplie du stylo par SitePoint (@SitePoint) sur CodePen.\nVoir les fantômes\nIl serait utile de voir un aperçu du bloc que nous allons placer avant de le placer. On parle parfois de «montrer un fantôme» de la chose que nous allons faire.\nLe code pour l&#39;activer est assez similaire à celui que nous avons déjà vu:\nlaissez ghost = null;\r\n\r\nfonction removeGhost () \r\n  si (fantôme) \r\n    ghost.block.remove ();\r\n    fantôme = null;\r\n  \r\n\r\n\r\nfonction createGhostAt (x, y, z) \r\n  const next = new Block.Dirt (x, y, z);\r\n\r\n  next.block\r\n    .addClass (&quot;fantôme&quot;)\r\n    .appendTo ($ scene);\r\n\r\n  fantôme = suivant;\r\n\r\n\r\n$ body.on (&quot;mouseenter&quot;, &quot;.side&quot;, function (e) \r\n  removeGhost ();\r\n\r\n  const $ this = jQuery (this);\r\n  const previous = $ this.data (&quot;block&quot;);\r\n\r\n  const coordonnées = createCoordinatesFrom (\r\n    $ this.data (&quot;type&quot;),\r\n    previous.x,\r\n    previous.y,\r\n    previous.z\r\n  );\r\n\r\n  createGhostAt (... coordonnées);\r\n);\r\n\r\n$ body.on (&quot;mouseleave&quot;, &quot;.side&quot;, fonction (e) \r\n  removeGhost ();\r\n);\r\n\nLa principale différence est que nous conservons une seule instance du bloc fantôme. Au fur et à mesure que chaque nouveau est créé, l&#39;ancien est supprimé. Cela pourrait bénéficier de quelques styles supplémentaires:\n.fantôme \r\n  événements de pointeur: aucun;\r\n\r\n\r\n.ghost .side \r\n  opacité: 0,6;\r\n  événements de pointeur: aucun;\r\n  -filtre Web: luminosité (1,5);\r\n\r\n\nLaissés actifs, les événements de pointeur associés aux éléments du fantôme contrecarreraient la mouseenter et mouseleave événements du côté en dessous. Comme nous n&#39;avons pas besoin d&#39;interagir avec les éléments fantômes, nous pouvons désactiver ces événements de pointeur.\nCe résultat est plutôt soigné:\nVoir le Pen 3D Block Ghosts de SitePoint (@SitePoint) sur CodePen.\nChanger de perspective\nPlus nous ajoutons d&#39;interactivité, plus il est difficile de voir ce qui se passe. Cela semble être le bon moment pour faire quelque chose à ce sujet. Ce serait génial si nous pouvions zoomer et faire pivoter la fenêtre pour pouvoir voir ce qui se passe un peu mieux…\nCommençons par le zoom. De nombreuses interfaces (et jeux) permettent un zoom dans la fenêtre en faisant défiler la molette de la souris. Différents navigateurs gèrent les événements de la molette de la souris de différentes manières, il est donc judicieux d&#39;utiliser une bibliothèque d&#39;abstraction. \nUne fois installé, nous pouvons nous connecter aux événements:\nlaissez sceneTransformScale = 1;\r\n\r\n$ body.on (&quot;molette&quot;, fonction (événement) \r\n  if (event.originalEvent.deltaY&gt; 0) \r\n    sceneTransformScale - = 0,05;\r\n   autre \r\n    sceneTransformScale + = 0,05;\r\n  \r\n\r\n  $ scene.css (\r\n    &quot;transformer&quot;: `\r\n      scaleX ($ sceneTransformScale)\r\n      scaleY ($ sceneTransformScale)\r\n      scaleZ ($ sceneTransformScale)\r\n    &quot;\r\n  );\r\n);\r\n\n\nMaintenant, nous pouvons contrôler l&#39;échelle de la scène entière, simplement en faisant défiler la molette de la souris. Malheureusement, au moment où nous le faisons, les rotations sont annulées. Nous devons prendre en compte la rotation, car nous permettons de faire glisser la fenêtre avec la souris pour l&#39;ajuster:\nlaissez sceneTransformX = 60;\r\nlaissez sceneTransformY = 0;\r\nlaissez sceneTransformZ = 60;\r\nlaissez sceneTransformScale = 1;\r\n\r\nconst changeViewport = function () \r\n  $ scene.css (\r\n    &quot;transformer&quot;: `\r\n      rotationX ($ sceneTransformX deg)\r\n      rotationY ($ sceneTransformY deg)\r\n      rotationZ ($ sceneTransformZ deg)\r\n      scaleX ($ sceneTransformScale)\r\n      scaleY ($ sceneTransformScale)\r\n      scaleZ ($ sceneTransformScale)\r\n    &quot;\r\n  );\r\n;\r\n\nCette fonction ne tient pas seulement compte du facteur d&#39;échelle de la scène, mais également des facteurs de rotation x, y et z. Nous devons également modifier notre écouteur d&#39;événement de zoom:\n$ body.on (&quot;molette&quot;, fonction (événement) \r\n  if (event.originalEvent.deltaY&gt; 0) \r\n    sceneTransformScale - = 0,05;\r\n   autre \r\n    sceneTransformScale + = 0,05;\r\n  \r\n\r\n  changeViewport ();\r\n);\r\n\nMaintenant, nous pouvons commencer à faire pivoter la scène. Nous avons besoin:\n\nUn écouteur d&#39;événements pour le début de l&#39;action de glissement\nUn écouteur d&#39;événements lorsque la souris se déplace (tout en faisant glisser)\nUn écouteur d&#39;événements pour quand l&#39;action de glisser s&#39;arrête\n\nQuelque chose comme ça devrait faire l&#39;affaire:\nNumber.prototype.toInt = String.prototype.toInt = function () \r\n  return parseInt (this, 10);\r\n;\r\n\r\nlaissez lastMouseX = null;\r\nlaissez lastMouseY = null;\r\n\r\n$ body.on (&quot;mousedown&quot;, fonction (e) \r\n  lastMouseX = e.clientX / 10;\r\n  lastMouseY = e.clientY / 10;\r\n);\r\n\r\n$ body.on (&quot;mousemove&quot;, fonction (e) \r\n  si (! lastMouseX) \r\n    revenir;\r\n  \r\n\r\n  laissez nextMouseX = e.clientX / 10;\r\n  laissez nextMouseY = e.clientY / 10;\r\n\r\n  if (nextMouseX! == lastMouseX) \r\n    deltaX = nextMouseX.toInt () - lastMouseX.toInt ();\r\n    degrés = sceneTransformZ - deltaX;\r\n\r\n    si (degrés&gt; 360) \r\n        degrés - = 360;\r\n    \r\n\r\n    si (degrés < 0) \r\n        degrees += 360;\r\n    \r\n\r\n    sceneTransformZ = degrees;\r\n    lastMouseX = nextMouseX;\r\n\r\n    changeViewport();\r\n  \r\n\r\n  if (nextMouseY !== lastMouseY) \r\n    deltaY = nextMouseY.toInt() - lastMouseY.toInt();\r\n    degrees = sceneTransformX - deltaY;\r\n\r\n    if (degrees > 360) \r\n        degrés - = 360;\r\n    \r\n\r\n    si (degrés &lt;0) \r\n        degrés + = 360;\r\n    \r\n\r\n    sceneTransformX = degrés;\r\n    lastMouseY = nextMouseY;\r\n\r\n    changeViewport ();\r\n  \r\n);\r\n\r\n$ body.on (&quot;mouseup&quot;, fonction (e) \r\n  lastMouseX = null;\r\n  lastMouseY = null;\r\n);\r\n\nSur souris vers le bas nous capturons la souris initiale X et y coordonnées. Lorsque la souris se déplace (si le bouton est toujours enfoncé), nous ajustons la sceneTransformZ et sceneTransformX d&#39;un montant réduit. Il n&#39;y a pas de mal à laisser passer les valeurs 360 degrés ou moins 0 degrés, mais ceux-ci seraient terribles si nous voulions les afficher à l&#39;écran.\n\nCalcul à l&#39;intérieur d&#39;un déplacer la souris l&#39;écouteur d&#39;événements peut être coûteux en calcul en raison de la quantité de déclencheurs qui peuvent être déclenchés. Il y a potentiellement des millions de pixels sur l&#39;écran, et cet écouteur peut être déclenché lorsque la souris se déplace vers chacun d&#39;eux. C’est pourquoi nous quittons tôt si le bouton de la souris n’est pas maintenu enfoncé.\n\nLorsque le bouton de la souris est relâché, nous désactivons lastMouseX et lastMouseY, de sorte que la déplacer la souris l&#39;auditeur arrête de calculer les choses. On pourrait juste effacer lastMouseX, mais effacer les deux me semble plus propre.\nMalheureusement, l&#39;événement mousedown peut interférer avec l&#39;événement click sur les côtés du bloc. Nous pouvons contourner cela en empêchant le bouillonnement des événements:\n$ scene.on (&quot;mousedown&quot;, fonction (e) \r\n  e.stopPropagation ();\r\n);\r\n\nEssayer…\nVoir le zoom et la rotation du stylet par SitePoint (@SitePoint) sur CodePen.\nSuppression de blocs\nComplétons l&#39;expérience en ajoutant la possibilité de supprimer des blocs. Nous devons faire quelques choses subtiles mais importantes:\n\nChanger la couleur de la bordure de survol du vert au rouge\nDésactivez les fantômes bloqués\n\nCe sera plus facile à faire avec CSS, tant que nous avons une classe de corps pour indiquer si nous sommes en mode additionnel (normal) ou en mode soustraction:\n$ body.on (&quot;keydown&quot;, fonction (e)  e.controlKey );\r\n\r\n$ body.on (&quot;keyup&quot;, fonction (e) \r\n  $ body.removeClass (&quot;soustraction&quot;);\r\n);\r\n\nLorsqu&#39;une touche de modification est enfoncée (alt, contrôle, ou commander), ce code s&#39;assurera corps a un soustraction classe. Cela facilite le ciblage de divers éléments à l&#39;aide de cette classe:\n.subtraction .block: hover .side \r\n  contour: 1px rgba solide (255, 0, 0, 0,5);\r\n\r\n\r\n.soustraction .ghost \r\n  affichage: aucun;\r\n\r\n\n\nNous recherchons un certain nombre de touches de modification, car différents systèmes d&#39;exploitation interceptent différents modificateurs. Par exemple, touche Alt et metaKey travailler sur macOS, alors que clé de contrôle fonctionne sur Ubuntu.\nSi nous cliquons sur un bloc, lorsque nous sommes en mode soustraction, nous devons le supprimer:\n$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \r\n  const $ this = $ (this);\r\n  const previous = $ this.data (&quot;block&quot;);\r\n\r\n  if ($ body.hasClass (&quot;soustraction&quot;)) \r\n    previous.block.remove ();\r\n    précédent = null;\r\n   autre \r\n    const coordonnées = createCoordinatesFrom (\r\n      $ this.data (&quot;type&quot;),\r\n      previous.x,\r\n      previous.y,\r\n      previous.z\r\n    );\r\n\r\n    const suivant = nouveau Block.Dirt (... coordonnées);\r\n    next.block.appendTo ($ scene);\r\n  \r\n);\r\n\nC&#39;est pareil .côté écouteur d&#39;événements de clic que nous avions auparavant, mais au lieu d&#39;ajouter simplement de nouveaux blocs lorsqu&#39;un côté est cliqué, nous vérifions d&#39;abord si nous sommes en mode de soustraction. Si c&#39;est le cas, le bloc sur lequel nous venons de cliquer est supprimé de la scène.\nLa démo finale\nLa démo finale est merveilleuse pour jouer avec:\nVoir le stylo Suppression de blocs par SitePoint (@SitePoint) sur CodePen.\nIl y a un long chemin à parcourir avant de prendre en charge autant de blocs et d&#39;interactions que Minecraft, mais c&#39;est un bon début. De plus, nous avons réussi à atteindre cet objectif sans avoir à étudier des techniques 3D avancées. C’est une utilisation non conventionnelle (et créative) des transformations CSS!\nSi vous souhaitez en faire plus avec ce code, passez à l&#39;autre moitié de cette aventure. Vous n&#39;avez pas besoin d&#39;être un expert PHP pour interagir avec les serveurs Minecraft. Et imaginez les choses incroyables que vous pouvez faire avec ces connaissances…\nEt n&#39;oubliez pas: ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.\n\n\n          \n                      \nChristopher est un écrivain et codeur travaillant chez Over. Il travaille généralement sur l&#39;architecture des applications, mais parfois vous le trouverez en train de construire des compilateurs ou des robots.\n\n\nClick to rate this post!\r\n                                   \r\n                               [Total: 0  Average: 0]","paragraphs":["Cet article a été révisé par des pairs par Paul O’Brien. Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible!","J&#39;ai toujours voulu créer un jeu 3D. Je n&#39;ai tout simplement jamais eu le temps et l&#39;énergie pour apprendre les subtilités de la programmation 3D. J&#39;ai découvert que je n&#39;avais pas besoin de &#8230;\nEn bricolant un jour, j&#39;ai pensé que je pourrais peut-être simuler un environnement 3D en utilisant des transformations CSS. Je suis tombé sur un vieil article sur la création de mondes 3D avec HTML et CSS.\nJe voulais simuler un monde Minecraft (ou une infime partie de celui-ci au moins). Minecraft est un jeu de bac à sable, dans lequel vous pouvez casser et placer des blocs. Je voulais le même type de fonctionnalité, mais avec HTML, JavaScript et CSS.\nVenez décrire ce que j&#39;ai appris et comment cela peut vous aider à être plus créatif avec vos transformations CSS!","Remarque: La plupart du code de ce didacticiel se trouve sur Github. Je l&#39;ai testé dans la dernière version de Chrome. Je ne peux pas vous promettre que cela aura exactement la même apparence dans d&#39;autres navigateurs, mais les concepts de base sont universels.","Ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.\nLes choses que nous faisons déjà\nJ&#39;ai écrit ma juste part de CSS et j&#39;en suis venu à bien le comprendre, dans le but de créer des sites Web. Mais cette compréhension repose sur l&#39;hypothèse que je vais travailler dans un espace 2D.\nPrenons un exemple:\n.outils \n  position: absolue;\n  gauche: 35px;\n  haut: 25px;\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;",".Toile \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 100%;\n  hauteur: 100%;\n  indice z: 2;","Ici, nous avons un élément canvas, commençant dans le coin supérieur gauche de la page et s&#39;étendant jusqu&#39;en bas à droite. En plus de cela, nous ajoutons un élément outils. Il commence 25px de gauche et 35px en haut de la page et les mesures 200px large par 400px haute.\nSelon la commande div.tools et div.canvas sont ajoutés au balisage, il est tout à fait possible que div.canvas pourrait se chevaucher div.tools. C&#39;est à l&#39;exception de la z-index styles appliqués à chacun.\nVous êtes probablement habitué à considérer les éléments ainsi conçus comme des surfaces 2D susceptibles de se chevaucher. Mais ce chevauchement est essentiellement une troisième dimension. la gauche, Haut, et z-index peut aussi bien être renommé X, y, et z. Tant que nous supposons que chaque élément a une profondeur fixe de 1px, et z-index a un implicite px unité, nous pensons déjà en termes 3D.\nCertains d&#39;entre nous ont tendance à avoir du mal avec les concepts de rotation et de traduction dans cette troisième dimension…\nLa théorie des transformations\nLes traductions CSS dupliquent cette fonctionnalité familière, dans une API qui dépasse les limites Haut, la gauche, et z-index placer sur nous. Il est possible de remplacer certains de nos styles précédents par des traductions:\n.outils \n  position: absolue;\n  fond: vert;\n  / *\n    gauche: 35px;\n    haut: 25px;\n  * /\n  transform-origin: 0 0;\n  transformer: traduire (35px, 25px);\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;","Au lieu de définir la gauche et Haut compensations (avec une origine supposée de 0px de gauche et 0px par le haut), nous pouvons déclarer une origine explicite. Nous pouvons effectuer toutes sortes de transformations sur cet élément, pour lesquelles utiliser 0 0 comme centre. traduire (35px, 25px) déplace l&#39;élément 35px à droite et 25px vers le bas. Nous pouvons utiliser des valeurs négatives pour déplacer l&#39;élément vers la gauche et / ou vers le haut.\nAvec la possibilité de définir une origine pour nos transformations, nous pouvons également commencer à faire d&#39;autres choses intéressantes. Par exemple, nous pouvons faire pivoter et mettre à l&#39;échelle des éléments:\ntransform-origin: centre;\ntransformer: l&#39;échelle (0,5) tourne (45 degrés);","Chaque élément commence par un défaut transform-origin de 50% 50% 0, mais une valeur de centre ensembles X, y, et z à l&#39;équivalent de 50%. Nous pouvons adapter notre élément à une valeur comprise entre 0 et 1et faites-le pivoter (dans le sens horaire) de degrés ou de radians. Et nous pouvons convertir entre les deux avec:","45deg = (45 * Math.PI) / 180 ≅ 0.79rad\n0.79rad = (0,79 * 180) / Math.PI ≅ 45deg","Pour faire pivoter un élément dans le sens inverse des aiguilles d&#39;une montre, il suffit d&#39;utiliser un négatif deg ou rad valeur.\nCe qui est encore plus intéressant, à propos de ces transformations, c&#39;est que nous pouvons en utiliser des versions 3D.","Les navigateurs Evergreen prennent assez bien en charge ces styles, bien qu&#39;ils puissent nécessiter des préfixes de fournisseurs. CodePen a une option &quot;autoprefix&quot; soignée, mais vous pouvez ajouter des bibliothèques comme PostCSS à votre code local pour obtenir la même chose.","Le premier bloc\nCommençons à créer notre monde 3D. Nous allons commencer par créer un espace dans lequel placer nos blocs. Créez un nouveau fichier, appelé index.html:","Ici, nous étirons le corps sur toute la largeur et la hauteur, réinitialisant le rembourrage 0px. Ensuite, nous créons un petit div.scene, que nous utiliserons pour contenir différents blocs. Nous utilisons 50% la gauche et Haut, ainsi qu&#39;une gauche et un haut négatifs marge (égal à la moitié du largeur et la taille) pour le centrer horizontalement et verticalement. Ensuite, nous l&#39;inclinons légèrement (en utilisant la rotation 3D) afin d&#39;avoir une vue en perspective de l&#39;emplacement des blocs.","Remarquez comment nous définissons style de transformation: préserver-3d. C&#39;est ainsi que les éléments enfants peuvent également être manipulés dans un espace 3D.","Le résultat devrait ressembler à ceci:\nVoir la scène vide du stylo par SitePoint (@SitePoint) sur CodePen.\nMaintenant, commençons à ajouter une forme de bloc à la scène. Nous devons créer un nouveau fichier JavaScript, appelé block.js:\n&quot;utiliser strictement&quot;","bloc de classe \n  constructeur (x, y, z) \n    this.x = x;\n    this.y = y;\n    this.z = z;","    this.build ();\n  ","  construire() \n    // TODO: construire le bloc\n  ","  createFace (type, x, y, z, rx, ry, rz) \n    // TODO: retourne une face de bloc\n  ","  createTexture (type) \n    // TODO: obtenez la texture\n  ","Chaque bloc doit avoir une forme 3D à 6 faces. Nous pouvons diviser les différentes parties de la construction en méthodes pour (1) construire le bloc entier, (2) construire chaque surface et (3) obtenir la texture de chaque surface.\nChacun de ces comportements (ou méthodes) est contenu dans une classe ES6. C’est une bonne façon de regrouper les structures de données et les méthodes qui les opèrent ensemble. Vous connaissez peut-être la forme traditionnelle:\nbloc fonction (x, y, z) \n  this.x = x;\n  this.y = y;\n  this.z = z;","  this.build ();","var proto = Block.prototype;","proto.build = function () \n  // TODO: construire le bloc\n;","proto.createFace = fonction (type, x, y, z, rx, ry, rz) \n  // TODO: retourne une face de bloc","proto.createTexture = fonction (type) \n  // TODO: obtenez la texture","Cela peut sembler un peu différent, mais c&#39;est à peu près la même chose. En plus d&#39;une syntaxe plus courte, les classes ES6 fournissent également des raccourcis pour étendre les prototypes et appeler les méthodes remplacées. Mais je m&#39;égare…\nTravaillons de bas en haut:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    );","createTexture (type) \n  retour `rgba (100, 100, 255, 0,2)`;","Chaque surface (ou face) est constituée d&#39;un div tourné et translaté. Nous ne pouvons pas rendre les éléments plus épais que 1px, mais nous pouvons simuler la profondeur en couvrant tous les trous et en utilisant plusieurs éléments parallèles les uns aux autres. On peut donner au bloc l&#39;illusion de profondeur, même s&#39;il est creux.\nÀ cette fin, le createFace prend un ensemble de coordonnées:  X, y, et z pour la position du visage. Nous fournissons également des rotations pour chaque axe, afin que nous puissions appeler createFace avec n&#39;importe quelle configuration et il traduira et fera pivoter le visage comme nous le voulons.\nConstruisons la forme de base:\nconstruire() \n  taille const = 64;\n  const x = this.x * taille;\n  const y = this.y * taille;\n  const z = this.z * taille;","  const block = this.block = $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n      &quot;\n    );","  $ (`&#39;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (90deg)\n        rotationY (0deg)\n        rotationZ (0deg)\n      &quot;\n    );","  $ (`&#39;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (0deg)\n        rotationY (90deg)\n        rotationZ (0deg)\n      &quot;\n    );","  $ (`&#39;)\n    .appendTo (block);","Nous avons l&#39;habitude de penser en termes de positions d&#39;un pixel, mais un jeu comme Minecraft fonctionne à plus grande échelle. Chaque bloc est plus grand et le système de coordonnées traite de la position du bloc, pas des pixels individuels qui le composent. Je veux transmettre le même genre d&#39;idée ici…\nLorsque quelqu&#39;un crée un nouveau bloc, à 1 × 2 × 3, Je veux que cela signifie 0px × 64px × 128px. Nous multiplions donc chaque coordonnée par la taille par défaut (dans ce cas 64px, car c&#39;est la taille des textures du pack de textures que nous utiliserons).\nEnsuite, nous créons un conteneur div (que nous appelons div.block). À l&#39;intérieur, nous plaçons encore 3 divisions. Ceux-ci nous montreront l&#39;axe de notre bloc &#8211; ils sont comme des guides dans un programme de rendu 3D. Nous devons également ajouter de nouveaux CSS pour notre bloc:\n.bloquer \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  transformer-style: préserver-3d;\n  origine de transformation: 50% 50% 50%;","axe .x,\naxe-y,\n.Axe z \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 66px;\n  hauteur: 66px;\n  origine de transformation: 50% 50% 50%;","Axe .x \n  bordure: solide 2px rgba (255, 0, 0, 0,3);","Axe .y \n  bordure: solide 2px rgba (0, 255, 0, 0,3);",".Axe z \n  bordure: solide 2px rgba (0, 0, 255, 0,3);","Ce style est similaire à ce que nous avons vu auparavant. Nous devons nous rappeler de définir style de transformation: préserver-3d sur le .bloquer, afin que les axes soient rendus dans leur propre espace 3D. Nous leur donnons une couleur différente et les rendons légèrement plus grands que le bloc dans lequel ils sont contenus. C&#39;est pour qu&#39;ils soient visibles même lorsque le bloc a des côtés.\nCréons un nouveau bloc et ajoutons-le au div.scene:\nlet first = new Block (1, 1, 1);","$ (&quot;. scene&quot;). append (first.block);","Le résultat devrait ressembler à ceci:\nVoir le Pen Basic 3D Block de SitePoint (@SitePoint) sur CodePen.\nMaintenant, ajoutons ces visages:\ncette\n  .createFace (&quot;top&quot;, 0, 0, size / 2, 0, 0, 0)\n  .appendTo (block);","cette\n  .createFace (&quot;side-1&quot;, 0, taille / 2, 0, 270, 0, 0)\n  .appendTo (block);","cette\n  .createFace (&quot;side-2&quot;, taille / 2, 0, 0, 0, 90, 0)\n  .appendTo (block);","cette\n  .createFace (&quot;côté-3&quot;, 0, taille / -2, 0, -270, 0, 0)\n  .appendTo (block);","cette\n  .createFace (&quot;side-4&quot;, taille / -2, 0, 0, 0, -90, 0)\n  .appendTo (block);","cette\n  .createFace (&quot;bas&quot;, 0, 0, taille / -2, 0, 180, 0)\n  .appendTo (block);","J&#39;ai trouvé ce code un peu d&#39;essai et d&#39;erreur (en raison de mon expérience limitée avec la perspective 3D). Chaque élément commence exactement dans la même position que le div.z-axe élément. Autrement dit, dans le centre vertical de la div.block et face au sommet.\nDonc, pour l&#39;élément &quot;top&quot;, j&#39;ai dû le traduire &quot;vers le haut&quot; de la moitié de la taille du bloc, mais je n&#39;ai pas eu à le faire pivoter de quelque façon que ce soit. Pour l&#39;élément «bas», j&#39;ai dû le faire pivoter de 180 degrés (le long de l&#39;axe x ou y), et le déplacer de la moitié de la taille du bloc.\nEn utilisant une pensée similaire, j&#39;ai tourné et traduit chacun des côtés restants. J&#39;ai également dû leur ajouter du CSS correspondant:\n.side \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  visibilité arrière: cachée;\n  contour: 1px rgba solide (0, 0, 0, 0,3);","Ajouter Visibilité arrière: caché empêche le rendu du côté «bas» des éléments. Habituellement, ils semblaient juste les mêmes (seulement en miroir), peu importe la façon dont ils étaient tournés. Avec les faces arrière cachées, seul le côté «supérieur» est rendu. Soyez prudent lors de l&#39;activation: vos surfaces doivent être tournées dans le bon sens ou les côtés du bloc disparaîtront. C’est la raison des rotations 90/270 / -90 / -270 que j’ai données aux côtés.\nVoir le Pen 3D Block Sides by SitePoint (@SitePoint) sur CodePen.\nRendons ce bloc un peu plus réaliste. Nous devons créer un nouveau fichier, appelé block.dirt.jset remplacer la createTexture méthode:\n&quot;utiliser strictement&quot;","const DIRT_TEXTURES = \n  &quot;Haut&quot;: [\n    \"textures/dirt-top-1.png\",\n    \"textures/dirt-top-2.png\",\n    \"textures/dirt-top-3.png\"\n  ],\n  &quot;côté&quot;: [\n    \"textures/dirt-side-1.png\",\n    \"textures/dirt-side-2.png\",\n    \"textures/dirt-side-3.png\",\n    \"textures/dirt-side-4.png\",\n    \"textures/dirt-side-5.png\"\n  ]\n;","classe Dirt étend le bloc \n  createTexture (type)  type === &quot;bottom&quot;) \n      const texture = DIRT_TEXTURES.top.random ();","      return `url ($ texture)`;\n    ","    const texture = DIRT_TEXTURES.side.random ();","    return `url ($ texture)`;\n  ","Block.Dirt = Dirt;","Nous allons utiliser un pack de texture populaire, appelé Sphax PureBDCraft. Il est gratuit à télécharger et à utiliser (à condition que vous n&#39;essayiez pas de le vendre), et il existe en différentes tailles. J&#39;utilise le x64 version.","Nous commençons par définir une table de correspondance pour les textures des côtés et du haut du bloc. Le pack de textures ne spécifie pas les textures à utiliser pour le bas, nous allons donc simplement réutiliser les textures du haut.\nSi le côté ayant besoin d&#39;une texture est «haut» ou «bas», alors nous récupérons une texture aléatoire dans la liste «haut». La méthode aléatoire n&#39;existe pas jusqu&#39;à ce que nous la définissions:\nArray.prototype.random = function () \n  retourner ceci[Math.floor(Math.random() * this.length)];\n;","De même, si nous avons besoin d&#39;une texture pour un côté, nous en récupérons une au hasard. Ces textures sont sans couture, donc la randomisation fonctionne en notre faveur.\nLe résultat devrait ressembler à ceci:\nVoir les textures de bloc 3D Pen par SitePoint (@SitePoint) sur CodePen.\nFaire une scène\nComment rendons-nous cela interactif? Eh bien, un bon endroit pour commencer est avec une scène. Nous avons déjà placé des blocs dans la scène, il ne nous reste plus qu&#39;à activer le placement dynamique!\nPour commencer, nous pouvons rendre une surface plane de blocs:\nconst $ scene = $ (&quot;. scene&quot;);","pour (var x = 0; x &lt;6; x ++) \n  pour (var y = 0; y &lt;6; y ++) \n    let next = new Block.Dirt (x, y, 0);\n    next.block.appendTo ($ scene);\n  ","Génial, cela nous donne une surface plane pour commencer à ajouter des blocs. Maintenant, mettons en surbrillance les surfaces lorsque nous les survolons avec notre curseur:\n.block: hover .side \n  contour: 1px rgba solide (0, 255, 0, 0,5);","Il se passe cependant quelque chose d&#39;étrange:","En effet, les surfaces se coupent de manière aléatoire. Il n&#39;y a pas de bon moyen de résoudre ce problème, mais nous pouvons l&#39;empêcher de se produire en redimensionnant légèrement les blocs:\nconst block = this.block = $ (`&#39;)\n  .css (\n    transformer: `\n      translateX ($ x px)\n      translateY ($ y px)\n      translateZ ($ z px)\n      échelle (0.99)\n    &quot;\n  );","Bien que cela améliore l&#39;apparence, cela affectera les performances plus il y aura de blocs dans la scène. Marchez légèrement lors de la mise à l&#39;échelle de nombreux éléments à la fois…\nÉtiquetons chaque surface avec le bloc et le type qui lui appartiennent:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    )\n    .data (&quot;bloquer&quot;, ceci)\n    .data (&quot;type&quot;, type);","Ensuite, en cliquant sur une surface, nous pouvons dériver un nouvel ensemble de coordonnées et créer un nouveau bloc:\nfunction createCoordinatesFrom (côté, x, y, z) \n  if (side == &quot;top&quot;) \n    z + = 1;\n  ","  si (côté == &quot;côté-1&quot;) \n    y + = 1;\n  ","  si (côté == &quot;côté-2&quot;) \n    x + = 1;\n  ","  si (côté == &quot;côté-3&quot;) \n    y - = 1;\n  ","  si (côté == &quot;côté-4&quot;) \n    x - = 1;\n  ","  si (côté == &quot;bas&quot;) \n    z - = 1;\n  ","  revenir [x, y, z];","const $ body = $ (&quot;corps&quot;);","$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);","  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );","  const suivant = nouveau Block.Dirt (... coordonnées);","  next.block.appendTo ($ scene);\n);","createCoordinatesFrom a une tâche simple mais importante. Compte tenu du type de côté et des coordonnées du bloc auquel il appartient, createCoordinatesFrom devrait renvoyer un nouvel ensemble de coordonnées. C&#39;est là que le nouveau bloc sera placé.\nEnsuite, nous avons joint un écouteur d&#39;événements. Il sera déclenché pour chaque côté div. qui obtient cliqué. Lorsque cela se produit, nous obtenons le bloc auquel appartient le côté et dérivons un nouvel ensemble de coordonnées pour le bloc suivant. Une fois que nous les avons, nous créons le bloc et l&#39;ajoutons à la scène.\nLe résultat est merveilleusement interactif:\nVoir la scène pré-remplie du stylo par SitePoint (@SitePoint) sur CodePen.\nVoir les fantômes\nIl serait utile de voir un aperçu du bloc que nous allons placer avant de le placer. On parle parfois de «montrer un fantôme» de la chose que nous allons faire.\nLe code pour l&#39;activer est assez similaire à celui que nous avons déjà vu:\nlaissez ghost = null;","fonction removeGhost () \n  si (fantôme) \n    ghost.block.remove ();\n    fantôme = null;\n  ","fonction createGhostAt (x, y, z) \n  const next = new Block.Dirt (x, y, z);","  next.block\n    .addClass (&quot;fantôme&quot;)\n    .appendTo ($ scene);","  fantôme = suivant;","$ body.on (&quot;mouseenter&quot;, &quot;.side&quot;, function (e) \n  removeGhost ();","  const $ this = jQuery (this);\n  const previous = $ this.data (&quot;block&quot;);","  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );","  createGhostAt (... coordonnées);\n);","$ body.on (&quot;mouseleave&quot;, &quot;.side&quot;, fonction (e) \n  removeGhost ();\n);","La principale différence est que nous conservons une seule instance du bloc fantôme. Au fur et à mesure que chaque nouveau est créé, l&#39;ancien est supprimé. Cela pourrait bénéficier de quelques styles supplémentaires:\n.fantôme \n  événements de pointeur: aucun;",".ghost .side \n  opacité: 0,6;\n  événements de pointeur: aucun;\n  -filtre Web: luminosité (1,5);","Laissés actifs, les événements de pointeur associés aux éléments du fantôme contrecarreraient la mouseenter et mouseleave événements du côté en dessous. Comme nous n&#39;avons pas besoin d&#39;interagir avec les éléments fantômes, nous pouvons désactiver ces événements de pointeur.\nCe résultat est plutôt soigné:\nVoir le Pen 3D Block Ghosts de SitePoint (@SitePoint) sur CodePen.\nChanger de perspective\nPlus nous ajoutons d&#39;interactivité, plus il est difficile de voir ce qui se passe. Cela semble être le bon moment pour faire quelque chose à ce sujet. Ce serait génial si nous pouvions zoomer et faire pivoter la fenêtre pour pouvoir voir ce qui se passe un peu mieux…\nCommençons par le zoom. De nombreuses interfaces (et jeux) permettent un zoom dans la fenêtre en faisant défiler la molette de la souris. Différents navigateurs gèrent les événements de la molette de la souris de différentes manières, il est donc judicieux d&#39;utiliser une bibliothèque d&#39;abstraction. \nUne fois installé, nous pouvons nous connecter aux événements:\nlaissez sceneTransformScale = 1;","$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  ","  $ scene.css (\n    &quot;transformer&quot;: `\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n);","Maintenant, nous pouvons contrôler l&#39;échelle de la scène entière, simplement en faisant défiler la molette de la souris. Malheureusement, au moment où nous le faisons, les rotations sont annulées. Nous devons prendre en compte la rotation, car nous permettons de faire glisser la fenêtre avec la souris pour l&#39;ajuster:\nlaissez sceneTransformX = 60;\nlaissez sceneTransformY = 0;\nlaissez sceneTransformZ = 60;\nlaissez sceneTransformScale = 1;","const changeViewport = function () \n  $ scene.css (\n    &quot;transformer&quot;: `\n      rotationX ($ sceneTransformX deg)\n      rotationY ($ sceneTransformY deg)\n      rotationZ ($ sceneTransformZ deg)\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n;","Cette fonction ne tient pas seulement compte du facteur d&#39;échelle de la scène, mais également des facteurs de rotation x, y et z. Nous devons également modifier notre écouteur d&#39;événement de zoom:\n$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  ","  changeViewport ();\n);","Maintenant, nous pouvons commencer à faire pivoter la scène. Nous avons besoin:","Un écouteur d&#39;événements pour le début de l&#39;action de glissement\nUn écouteur d&#39;événements lorsque la souris se déplace (tout en faisant glisser)\nUn écouteur d&#39;événements pour quand l&#39;action de glisser s&#39;arrête","Quelque chose comme ça devrait faire l&#39;affaire:\nNumber.prototype.toInt = String.prototype.toInt = function () \n  return parseInt (this, 10);\n;","laissez lastMouseX = null;\nlaissez lastMouseY = null;","$ body.on (&quot;mousedown&quot;, fonction (e) \n  lastMouseX = e.clientX / 10;\n  lastMouseY = e.clientY / 10;\n);","$ body.on (&quot;mousemove&quot;, fonction (e) \n  si (! lastMouseX) \n    revenir;\n  ","  laissez nextMouseX = e.clientX / 10;\n  laissez nextMouseY = e.clientY / 10;","  if (nextMouseX! == lastMouseX) \n    deltaX = nextMouseX.toInt () - lastMouseX.toInt ();\n    degrés = sceneTransformZ - deltaX;","    si (degrés&gt; 360) \n        degrés - = 360;\n    ","    si (degrés < 0) \n        degrees += 360;","sceneTransformZ = degrees;\n    lastMouseX = nextMouseX;","changeViewport();","if (nextMouseY !== lastMouseY) \n    deltaY = nextMouseY.toInt() - lastMouseY.toInt();\n    degrees = sceneTransformX - deltaY;","if (degrees > 360) \n        degrés - = 360;\n    ","    si (degrés &lt;0) \n        degrés + = 360;\n    ","    sceneTransformX = degrés;\n    lastMouseY = nextMouseY;","    changeViewport ();\n  \n);","$ body.on (&quot;mouseup&quot;, fonction (e) \n  lastMouseX = null;\n  lastMouseY = null;\n);","Sur souris vers le bas nous capturons la souris initiale X et y coordonnées. Lorsque la souris se déplace (si le bouton est toujours enfoncé), nous ajustons la sceneTransformZ et sceneTransformX d&#39;un montant réduit. Il n&#39;y a pas de mal à laisser passer les valeurs 360 degrés ou moins 0 degrés, mais ceux-ci seraient terribles si nous voulions les afficher à l&#39;écran.","Calcul à l&#39;intérieur d&#39;un déplacer la souris l&#39;écouteur d&#39;événements peut être coûteux en calcul en raison de la quantité de déclencheurs qui peuvent être déclenchés. Il y a potentiellement des millions de pixels sur l&#39;écran, et cet écouteur peut être déclenché lorsque la souris se déplace vers chacun d&#39;eux. C’est pourquoi nous quittons tôt si le bouton de la souris n’est pas maintenu enfoncé.","Lorsque le bouton de la souris est relâché, nous désactivons lastMouseX et lastMouseY, de sorte que la déplacer la souris l&#39;auditeur arrête de calculer les choses. On pourrait juste effacer lastMouseX, mais effacer les deux me semble plus propre.\nMalheureusement, l&#39;événement mousedown peut interférer avec l&#39;événement click sur les côtés du bloc. Nous pouvons contourner cela en empêchant le bouillonnement des événements:\n$ scene.on (&quot;mousedown&quot;, fonction (e) \n  e.stopPropagation ();\n);","Essayer…\nVoir le zoom et la rotation du stylet par SitePoint (@SitePoint) sur CodePen.\nSuppression de blocs\nComplétons l&#39;expérience en ajoutant la possibilité de supprimer des blocs. Nous devons faire quelques choses subtiles mais importantes:","Changer la couleur de la bordure de survol du vert au rouge\nDésactivez les fantômes bloqués","Ce sera plus facile à faire avec CSS, tant que nous avons une classe de corps pour indiquer si nous sommes en mode additionnel (normal) ou en mode soustraction:\n$ body.on (&quot;keydown&quot;, fonction (e)  e.controlKey );","$ body.on (&quot;keyup&quot;, fonction (e) \n  $ body.removeClass (&quot;soustraction&quot;);\n);","Lorsqu&#39;une touche de modification est enfoncée (alt, contrôle, ou commander), ce code s&#39;assurera corps a un soustraction classe. Cela facilite le ciblage de divers éléments à l&#39;aide de cette classe:\n.subtraction .block: hover .side \n  contour: 1px rgba solide (255, 0, 0, 0,5);",".soustraction .ghost \n  affichage: aucun;","Nous recherchons un certain nombre de touches de modification, car différents systèmes d&#39;exploitation interceptent différents modificateurs. Par exemple, touche Alt et metaKey travailler sur macOS, alors que clé de contrôle fonctionne sur Ubuntu.\nSi nous cliquons sur un bloc, lorsque nous sommes en mode soustraction, nous devons le supprimer:\n$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);","  if ($ body.hasClass (&quot;soustraction&quot;)) \n    previous.block.remove ();\n    précédent = null;\n   autre \n    const coordonnées = createCoordinatesFrom (\n      $ this.data (&quot;type&quot;),\n      previous.x,\n      previous.y,\n      previous.z\n    );","    const suivant = nouveau Block.Dirt (... coordonnées);\n    next.block.appendTo ($ scene);\n  \n);","C&#39;est pareil .côté écouteur d&#39;événements de clic que nous avions auparavant, mais au lieu d&#39;ajouter simplement de nouveaux blocs lorsqu&#39;un côté est cliqué, nous vérifions d&#39;abord si nous sommes en mode de soustraction. Si c&#39;est le cas, le bloc sur lequel nous venons de cliquer est supprimé de la scène.\nLa démo finale\nLa démo finale est merveilleuse pour jouer avec:\nVoir le stylo Suppression de blocs par SitePoint (@SitePoint) sur CodePen.\nIl y a un long chemin à parcourir avant de prendre en charge autant de blocs et d&#39;interactions que Minecraft, mais c&#39;est un bon début. De plus, nous avons réussi à atteindre cet objectif sans avoir à étudier des techniques 3D avancées. C’est une utilisation non conventionnelle (et créative) des transformations CSS!\nSi vous souhaitez en faire plus avec ce code, passez à l&#39;autre moitié de cette aventure. Vous n&#39;avez pas besoin d&#39;être un expert PHP pour interagir avec les serveurs Minecraft. Et imaginez les choses incroyables que vous pouvez faire avec ces connaissances…\nEt n&#39;oubliez pas: ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.","Christopher est un écrivain et codeur travaillant chez Over. Il travaille généralement sur l&#39;architecture des applications, mais parfois vous le trouverez en train de construire des compilateurs ou des robots.","Click to rate this post!\n                                   \n                               [Total: 0  Average: 0]"],"content_blocks":[{"id":"text-1","type":"text","heading":"","plain_text":"Cet article a été révisé par des pairs par Paul O’Brien. Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible!","html":"<p>Cet article a été révisé par des pairs par Paul O’Brien. Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible!</p>"},{"id":"text-2","type":"text","heading":"","plain_text":"J&#39;ai toujours voulu créer un jeu 3D. Je n&#39;ai tout simplement jamais eu le temps et l&#39;énergie pour apprendre les subtilités de la programmation 3D. J&#39;ai découvert que je n&#39;avais pas besoin de &#8230;\nEn bricolant un jour, j&#39;ai pensé que je pourrais peut-être simuler un environnement 3D en utilisant des transformations CSS. Je suis tombé sur un vieil article sur la création de mondes 3D avec HTML et CSS.\nJe voulais simuler un monde Minecraft (ou une infime partie de celui-ci au moins). Minecraft est un jeu de bac à sable, dans lequel vous pouvez casser et placer des blocs. Je voulais le même type de fonctionnalité, mais avec HTML, JavaScript et CSS.\nVenez décrire ce que j&#39;ai appris et comment cela peut vous aider à être plus créatif avec vos transformations CSS!","html":"<p>J&#039;ai toujours voulu créer un jeu 3D. Je n&#039;ai tout simplement jamais eu le temps et l&#039;énergie pour apprendre les subtilités de la programmation 3D. J&#039;ai découvert que je n&#039;avais pas besoin de &#8230;\nEn bricolant un jour, j&#039;ai pensé que je pourrais peut-être simuler un environnement 3D en utilisant des transformations CSS. Je suis tombé sur un vieil article sur la création de mondes 3D avec HTML et CSS.\nJe voulais simuler un monde Minecraft (ou une infime partie de celui-ci au moins). Minecraft est un jeu de bac à sable, dans lequel vous pouvez casser et placer des blocs. Je voulais le même type de fonctionnalité, mais avec HTML, JavaScript et CSS.\nVenez décrire ce que j&#039;ai appris et comment cela peut vous aider à être plus créatif avec vos transformations CSS!</p>"},{"id":"text-3","type":"text","heading":"","plain_text":"Remarque: La plupart du code de ce didacticiel se trouve sur Github. Je l&#39;ai testé dans la dernière version de Chrome. Je ne peux pas vous promettre que cela aura exactement la même apparence dans d&#39;autres navigateurs, mais les concepts de base sont universels.","html":"<p>Remarque: La plupart du code de ce didacticiel se trouve sur Github. Je l&#039;ai testé dans la dernière version de Chrome. Je ne peux pas vous promettre que cela aura exactement la même apparence dans d&#039;autres navigateurs, mais les concepts de base sont universels.</p>"},{"id":"text-4","type":"text","heading":"","plain_text":"Ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.\nLes choses que nous faisons déjà\nJ&#39;ai écrit ma juste part de CSS et j&#39;en suis venu à bien le comprendre, dans le but de créer des sites Web. Mais cette compréhension repose sur l&#39;hypothèse que je vais travailler dans un espace 2D.\nPrenons un exemple:\n.outils \n  position: absolue;\n  gauche: 35px;\n  haut: 25px;\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;","html":"<p>Ce n&#039;est que la moitié de l&#039;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#039;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.\nLes choses que nous faisons déjà\nJ&#039;ai écrit ma juste part de CSS et j&#039;en suis venu à bien le comprendre, dans le but de créer des sites Web. Mais cette compréhension repose sur l&#039;hypothèse que je vais travailler dans un espace 2D.\nPrenons un exemple:\n.outils \n  position: absolue;\n  gauche: 35px;\n  haut: 25px;\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;</p>"},{"id":"text-5","type":"text","heading":"","plain_text":".Toile \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 100%;\n  hauteur: 100%;\n  indice z: 2;","html":"<p>.Toile \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 100%;\n  hauteur: 100%;\n  indice z: 2;</p>"},{"id":"text-6","type":"text","heading":"","plain_text":"Ici, nous avons un élément canvas, commençant dans le coin supérieur gauche de la page et s&#39;étendant jusqu&#39;en bas à droite. En plus de cela, nous ajoutons un élément outils. Il commence 25px de gauche et 35px en haut de la page et les mesures 200px large par 400px haute.\nSelon la commande div.tools et div.canvas sont ajoutés au balisage, il est tout à fait possible que div.canvas pourrait se chevaucher div.tools. C&#39;est à l&#39;exception de la z-index styles appliqués à chacun.\nVous êtes probablement habitué à considérer les éléments ainsi conçus comme des surfaces 2D susceptibles de se chevaucher. Mais ce chevauchement est essentiellement une troisième dimension. la gauche, Haut, et z-index peut aussi bien être renommé X, y, et z. Tant que nous supposons que chaque élément a une profondeur fixe de 1px, et z-index a un implicite px unité, nous pensons déjà en termes 3D.\nCertains d&#39;entre nous ont tendance à avoir du mal avec les concepts de rotation et de traduction dans cette troisième dimension…\nLa théorie des transformations\nLes traductions CSS dupliquent cette fonctionnalité familière, dans une API qui dépasse les limites Haut, la gauche, et z-index placer sur nous. Il est possible de remplacer certains de nos styles précédents par des traductions:\n.outils \n  position: absolue;\n  fond: vert;\n  / *\n    gauche: 35px;\n    haut: 25px;\n  * /\n  transform-origin: 0 0;\n  transformer: traduire (35px, 25px);\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;","html":"<p>Ici, nous avons un élément canvas, commençant dans le coin supérieur gauche de la page et s&#039;étendant jusqu&#039;en bas à droite. En plus de cela, nous ajoutons un élément outils. Il commence 25px de gauche et 35px en haut de la page et les mesures 200px large par 400px haute.\nSelon la commande div.tools et div.canvas sont ajoutés au balisage, il est tout à fait possible que div.canvas pourrait se chevaucher div.tools. C&#039;est à l&#039;exception de la z-index styles appliqués à chacun.\nVous êtes probablement habitué à considérer les éléments ainsi conçus comme des surfaces 2D susceptibles de se chevaucher. Mais ce chevauchement est essentiellement une troisième dimension. la gauche, Haut, et z-index peut aussi bien être renommé X, y, et z. Tant que nous supposons que chaque élément a une profondeur fixe de 1px, et z-index a un implicite px unité, nous pensons déjà en termes 3D.\nCertains d&#039;entre nous ont tendance à avoir du mal avec les concepts de rotation et de traduction dans cette troisième dimension…\nLa théorie des transformations\nLes traductions CSS dupliquent cette fonctionnalité familière, dans une API qui dépasse les limites Haut, la gauche, et z-index placer sur nous. Il est possible de remplacer certains de nos styles précédents par des traductions:\n.outils \n  position: absolue;\n  fond: vert;\n  / *\n    gauche: 35px;\n    haut: 25px;\n  * /\n  transform-origin: 0 0;\n  transformer: traduire (35px, 25px);\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;</p>"},{"id":"text-7","type":"text","heading":"","plain_text":"Au lieu de définir la gauche et Haut compensations (avec une origine supposée de 0px de gauche et 0px par le haut), nous pouvons déclarer une origine explicite. Nous pouvons effectuer toutes sortes de transformations sur cet élément, pour lesquelles utiliser 0 0 comme centre. traduire (35px, 25px) déplace l&#39;élément 35px à droite et 25px vers le bas. Nous pouvons utiliser des valeurs négatives pour déplacer l&#39;élément vers la gauche et / ou vers le haut.\nAvec la possibilité de définir une origine pour nos transformations, nous pouvons également commencer à faire d&#39;autres choses intéressantes. Par exemple, nous pouvons faire pivoter et mettre à l&#39;échelle des éléments:\ntransform-origin: centre;\ntransformer: l&#39;échelle (0,5) tourne (45 degrés);","html":"<p>Au lieu de définir la gauche et Haut compensations (avec une origine supposée de 0px de gauche et 0px par le haut), nous pouvons déclarer une origine explicite. Nous pouvons effectuer toutes sortes de transformations sur cet élément, pour lesquelles utiliser 0 0 comme centre. traduire (35px, 25px) déplace l&#039;élément 35px à droite et 25px vers le bas. Nous pouvons utiliser des valeurs négatives pour déplacer l&#039;élément vers la gauche et / ou vers le haut.\nAvec la possibilité de définir une origine pour nos transformations, nous pouvons également commencer à faire d&#039;autres choses intéressantes. Par exemple, nous pouvons faire pivoter et mettre à l&#039;échelle des éléments:\ntransform-origin: centre;\ntransformer: l&#039;échelle (0,5) tourne (45 degrés);</p>"},{"id":"text-8","type":"text","heading":"","plain_text":"Chaque élément commence par un défaut transform-origin de 50% 50% 0, mais une valeur de centre ensembles X, y, et z à l&#39;équivalent de 50%. Nous pouvons adapter notre élément à une valeur comprise entre 0 et 1et faites-le pivoter (dans le sens horaire) de degrés ou de radians. Et nous pouvons convertir entre les deux avec:","html":"<p>Chaque élément commence par un défaut transform-origin de 50% 50% 0, mais une valeur de centre ensembles X, y, et z à l&#039;équivalent de 50%. Nous pouvons adapter notre élément à une valeur comprise entre 0 et 1et faites-le pivoter (dans le sens horaire) de degrés ou de radians. Et nous pouvons convertir entre les deux avec:</p>"},{"id":"text-9","type":"text","heading":"","plain_text":"45deg = (45 * Math.PI) / 180 ≅ 0.79rad\n0.79rad = (0,79 * 180) / Math.PI ≅ 45deg","html":"<p>45deg = (45 * Math.PI) / 180 ≅ 0.79rad\n0.79rad = (0,79 * 180) / Math.PI ≅ 45deg</p>"},{"id":"text-10","type":"text","heading":"","plain_text":"Pour faire pivoter un élément dans le sens inverse des aiguilles d&#39;une montre, il suffit d&#39;utiliser un négatif deg ou rad valeur.\nCe qui est encore plus intéressant, à propos de ces transformations, c&#39;est que nous pouvons en utiliser des versions 3D.","html":"<p>Pour faire pivoter un élément dans le sens inverse des aiguilles d&#039;une montre, il suffit d&#039;utiliser un négatif deg ou rad valeur.\nCe qui est encore plus intéressant, à propos de ces transformations, c&#039;est que nous pouvons en utiliser des versions 3D.</p>"},{"id":"text-11","type":"text","heading":"","plain_text":"Les navigateurs Evergreen prennent assez bien en charge ces styles, bien qu&#39;ils puissent nécessiter des préfixes de fournisseurs. CodePen a une option &quot;autoprefix&quot; soignée, mais vous pouvez ajouter des bibliothèques comme PostCSS à votre code local pour obtenir la même chose.","html":"<p>Les navigateurs Evergreen prennent assez bien en charge ces styles, bien qu&#039;ils puissent nécessiter des préfixes de fournisseurs. CodePen a une option &quot;autoprefix&quot; soignée, mais vous pouvez ajouter des bibliothèques comme PostCSS à votre code local pour obtenir la même chose.</p>"},{"id":"text-12","type":"text","heading":"","plain_text":"Le premier bloc\nCommençons à créer notre monde 3D. Nous allons commencer par créer un espace dans lequel placer nos blocs. Créez un nouveau fichier, appelé index.html:","html":"<p>Le premier bloc\nCommençons à créer notre monde 3D. Nous allons commencer par créer un espace dans lequel placer nos blocs. Créez un nouveau fichier, appelé index.html:</p>"},{"id":"text-13","type":"text","heading":"","plain_text":"Ici, nous étirons le corps sur toute la largeur et la hauteur, réinitialisant le rembourrage 0px. Ensuite, nous créons un petit div.scene, que nous utiliserons pour contenir différents blocs. Nous utilisons 50% la gauche et Haut, ainsi qu&#39;une gauche et un haut négatifs marge (égal à la moitié du largeur et la taille) pour le centrer horizontalement et verticalement. Ensuite, nous l&#39;inclinons légèrement (en utilisant la rotation 3D) afin d&#39;avoir une vue en perspective de l&#39;emplacement des blocs.","html":"<p>Ici, nous étirons le corps sur toute la largeur et la hauteur, réinitialisant le rembourrage 0px. Ensuite, nous créons un petit div.scene, que nous utiliserons pour contenir différents blocs. Nous utilisons 50% la gauche et Haut, ainsi qu&#039;une gauche et un haut négatifs marge (égal à la moitié du largeur et la taille) pour le centrer horizontalement et verticalement. Ensuite, nous l&#039;inclinons légèrement (en utilisant la rotation 3D) afin d&#039;avoir une vue en perspective de l&#039;emplacement des blocs.</p>"},{"id":"text-14","type":"text","heading":"","plain_text":"Remarquez comment nous définissons style de transformation: préserver-3d. C&#39;est ainsi que les éléments enfants peuvent également être manipulés dans un espace 3D.","html":"<p>Remarquez comment nous définissons style de transformation: préserver-3d. C&#039;est ainsi que les éléments enfants peuvent également être manipulés dans un espace 3D.</p>"},{"id":"text-15","type":"text","heading":"","plain_text":"Le résultat devrait ressembler à ceci:\nVoir la scène vide du stylo par SitePoint (@SitePoint) sur CodePen.\nMaintenant, commençons à ajouter une forme de bloc à la scène. Nous devons créer un nouveau fichier JavaScript, appelé block.js:\n&quot;utiliser strictement&quot;","html":"<p>Le résultat devrait ressembler à ceci:\nVoir la scène vide du stylo par SitePoint (@SitePoint) sur CodePen.\nMaintenant, commençons à ajouter une forme de bloc à la scène. Nous devons créer un nouveau fichier JavaScript, appelé block.js:\n&quot;utiliser strictement&quot;</p>"},{"id":"text-16","type":"text","heading":"","plain_text":"bloc de classe \n  constructeur (x, y, z) \n    this.x = x;\n    this.y = y;\n    this.z = z;","html":"<p>bloc de classe \n  constructeur (x, y, z) \n    this.x = x;\n    this.y = y;\n    this.z = z;</p>"},{"id":"text-17","type":"text","heading":"","plain_text":"    this.build ();\n  ","html":"<p>    this.build ();\n  </p>"},{"id":"text-18","type":"text","heading":"","plain_text":"  construire() \n    // TODO: construire le bloc\n  ","html":"<p>  construire() \n    // TODO: construire le bloc\n  </p>"},{"id":"text-19","type":"text","heading":"","plain_text":"  createFace (type, x, y, z, rx, ry, rz) \n    // TODO: retourne une face de bloc\n  ","html":"<p>  createFace (type, x, y, z, rx, ry, rz) \n    // TODO: retourne une face de bloc\n  </p>"},{"id":"text-20","type":"text","heading":"","plain_text":"  createTexture (type) \n    // TODO: obtenez la texture\n  ","html":"<p>  createTexture (type) \n    // TODO: obtenez la texture\n  </p>"},{"id":"text-21","type":"text","heading":"","plain_text":"Chaque bloc doit avoir une forme 3D à 6 faces. Nous pouvons diviser les différentes parties de la construction en méthodes pour (1) construire le bloc entier, (2) construire chaque surface et (3) obtenir la texture de chaque surface.\nChacun de ces comportements (ou méthodes) est contenu dans une classe ES6. C’est une bonne façon de regrouper les structures de données et les méthodes qui les opèrent ensemble. Vous connaissez peut-être la forme traditionnelle:\nbloc fonction (x, y, z) \n  this.x = x;\n  this.y = y;\n  this.z = z;","html":"<p>Chaque bloc doit avoir une forme 3D à 6 faces. Nous pouvons diviser les différentes parties de la construction en méthodes pour (1) construire le bloc entier, (2) construire chaque surface et (3) obtenir la texture de chaque surface.\nChacun de ces comportements (ou méthodes) est contenu dans une classe ES6. C’est une bonne façon de regrouper les structures de données et les méthodes qui les opèrent ensemble. Vous connaissez peut-être la forme traditionnelle:\nbloc fonction (x, y, z) \n  this.x = x;\n  this.y = y;\n  this.z = z;</p>"},{"id":"text-22","type":"text","heading":"","plain_text":"  this.build ();","html":"<p>  this.build ();</p>"},{"id":"text-23","type":"text","heading":"","plain_text":"var proto = Block.prototype;","html":"<p>var proto = Block.prototype;</p>"},{"id":"text-24","type":"text","heading":"","plain_text":"proto.build = function () \n  // TODO: construire le bloc\n;","html":"<p>proto.build = function () \n  // TODO: construire le bloc\n;</p>"},{"id":"text-25","type":"text","heading":"","plain_text":"proto.createFace = fonction (type, x, y, z, rx, ry, rz) \n  // TODO: retourne une face de bloc","html":"<p>proto.createFace = fonction (type, x, y, z, rx, ry, rz) \n  // TODO: retourne une face de bloc</p>"},{"id":"text-26","type":"text","heading":"","plain_text":"proto.createTexture = fonction (type) \n  // TODO: obtenez la texture","html":"<p>proto.createTexture = fonction (type) \n  // TODO: obtenez la texture</p>"},{"id":"text-27","type":"text","heading":"","plain_text":"Cela peut sembler un peu différent, mais c&#39;est à peu près la même chose. En plus d&#39;une syntaxe plus courte, les classes ES6 fournissent également des raccourcis pour étendre les prototypes et appeler les méthodes remplacées. Mais je m&#39;égare…\nTravaillons de bas en haut:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    );","html":"<p>Cela peut sembler un peu différent, mais c&#039;est à peu près la même chose. En plus d&#039;une syntaxe plus courte, les classes ES6 fournissent également des raccourcis pour étendre les prototypes et appeler les méthodes remplacées. Mais je m&#039;égare…\nTravaillons de bas en haut:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#039;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    );</p>"},{"id":"text-28","type":"text","heading":"","plain_text":"createTexture (type) \n  retour `rgba (100, 100, 255, 0,2)`;","html":"<p>createTexture (type) \n  retour `rgba (100, 100, 255, 0,2)`;</p>"},{"id":"text-29","type":"text","heading":"","plain_text":"Chaque surface (ou face) est constituée d&#39;un div tourné et translaté. Nous ne pouvons pas rendre les éléments plus épais que 1px, mais nous pouvons simuler la profondeur en couvrant tous les trous et en utilisant plusieurs éléments parallèles les uns aux autres. On peut donner au bloc l&#39;illusion de profondeur, même s&#39;il est creux.\nÀ cette fin, le createFace prend un ensemble de coordonnées:  X, y, et z pour la position du visage. Nous fournissons également des rotations pour chaque axe, afin que nous puissions appeler createFace avec n&#39;importe quelle configuration et il traduira et fera pivoter le visage comme nous le voulons.\nConstruisons la forme de base:\nconstruire() \n  taille const = 64;\n  const x = this.x * taille;\n  const y = this.y * taille;\n  const z = this.z * taille;","html":"<p>Chaque surface (ou face) est constituée d&#039;un div tourné et translaté. Nous ne pouvons pas rendre les éléments plus épais que 1px, mais nous pouvons simuler la profondeur en couvrant tous les trous et en utilisant plusieurs éléments parallèles les uns aux autres. On peut donner au bloc l&#039;illusion de profondeur, même s&#039;il est creux.\nÀ cette fin, le createFace prend un ensemble de coordonnées:  X, y, et z pour la position du visage. Nous fournissons également des rotations pour chaque axe, afin que nous puissions appeler createFace avec n&#039;importe quelle configuration et il traduira et fera pivoter le visage comme nous le voulons.\nConstruisons la forme de base:\nconstruire() \n  taille const = 64;\n  const x = this.x * taille;\n  const y = this.y * taille;\n  const z = this.z * taille;</p>"},{"id":"text-30","type":"text","heading":"","plain_text":"  const block = this.block = $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n      &quot;\n    );","html":"<p>  const block = this.block = $ (`&#039;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n      &quot;\n    );</p>"},{"id":"text-31","type":"text","heading":"","plain_text":"  $ (`&#39;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (90deg)\n        rotationY (0deg)\n        rotationZ (0deg)\n      &quot;\n    );","html":"<p>  $ (`&#039;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (90deg)\n        rotationY (0deg)\n        rotationZ (0deg)\n      &quot;\n    );</p>"},{"id":"text-32","type":"text","heading":"","plain_text":"  $ (`&#39;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (0deg)\n        rotationY (90deg)\n        rotationZ (0deg)\n      &quot;\n    );","html":"<p>  $ (`&#039;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (0deg)\n        rotationY (90deg)\n        rotationZ (0deg)\n      &quot;\n    );</p>"},{"id":"text-33","type":"text","heading":"","plain_text":"  $ (`&#39;)\n    .appendTo (block);","html":"<p>  $ (`&#039;)\n    .appendTo (block);</p>"},{"id":"text-34","type":"text","heading":"","plain_text":"Nous avons l&#39;habitude de penser en termes de positions d&#39;un pixel, mais un jeu comme Minecraft fonctionne à plus grande échelle. Chaque bloc est plus grand et le système de coordonnées traite de la position du bloc, pas des pixels individuels qui le composent. Je veux transmettre le même genre d&#39;idée ici…\nLorsque quelqu&#39;un crée un nouveau bloc, à 1 × 2 × 3, Je veux que cela signifie 0px × 64px × 128px. Nous multiplions donc chaque coordonnée par la taille par défaut (dans ce cas 64px, car c&#39;est la taille des textures du pack de textures que nous utiliserons).\nEnsuite, nous créons un conteneur div (que nous appelons div.block). À l&#39;intérieur, nous plaçons encore 3 divisions. Ceux-ci nous montreront l&#39;axe de notre bloc &#8211; ils sont comme des guides dans un programme de rendu 3D. Nous devons également ajouter de nouveaux CSS pour notre bloc:\n.bloquer \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  transformer-style: préserver-3d;\n  origine de transformation: 50% 50% 50%;","html":"<p>Nous avons l&#039;habitude de penser en termes de positions d&#039;un pixel, mais un jeu comme Minecraft fonctionne à plus grande échelle. Chaque bloc est plus grand et le système de coordonnées traite de la position du bloc, pas des pixels individuels qui le composent. Je veux transmettre le même genre d&#039;idée ici…\nLorsque quelqu&#039;un crée un nouveau bloc, à 1 × 2 × 3, Je veux que cela signifie 0px × 64px × 128px. Nous multiplions donc chaque coordonnée par la taille par défaut (dans ce cas 64px, car c&#039;est la taille des textures du pack de textures que nous utiliserons).\nEnsuite, nous créons un conteneur div (que nous appelons div.block). À l&#039;intérieur, nous plaçons encore 3 divisions. Ceux-ci nous montreront l&#039;axe de notre bloc &#8211; ils sont comme des guides dans un programme de rendu 3D. Nous devons également ajouter de nouveaux CSS pour notre bloc:\n.bloquer \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  transformer-style: préserver-3d;\n  origine de transformation: 50% 50% 50%;</p>"},{"id":"text-35","type":"text","heading":"","plain_text":"axe .x,\naxe-y,\n.Axe z \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 66px;\n  hauteur: 66px;\n  origine de transformation: 50% 50% 50%;","html":"<p>axe .x,\naxe-y,\n.Axe z \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 66px;\n  hauteur: 66px;\n  origine de transformation: 50% 50% 50%;</p>"},{"id":"text-36","type":"text","heading":"","plain_text":"Axe .x \n  bordure: solide 2px rgba (255, 0, 0, 0,3);","html":"<p>Axe .x \n  bordure: solide 2px rgba (255, 0, 0, 0,3);</p>"},{"id":"text-37","type":"text","heading":"","plain_text":"Axe .y \n  bordure: solide 2px rgba (0, 255, 0, 0,3);","html":"<p>Axe .y \n  bordure: solide 2px rgba (0, 255, 0, 0,3);</p>"},{"id":"text-38","type":"text","heading":"","plain_text":".Axe z \n  bordure: solide 2px rgba (0, 0, 255, 0,3);","html":"<p>.Axe z \n  bordure: solide 2px rgba (0, 0, 255, 0,3);</p>"},{"id":"text-39","type":"text","heading":"","plain_text":"Ce style est similaire à ce que nous avons vu auparavant. Nous devons nous rappeler de définir style de transformation: préserver-3d sur le .bloquer, afin que les axes soient rendus dans leur propre espace 3D. Nous leur donnons une couleur différente et les rendons légèrement plus grands que le bloc dans lequel ils sont contenus. C&#39;est pour qu&#39;ils soient visibles même lorsque le bloc a des côtés.\nCréons un nouveau bloc et ajoutons-le au div.scene:\nlet first = new Block (1, 1, 1);","html":"<p>Ce style est similaire à ce que nous avons vu auparavant. Nous devons nous rappeler de définir style de transformation: préserver-3d sur le .bloquer, afin que les axes soient rendus dans leur propre espace 3D. Nous leur donnons une couleur différente et les rendons légèrement plus grands que le bloc dans lequel ils sont contenus. C&#039;est pour qu&#039;ils soient visibles même lorsque le bloc a des côtés.\nCréons un nouveau bloc et ajoutons-le au div.scene:\nlet first = new Block (1, 1, 1);</p>"},{"id":"text-40","type":"text","heading":"","plain_text":"$ (&quot;. scene&quot;). append (first.block);","html":"<p>$ (&quot;. scene&quot;). append (first.block);</p>"},{"id":"text-41","type":"text","heading":"","plain_text":"Le résultat devrait ressembler à ceci:\nVoir le Pen Basic 3D Block de SitePoint (@SitePoint) sur CodePen.\nMaintenant, ajoutons ces visages:\ncette\n  .createFace (&quot;top&quot;, 0, 0, size / 2, 0, 0, 0)\n  .appendTo (block);","html":"<p>Le résultat devrait ressembler à ceci:\nVoir le Pen Basic 3D Block de SitePoint (@SitePoint) sur CodePen.\nMaintenant, ajoutons ces visages:\ncette\n  .createFace (&quot;top&quot;, 0, 0, size / 2, 0, 0, 0)\n  .appendTo (block);</p>"},{"id":"text-42","type":"text","heading":"","plain_text":"cette\n  .createFace (&quot;side-1&quot;, 0, taille / 2, 0, 270, 0, 0)\n  .appendTo (block);","html":"<p>cette\n  .createFace (&quot;side-1&quot;, 0, taille / 2, 0, 270, 0, 0)\n  .appendTo (block);</p>"},{"id":"text-43","type":"text","heading":"","plain_text":"cette\n  .createFace (&quot;side-2&quot;, taille / 2, 0, 0, 0, 90, 0)\n  .appendTo (block);","html":"<p>cette\n  .createFace (&quot;side-2&quot;, taille / 2, 0, 0, 0, 90, 0)\n  .appendTo (block);</p>"},{"id":"text-44","type":"text","heading":"","plain_text":"cette\n  .createFace (&quot;côté-3&quot;, 0, taille / -2, 0, -270, 0, 0)\n  .appendTo (block);","html":"<p>cette\n  .createFace (&quot;côté-3&quot;, 0, taille / -2, 0, -270, 0, 0)\n  .appendTo (block);</p>"},{"id":"text-45","type":"text","heading":"","plain_text":"cette\n  .createFace (&quot;side-4&quot;, taille / -2, 0, 0, 0, -90, 0)\n  .appendTo (block);","html":"<p>cette\n  .createFace (&quot;side-4&quot;, taille / -2, 0, 0, 0, -90, 0)\n  .appendTo (block);</p>"},{"id":"text-46","type":"text","heading":"","plain_text":"cette\n  .createFace (&quot;bas&quot;, 0, 0, taille / -2, 0, 180, 0)\n  .appendTo (block);","html":"<p>cette\n  .createFace (&quot;bas&quot;, 0, 0, taille / -2, 0, 180, 0)\n  .appendTo (block);</p>"},{"id":"text-47","type":"text","heading":"","plain_text":"J&#39;ai trouvé ce code un peu d&#39;essai et d&#39;erreur (en raison de mon expérience limitée avec la perspective 3D). Chaque élément commence exactement dans la même position que le div.z-axe élément. Autrement dit, dans le centre vertical de la div.block et face au sommet.\nDonc, pour l&#39;élément &quot;top&quot;, j&#39;ai dû le traduire &quot;vers le haut&quot; de la moitié de la taille du bloc, mais je n&#39;ai pas eu à le faire pivoter de quelque façon que ce soit. Pour l&#39;élément «bas», j&#39;ai dû le faire pivoter de 180 degrés (le long de l&#39;axe x ou y), et le déplacer de la moitié de la taille du bloc.\nEn utilisant une pensée similaire, j&#39;ai tourné et traduit chacun des côtés restants. J&#39;ai également dû leur ajouter du CSS correspondant:\n.side \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  visibilité arrière: cachée;\n  contour: 1px rgba solide (0, 0, 0, 0,3);","html":"<p>J&#039;ai trouvé ce code un peu d&#039;essai et d&#039;erreur (en raison de mon expérience limitée avec la perspective 3D). Chaque élément commence exactement dans la même position que le div.z-axe élément. Autrement dit, dans le centre vertical de la div.block et face au sommet.\nDonc, pour l&#039;élément &quot;top&quot;, j&#039;ai dû le traduire &quot;vers le haut&quot; de la moitié de la taille du bloc, mais je n&#039;ai pas eu à le faire pivoter de quelque façon que ce soit. Pour l&#039;élément «bas», j&#039;ai dû le faire pivoter de 180 degrés (le long de l&#039;axe x ou y), et le déplacer de la moitié de la taille du bloc.\nEn utilisant une pensée similaire, j&#039;ai tourné et traduit chacun des côtés restants. J&#039;ai également dû leur ajouter du CSS correspondant:\n.side \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  visibilité arrière: cachée;\n  contour: 1px rgba solide (0, 0, 0, 0,3);</p>"},{"id":"text-48","type":"text","heading":"","plain_text":"Ajouter Visibilité arrière: caché empêche le rendu du côté «bas» des éléments. Habituellement, ils semblaient juste les mêmes (seulement en miroir), peu importe la façon dont ils étaient tournés. Avec les faces arrière cachées, seul le côté «supérieur» est rendu. Soyez prudent lors de l&#39;activation: vos surfaces doivent être tournées dans le bon sens ou les côtés du bloc disparaîtront. C’est la raison des rotations 90/270 / -90 / -270 que j’ai données aux côtés.\nVoir le Pen 3D Block Sides by SitePoint (@SitePoint) sur CodePen.\nRendons ce bloc un peu plus réaliste. Nous devons créer un nouveau fichier, appelé block.dirt.jset remplacer la createTexture méthode:\n&quot;utiliser strictement&quot;","html":"<p>Ajouter Visibilité arrière: caché empêche le rendu du côté «bas» des éléments. Habituellement, ils semblaient juste les mêmes (seulement en miroir), peu importe la façon dont ils étaient tournés. Avec les faces arrière cachées, seul le côté «supérieur» est rendu. Soyez prudent lors de l&#039;activation: vos surfaces doivent être tournées dans le bon sens ou les côtés du bloc disparaîtront. C’est la raison des rotations 90/270 / -90 / -270 que j’ai données aux côtés.\nVoir le Pen 3D Block Sides by SitePoint (@SitePoint) sur CodePen.\nRendons ce bloc un peu plus réaliste. Nous devons créer un nouveau fichier, appelé block.dirt.jset remplacer la createTexture méthode:\n&quot;utiliser strictement&quot;</p>"},{"id":"text-49","type":"text","heading":"","plain_text":"const DIRT_TEXTURES = \n  &quot;Haut&quot;: [\n    \"textures/dirt-top-1.png\",\n    \"textures/dirt-top-2.png\",\n    \"textures/dirt-top-3.png\"\n  ],\n  &quot;côté&quot;: [\n    \"textures/dirt-side-1.png\",\n    \"textures/dirt-side-2.png\",\n    \"textures/dirt-side-3.png\",\n    \"textures/dirt-side-4.png\",\n    \"textures/dirt-side-5.png\"\n  ]\n;","html":"<p>const DIRT_TEXTURES = \n  &quot;Haut&quot;: [\n    &quot;textures/dirt-top-1.png&quot;,\n    &quot;textures/dirt-top-2.png&quot;,\n    &quot;textures/dirt-top-3.png&quot;\n  ],\n  &quot;côté&quot;: [\n    &quot;textures/dirt-side-1.png&quot;,\n    &quot;textures/dirt-side-2.png&quot;,\n    &quot;textures/dirt-side-3.png&quot;,\n    &quot;textures/dirt-side-4.png&quot;,\n    &quot;textures/dirt-side-5.png&quot;\n  ]\n;</p>"},{"id":"text-50","type":"text","heading":"","plain_text":"classe Dirt étend le bloc \n  createTexture (type)  type === &quot;bottom&quot;) \n      const texture = DIRT_TEXTURES.top.random ();","html":"<p>classe Dirt étend le bloc \n  createTexture (type)  type === &quot;bottom&quot;) \n      const texture = DIRT_TEXTURES.top.random ();</p>"},{"id":"text-51","type":"text","heading":"","plain_text":"      return `url ($ texture)`;\n    ","html":"<p>      return `url ($ texture)`;\n    </p>"},{"id":"text-52","type":"text","heading":"","plain_text":"    const texture = DIRT_TEXTURES.side.random ();","html":"<p>    const texture = DIRT_TEXTURES.side.random ();</p>"},{"id":"text-53","type":"text","heading":"","plain_text":"    return `url ($ texture)`;\n  ","html":"<p>    return `url ($ texture)`;\n  </p>"},{"id":"text-54","type":"text","heading":"","plain_text":"Block.Dirt = Dirt;","html":"<p>Block.Dirt = Dirt;</p>"},{"id":"text-55","type":"text","heading":"","plain_text":"Nous allons utiliser un pack de texture populaire, appelé Sphax PureBDCraft. Il est gratuit à télécharger et à utiliser (à condition que vous n&#39;essayiez pas de le vendre), et il existe en différentes tailles. J&#39;utilise le x64 version.","html":"<p>Nous allons utiliser un pack de texture populaire, appelé Sphax PureBDCraft. Il est gratuit à télécharger et à utiliser (à condition que vous n&#039;essayiez pas de le vendre), et il existe en différentes tailles. J&#039;utilise le x64 version.</p>"},{"id":"text-56","type":"text","heading":"","plain_text":"Nous commençons par définir une table de correspondance pour les textures des côtés et du haut du bloc. Le pack de textures ne spécifie pas les textures à utiliser pour le bas, nous allons donc simplement réutiliser les textures du haut.\nSi le côté ayant besoin d&#39;une texture est «haut» ou «bas», alors nous récupérons une texture aléatoire dans la liste «haut». La méthode aléatoire n&#39;existe pas jusqu&#39;à ce que nous la définissions:\nArray.prototype.random = function () \n  retourner ceci[Math.floor(Math.random() * this.length)];\n;","html":"<p>Nous commençons par définir une table de correspondance pour les textures des côtés et du haut du bloc. Le pack de textures ne spécifie pas les textures à utiliser pour le bas, nous allons donc simplement réutiliser les textures du haut.\nSi le côté ayant besoin d&#039;une texture est «haut» ou «bas», alors nous récupérons une texture aléatoire dans la liste «haut». La méthode aléatoire n&#039;existe pas jusqu&#039;à ce que nous la définissions:\nArray.prototype.random = function () \n  retourner ceci[Math.floor(Math.random() * this.length)];\n;</p>"},{"id":"text-57","type":"text","heading":"","plain_text":"De même, si nous avons besoin d&#39;une texture pour un côté, nous en récupérons une au hasard. Ces textures sont sans couture, donc la randomisation fonctionne en notre faveur.\nLe résultat devrait ressembler à ceci:\nVoir les textures de bloc 3D Pen par SitePoint (@SitePoint) sur CodePen.\nFaire une scène\nComment rendons-nous cela interactif? Eh bien, un bon endroit pour commencer est avec une scène. Nous avons déjà placé des blocs dans la scène, il ne nous reste plus qu&#39;à activer le placement dynamique!\nPour commencer, nous pouvons rendre une surface plane de blocs:\nconst $ scene = $ (&quot;. scene&quot;);","html":"<p>De même, si nous avons besoin d&#039;une texture pour un côté, nous en récupérons une au hasard. Ces textures sont sans couture, donc la randomisation fonctionne en notre faveur.\nLe résultat devrait ressembler à ceci:\nVoir les textures de bloc 3D Pen par SitePoint (@SitePoint) sur CodePen.\nFaire une scène\nComment rendons-nous cela interactif? Eh bien, un bon endroit pour commencer est avec une scène. Nous avons déjà placé des blocs dans la scène, il ne nous reste plus qu&#039;à activer le placement dynamique!\nPour commencer, nous pouvons rendre une surface plane de blocs:\nconst $ scene = $ (&quot;. scene&quot;);</p>"},{"id":"text-58","type":"text","heading":"","plain_text":"pour (var x = 0; x &lt;6; x ++) \n  pour (var y = 0; y &lt;6; y ++) \n    let next = new Block.Dirt (x, y, 0);\n    next.block.appendTo ($ scene);\n  ","html":"<p>pour (var x = 0; x &lt;6; x ++) \n  pour (var y = 0; y &lt;6; y ++) \n    let next = new Block.Dirt (x, y, 0);\n    next.block.appendTo ($ scene);\n  </p>"},{"id":"text-59","type":"text","heading":"","plain_text":"Génial, cela nous donne une surface plane pour commencer à ajouter des blocs. Maintenant, mettons en surbrillance les surfaces lorsque nous les survolons avec notre curseur:\n.block: hover .side \n  contour: 1px rgba solide (0, 255, 0, 0,5);","html":"<p>Génial, cela nous donne une surface plane pour commencer à ajouter des blocs. Maintenant, mettons en surbrillance les surfaces lorsque nous les survolons avec notre curseur:\n.block: hover .side \n  contour: 1px rgba solide (0, 255, 0, 0,5);</p>"},{"id":"text-60","type":"text","heading":"","plain_text":"Il se passe cependant quelque chose d&#39;étrange:","html":"<p>Il se passe cependant quelque chose d&#039;étrange:</p>"},{"id":"text-61","type":"text","heading":"","plain_text":"En effet, les surfaces se coupent de manière aléatoire. Il n&#39;y a pas de bon moyen de résoudre ce problème, mais nous pouvons l&#39;empêcher de se produire en redimensionnant légèrement les blocs:\nconst block = this.block = $ (`&#39;)\n  .css (\n    transformer: `\n      translateX ($ x px)\n      translateY ($ y px)\n      translateZ ($ z px)\n      échelle (0.99)\n    &quot;\n  );","html":"<p>En effet, les surfaces se coupent de manière aléatoire. Il n&#039;y a pas de bon moyen de résoudre ce problème, mais nous pouvons l&#039;empêcher de se produire en redimensionnant légèrement les blocs:\nconst block = this.block = $ (`&#039;)\n  .css (\n    transformer: `\n      translateX ($ x px)\n      translateY ($ y px)\n      translateZ ($ z px)\n      échelle (0.99)\n    &quot;\n  );</p>"},{"id":"text-62","type":"text","heading":"","plain_text":"Bien que cela améliore l&#39;apparence, cela affectera les performances plus il y aura de blocs dans la scène. Marchez légèrement lors de la mise à l&#39;échelle de nombreux éléments à la fois…\nÉtiquetons chaque surface avec le bloc et le type qui lui appartiennent:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    )\n    .data (&quot;bloquer&quot;, ceci)\n    .data (&quot;type&quot;, type);","html":"<p>Bien que cela améliore l&#039;apparence, cela affectera les performances plus il y aura de blocs dans la scène. Marchez légèrement lors de la mise à l&#039;échelle de nombreux éléments à la fois…\nÉtiquetons chaque surface avec le bloc et le type qui lui appartiennent:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#039;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    )\n    .data (&quot;bloquer&quot;, ceci)\n    .data (&quot;type&quot;, type);</p>"},{"id":"text-63","type":"text","heading":"","plain_text":"Ensuite, en cliquant sur une surface, nous pouvons dériver un nouvel ensemble de coordonnées et créer un nouveau bloc:\nfunction createCoordinatesFrom (côté, x, y, z) \n  if (side == &quot;top&quot;) \n    z + = 1;\n  ","html":"<p>Ensuite, en cliquant sur une surface, nous pouvons dériver un nouvel ensemble de coordonnées et créer un nouveau bloc:\nfunction createCoordinatesFrom (côté, x, y, z) \n  if (side == &quot;top&quot;) \n    z + = 1;\n  </p>"},{"id":"text-64","type":"text","heading":"","plain_text":"  si (côté == &quot;côté-1&quot;) \n    y + = 1;\n  ","html":"<p>  si (côté == &quot;côté-1&quot;) \n    y + = 1;\n  </p>"},{"id":"text-65","type":"text","heading":"","plain_text":"  si (côté == &quot;côté-2&quot;) \n    x + = 1;\n  ","html":"<p>  si (côté == &quot;côté-2&quot;) \n    x + = 1;\n  </p>"},{"id":"text-66","type":"text","heading":"","plain_text":"  si (côté == &quot;côté-3&quot;) \n    y - = 1;\n  ","html":"<p>  si (côté == &quot;côté-3&quot;) \n    y - = 1;\n  </p>"},{"id":"text-67","type":"text","heading":"","plain_text":"  si (côté == &quot;côté-4&quot;) \n    x - = 1;\n  ","html":"<p>  si (côté == &quot;côté-4&quot;) \n    x - = 1;\n  </p>"},{"id":"text-68","type":"text","heading":"","plain_text":"  si (côté == &quot;bas&quot;) \n    z - = 1;\n  ","html":"<p>  si (côté == &quot;bas&quot;) \n    z - = 1;\n  </p>"},{"id":"text-69","type":"text","heading":"","plain_text":"  revenir [x, y, z];","html":"<p>  revenir [x, y, z];</p>"},{"id":"text-70","type":"text","heading":"","plain_text":"const $ body = $ (&quot;corps&quot;);","html":"<p>const $ body = $ (&quot;corps&quot;);</p>"},{"id":"text-71","type":"text","heading":"","plain_text":"$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);","html":"<p>$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);</p>"},{"id":"text-72","type":"text","heading":"","plain_text":"  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );","html":"<p>  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );</p>"},{"id":"text-73","type":"text","heading":"","plain_text":"  const suivant = nouveau Block.Dirt (... coordonnées);","html":"<p>  const suivant = nouveau Block.Dirt (... coordonnées);</p>"},{"id":"text-74","type":"text","heading":"","plain_text":"  next.block.appendTo ($ scene);\n);","html":"<p>  next.block.appendTo ($ scene);\n);</p>"},{"id":"text-75","type":"text","heading":"","plain_text":"createCoordinatesFrom a une tâche simple mais importante. Compte tenu du type de côté et des coordonnées du bloc auquel il appartient, createCoordinatesFrom devrait renvoyer un nouvel ensemble de coordonnées. C&#39;est là que le nouveau bloc sera placé.\nEnsuite, nous avons joint un écouteur d&#39;événements. Il sera déclenché pour chaque côté div. qui obtient cliqué. Lorsque cela se produit, nous obtenons le bloc auquel appartient le côté et dérivons un nouvel ensemble de coordonnées pour le bloc suivant. Une fois que nous les avons, nous créons le bloc et l&#39;ajoutons à la scène.\nLe résultat est merveilleusement interactif:\nVoir la scène pré-remplie du stylo par SitePoint (@SitePoint) sur CodePen.\nVoir les fantômes\nIl serait utile de voir un aperçu du bloc que nous allons placer avant de le placer. On parle parfois de «montrer un fantôme» de la chose que nous allons faire.\nLe code pour l&#39;activer est assez similaire à celui que nous avons déjà vu:\nlaissez ghost = null;","html":"<p>createCoordinatesFrom a une tâche simple mais importante. Compte tenu du type de côté et des coordonnées du bloc auquel il appartient, createCoordinatesFrom devrait renvoyer un nouvel ensemble de coordonnées. C&#039;est là que le nouveau bloc sera placé.\nEnsuite, nous avons joint un écouteur d&#039;événements. Il sera déclenché pour chaque côté div. qui obtient cliqué. Lorsque cela se produit, nous obtenons le bloc auquel appartient le côté et dérivons un nouvel ensemble de coordonnées pour le bloc suivant. Une fois que nous les avons, nous créons le bloc et l&#039;ajoutons à la scène.\nLe résultat est merveilleusement interactif:\nVoir la scène pré-remplie du stylo par SitePoint (@SitePoint) sur CodePen.\nVoir les fantômes\nIl serait utile de voir un aperçu du bloc que nous allons placer avant de le placer. On parle parfois de «montrer un fantôme» de la chose que nous allons faire.\nLe code pour l&#039;activer est assez similaire à celui que nous avons déjà vu:\nlaissez ghost = null;</p>"},{"id":"text-76","type":"text","heading":"","plain_text":"fonction removeGhost () \n  si (fantôme) \n    ghost.block.remove ();\n    fantôme = null;\n  ","html":"<p>fonction removeGhost () \n  si (fantôme) \n    ghost.block.remove ();\n    fantôme = null;\n  </p>"},{"id":"text-77","type":"text","heading":"","plain_text":"fonction createGhostAt (x, y, z) \n  const next = new Block.Dirt (x, y, z);","html":"<p>fonction createGhostAt (x, y, z) \n  const next = new Block.Dirt (x, y, z);</p>"},{"id":"text-78","type":"text","heading":"","plain_text":"  next.block\n    .addClass (&quot;fantôme&quot;)\n    .appendTo ($ scene);","html":"<p>  next.block\n    .addClass (&quot;fantôme&quot;)\n    .appendTo ($ scene);</p>"},{"id":"text-79","type":"text","heading":"","plain_text":"  fantôme = suivant;","html":"<p>  fantôme = suivant;</p>"},{"id":"text-80","type":"text","heading":"","plain_text":"$ body.on (&quot;mouseenter&quot;, &quot;.side&quot;, function (e) \n  removeGhost ();","html":"<p>$ body.on (&quot;mouseenter&quot;, &quot;.side&quot;, function (e) \n  removeGhost ();</p>"},{"id":"text-81","type":"text","heading":"","plain_text":"  const $ this = jQuery (this);\n  const previous = $ this.data (&quot;block&quot;);","html":"<p>  const $ this = jQuery (this);\n  const previous = $ this.data (&quot;block&quot;);</p>"},{"id":"text-82","type":"text","heading":"","plain_text":"  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );","html":"<p>  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );</p>"},{"id":"text-83","type":"text","heading":"","plain_text":"  createGhostAt (... coordonnées);\n);","html":"<p>  createGhostAt (... coordonnées);\n);</p>"},{"id":"text-84","type":"text","heading":"","plain_text":"$ body.on (&quot;mouseleave&quot;, &quot;.side&quot;, fonction (e) \n  removeGhost ();\n);","html":"<p>$ body.on (&quot;mouseleave&quot;, &quot;.side&quot;, fonction (e) \n  removeGhost ();\n);</p>"},{"id":"text-85","type":"text","heading":"","plain_text":"La principale différence est que nous conservons une seule instance du bloc fantôme. Au fur et à mesure que chaque nouveau est créé, l&#39;ancien est supprimé. Cela pourrait bénéficier de quelques styles supplémentaires:\n.fantôme \n  événements de pointeur: aucun;","html":"<p>La principale différence est que nous conservons une seule instance du bloc fantôme. Au fur et à mesure que chaque nouveau est créé, l&#039;ancien est supprimé. Cela pourrait bénéficier de quelques styles supplémentaires:\n.fantôme \n  événements de pointeur: aucun;</p>"},{"id":"text-86","type":"text","heading":"","plain_text":".ghost .side \n  opacité: 0,6;\n  événements de pointeur: aucun;\n  -filtre Web: luminosité (1,5);","html":"<p>.ghost .side \n  opacité: 0,6;\n  événements de pointeur: aucun;\n  -filtre Web: luminosité (1,5);</p>"},{"id":"text-87","type":"text","heading":"","plain_text":"Laissés actifs, les événements de pointeur associés aux éléments du fantôme contrecarreraient la mouseenter et mouseleave événements du côté en dessous. Comme nous n&#39;avons pas besoin d&#39;interagir avec les éléments fantômes, nous pouvons désactiver ces événements de pointeur.\nCe résultat est plutôt soigné:\nVoir le Pen 3D Block Ghosts de SitePoint (@SitePoint) sur CodePen.\nChanger de perspective\nPlus nous ajoutons d&#39;interactivité, plus il est difficile de voir ce qui se passe. Cela semble être le bon moment pour faire quelque chose à ce sujet. Ce serait génial si nous pouvions zoomer et faire pivoter la fenêtre pour pouvoir voir ce qui se passe un peu mieux…\nCommençons par le zoom. De nombreuses interfaces (et jeux) permettent un zoom dans la fenêtre en faisant défiler la molette de la souris. Différents navigateurs gèrent les événements de la molette de la souris de différentes manières, il est donc judicieux d&#39;utiliser une bibliothèque d&#39;abstraction. \nUne fois installé, nous pouvons nous connecter aux événements:\nlaissez sceneTransformScale = 1;","html":"<p>Laissés actifs, les événements de pointeur associés aux éléments du fantôme contrecarreraient la mouseenter et mouseleave événements du côté en dessous. Comme nous n&#039;avons pas besoin d&#039;interagir avec les éléments fantômes, nous pouvons désactiver ces événements de pointeur.\nCe résultat est plutôt soigné:\nVoir le Pen 3D Block Ghosts de SitePoint (@SitePoint) sur CodePen.\nChanger de perspective\nPlus nous ajoutons d&#039;interactivité, plus il est difficile de voir ce qui se passe. Cela semble être le bon moment pour faire quelque chose à ce sujet. Ce serait génial si nous pouvions zoomer et faire pivoter la fenêtre pour pouvoir voir ce qui se passe un peu mieux…\nCommençons par le zoom. De nombreuses interfaces (et jeux) permettent un zoom dans la fenêtre en faisant défiler la molette de la souris. Différents navigateurs gèrent les événements de la molette de la souris de différentes manières, il est donc judicieux d&#039;utiliser une bibliothèque d&#039;abstraction. \nUne fois installé, nous pouvons nous connecter aux événements:\nlaissez sceneTransformScale = 1;</p>"},{"id":"text-88","type":"text","heading":"","plain_text":"$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  ","html":"<p>$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  </p>"},{"id":"text-89","type":"text","heading":"","plain_text":"  $ scene.css (\n    &quot;transformer&quot;: `\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n);","html":"<p>  $ scene.css (\n    &quot;transformer&quot;: `\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n);</p>"},{"id":"text-90","type":"text","heading":"","plain_text":"Maintenant, nous pouvons contrôler l&#39;échelle de la scène entière, simplement en faisant défiler la molette de la souris. Malheureusement, au moment où nous le faisons, les rotations sont annulées. Nous devons prendre en compte la rotation, car nous permettons de faire glisser la fenêtre avec la souris pour l&#39;ajuster:\nlaissez sceneTransformX = 60;\nlaissez sceneTransformY = 0;\nlaissez sceneTransformZ = 60;\nlaissez sceneTransformScale = 1;","html":"<p>Maintenant, nous pouvons contrôler l&#039;échelle de la scène entière, simplement en faisant défiler la molette de la souris. Malheureusement, au moment où nous le faisons, les rotations sont annulées. Nous devons prendre en compte la rotation, car nous permettons de faire glisser la fenêtre avec la souris pour l&#039;ajuster:\nlaissez sceneTransformX = 60;\nlaissez sceneTransformY = 0;\nlaissez sceneTransformZ = 60;\nlaissez sceneTransformScale = 1;</p>"},{"id":"text-91","type":"text","heading":"","plain_text":"const changeViewport = function () \n  $ scene.css (\n    &quot;transformer&quot;: `\n      rotationX ($ sceneTransformX deg)\n      rotationY ($ sceneTransformY deg)\n      rotationZ ($ sceneTransformZ deg)\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n;","html":"<p>const changeViewport = function () \n  $ scene.css (\n    &quot;transformer&quot;: `\n      rotationX ($ sceneTransformX deg)\n      rotationY ($ sceneTransformY deg)\n      rotationZ ($ sceneTransformZ deg)\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n;</p>"},{"id":"text-92","type":"text","heading":"","plain_text":"Cette fonction ne tient pas seulement compte du facteur d&#39;échelle de la scène, mais également des facteurs de rotation x, y et z. Nous devons également modifier notre écouteur d&#39;événement de zoom:\n$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  ","html":"<p>Cette fonction ne tient pas seulement compte du facteur d&#039;échelle de la scène, mais également des facteurs de rotation x, y et z. Nous devons également modifier notre écouteur d&#039;événement de zoom:\n$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  </p>"},{"id":"text-93","type":"text","heading":"","plain_text":"  changeViewport ();\n);","html":"<p>  changeViewport ();\n);</p>"},{"id":"text-94","type":"text","heading":"","plain_text":"Maintenant, nous pouvons commencer à faire pivoter la scène. Nous avons besoin:","html":"<p>Maintenant, nous pouvons commencer à faire pivoter la scène. Nous avons besoin:</p>"},{"id":"text-95","type":"text","heading":"","plain_text":"Un écouteur d&#39;événements pour le début de l&#39;action de glissement\nUn écouteur d&#39;événements lorsque la souris se déplace (tout en faisant glisser)\nUn écouteur d&#39;événements pour quand l&#39;action de glisser s&#39;arrête","html":"<p>Un écouteur d&#039;événements pour le début de l&#039;action de glissement\nUn écouteur d&#039;événements lorsque la souris se déplace (tout en faisant glisser)\nUn écouteur d&#039;événements pour quand l&#039;action de glisser s&#039;arrête</p>"},{"id":"text-96","type":"text","heading":"","plain_text":"Quelque chose comme ça devrait faire l&#39;affaire:\nNumber.prototype.toInt = String.prototype.toInt = function () \n  return parseInt (this, 10);\n;","html":"<p>Quelque chose comme ça devrait faire l&#039;affaire:\nNumber.prototype.toInt = String.prototype.toInt = function () \n  return parseInt (this, 10);\n;</p>"},{"id":"text-97","type":"text","heading":"","plain_text":"laissez lastMouseX = null;\nlaissez lastMouseY = null;","html":"<p>laissez lastMouseX = null;\nlaissez lastMouseY = null;</p>"},{"id":"text-98","type":"text","heading":"","plain_text":"$ body.on (&quot;mousedown&quot;, fonction (e) \n  lastMouseX = e.clientX / 10;\n  lastMouseY = e.clientY / 10;\n);","html":"<p>$ body.on (&quot;mousedown&quot;, fonction (e) \n  lastMouseX = e.clientX / 10;\n  lastMouseY = e.clientY / 10;\n);</p>"},{"id":"text-99","type":"text","heading":"","plain_text":"$ body.on (&quot;mousemove&quot;, fonction (e) \n  si (! lastMouseX) \n    revenir;\n  ","html":"<p>$ body.on (&quot;mousemove&quot;, fonction (e) \n  si (! lastMouseX) \n    revenir;\n  </p>"},{"id":"text-100","type":"text","heading":"","plain_text":"  laissez nextMouseX = e.clientX / 10;\n  laissez nextMouseY = e.clientY / 10;","html":"<p>  laissez nextMouseX = e.clientX / 10;\n  laissez nextMouseY = e.clientY / 10;</p>"},{"id":"text-101","type":"text","heading":"","plain_text":"  if (nextMouseX! == lastMouseX) \n    deltaX = nextMouseX.toInt () - lastMouseX.toInt ();\n    degrés = sceneTransformZ - deltaX;","html":"<p>  if (nextMouseX! == lastMouseX) \n    deltaX = nextMouseX.toInt () - lastMouseX.toInt ();\n    degrés = sceneTransformZ - deltaX;</p>"},{"id":"text-102","type":"text","heading":"","plain_text":"    si (degrés&gt; 360) \n        degrés - = 360;\n    ","html":"<p>    si (degrés&gt; 360) \n        degrés - = 360;\n    </p>"},{"id":"text-103","type":"text","heading":"","plain_text":"    si (degrés < 0) \n        degrees += 360;","html":"<p>    si (degrés &lt; 0) \n        degrees += 360;</p>"},{"id":"text-104","type":"text","heading":"","plain_text":"sceneTransformZ = degrees;\n    lastMouseX = nextMouseX;","html":"<p>sceneTransformZ = degrees;\n    lastMouseX = nextMouseX;</p>"},{"id":"text-105","type":"text","heading":"","plain_text":"changeViewport();","html":"<p>changeViewport();</p>"},{"id":"text-106","type":"text","heading":"","plain_text":"if (nextMouseY !== lastMouseY) \n    deltaY = nextMouseY.toInt() - lastMouseY.toInt();\n    degrees = sceneTransformX - deltaY;","html":"<p>if (nextMouseY !== lastMouseY) \n    deltaY = nextMouseY.toInt() - lastMouseY.toInt();\n    degrees = sceneTransformX - deltaY;</p>"},{"id":"text-107","type":"text","heading":"","plain_text":"if (degrees > 360) \n        degrés - = 360;\n    ","html":"<p>if (degrees &gt; 360) \n        degrés - = 360;\n    </p>"},{"id":"text-108","type":"text","heading":"","plain_text":"    si (degrés &lt;0) \n        degrés + = 360;\n    ","html":"<p>    si (degrés &lt;0) \n        degrés + = 360;\n    </p>"},{"id":"text-109","type":"text","heading":"","plain_text":"    sceneTransformX = degrés;\n    lastMouseY = nextMouseY;","html":"<p>    sceneTransformX = degrés;\n    lastMouseY = nextMouseY;</p>"},{"id":"text-110","type":"text","heading":"","plain_text":"    changeViewport ();\n  \n);","html":"<p>    changeViewport ();\n  \n);</p>"},{"id":"text-111","type":"text","heading":"","plain_text":"$ body.on (&quot;mouseup&quot;, fonction (e) \n  lastMouseX = null;\n  lastMouseY = null;\n);","html":"<p>$ body.on (&quot;mouseup&quot;, fonction (e) \n  lastMouseX = null;\n  lastMouseY = null;\n);</p>"},{"id":"text-112","type":"text","heading":"","plain_text":"Sur souris vers le bas nous capturons la souris initiale X et y coordonnées. Lorsque la souris se déplace (si le bouton est toujours enfoncé), nous ajustons la sceneTransformZ et sceneTransformX d&#39;un montant réduit. Il n&#39;y a pas de mal à laisser passer les valeurs 360 degrés ou moins 0 degrés, mais ceux-ci seraient terribles si nous voulions les afficher à l&#39;écran.","html":"<p>Sur souris vers le bas nous capturons la souris initiale X et y coordonnées. Lorsque la souris se déplace (si le bouton est toujours enfoncé), nous ajustons la sceneTransformZ et sceneTransformX d&#039;un montant réduit. Il n&#039;y a pas de mal à laisser passer les valeurs 360 degrés ou moins 0 degrés, mais ceux-ci seraient terribles si nous voulions les afficher à l&#039;écran.</p>"},{"id":"text-113","type":"text","heading":"","plain_text":"Calcul à l&#39;intérieur d&#39;un déplacer la souris l&#39;écouteur d&#39;événements peut être coûteux en calcul en raison de la quantité de déclencheurs qui peuvent être déclenchés. Il y a potentiellement des millions de pixels sur l&#39;écran, et cet écouteur peut être déclenché lorsque la souris se déplace vers chacun d&#39;eux. C’est pourquoi nous quittons tôt si le bouton de la souris n’est pas maintenu enfoncé.","html":"<p>Calcul à l&#039;intérieur d&#039;un déplacer la souris l&#039;écouteur d&#039;événements peut être coûteux en calcul en raison de la quantité de déclencheurs qui peuvent être déclenchés. Il y a potentiellement des millions de pixels sur l&#039;écran, et cet écouteur peut être déclenché lorsque la souris se déplace vers chacun d&#039;eux. C’est pourquoi nous quittons tôt si le bouton de la souris n’est pas maintenu enfoncé.</p>"},{"id":"text-114","type":"text","heading":"","plain_text":"Lorsque le bouton de la souris est relâché, nous désactivons lastMouseX et lastMouseY, de sorte que la déplacer la souris l&#39;auditeur arrête de calculer les choses. On pourrait juste effacer lastMouseX, mais effacer les deux me semble plus propre.\nMalheureusement, l&#39;événement mousedown peut interférer avec l&#39;événement click sur les côtés du bloc. Nous pouvons contourner cela en empêchant le bouillonnement des événements:\n$ scene.on (&quot;mousedown&quot;, fonction (e) \n  e.stopPropagation ();\n);","html":"<p>Lorsque le bouton de la souris est relâché, nous désactivons lastMouseX et lastMouseY, de sorte que la déplacer la souris l&#039;auditeur arrête de calculer les choses. On pourrait juste effacer lastMouseX, mais effacer les deux me semble plus propre.\nMalheureusement, l&#039;événement mousedown peut interférer avec l&#039;événement click sur les côtés du bloc. Nous pouvons contourner cela en empêchant le bouillonnement des événements:\n$ scene.on (&quot;mousedown&quot;, fonction (e) \n  e.stopPropagation ();\n);</p>"},{"id":"text-115","type":"text","heading":"","plain_text":"Essayer…\nVoir le zoom et la rotation du stylet par SitePoint (@SitePoint) sur CodePen.\nSuppression de blocs\nComplétons l&#39;expérience en ajoutant la possibilité de supprimer des blocs. Nous devons faire quelques choses subtiles mais importantes:","html":"<p>Essayer…\nVoir le zoom et la rotation du stylet par SitePoint (@SitePoint) sur CodePen.\nSuppression de blocs\nComplétons l&#039;expérience en ajoutant la possibilité de supprimer des blocs. Nous devons faire quelques choses subtiles mais importantes:</p>"},{"id":"text-116","type":"text","heading":"","plain_text":"Changer la couleur de la bordure de survol du vert au rouge\nDésactivez les fantômes bloqués","html":"<p>Changer la couleur de la bordure de survol du vert au rouge\nDésactivez les fantômes bloqués</p>"},{"id":"text-117","type":"text","heading":"","plain_text":"Ce sera plus facile à faire avec CSS, tant que nous avons une classe de corps pour indiquer si nous sommes en mode additionnel (normal) ou en mode soustraction:\n$ body.on (&quot;keydown&quot;, fonction (e)  e.controlKey );","html":"<p>Ce sera plus facile à faire avec CSS, tant que nous avons une classe de corps pour indiquer si nous sommes en mode additionnel (normal) ou en mode soustraction:\n$ body.on (&quot;keydown&quot;, fonction (e)  e.controlKey );</p>"},{"id":"text-118","type":"text","heading":"","plain_text":"$ body.on (&quot;keyup&quot;, fonction (e) \n  $ body.removeClass (&quot;soustraction&quot;);\n);","html":"<p>$ body.on (&quot;keyup&quot;, fonction (e) \n  $ body.removeClass (&quot;soustraction&quot;);\n);</p>"},{"id":"text-119","type":"text","heading":"","plain_text":"Lorsqu&#39;une touche de modification est enfoncée (alt, contrôle, ou commander), ce code s&#39;assurera corps a un soustraction classe. Cela facilite le ciblage de divers éléments à l&#39;aide de cette classe:\n.subtraction .block: hover .side \n  contour: 1px rgba solide (255, 0, 0, 0,5);","html":"<p>Lorsqu&#039;une touche de modification est enfoncée (alt, contrôle, ou commander), ce code s&#039;assurera corps a un soustraction classe. Cela facilite le ciblage de divers éléments à l&#039;aide de cette classe:\n.subtraction .block: hover .side \n  contour: 1px rgba solide (255, 0, 0, 0,5);</p>"},{"id":"text-120","type":"text","heading":"","plain_text":".soustraction .ghost \n  affichage: aucun;","html":"<p>.soustraction .ghost \n  affichage: aucun;</p>"},{"id":"text-121","type":"text","heading":"","plain_text":"Nous recherchons un certain nombre de touches de modification, car différents systèmes d&#39;exploitation interceptent différents modificateurs. Par exemple, touche Alt et metaKey travailler sur macOS, alors que clé de contrôle fonctionne sur Ubuntu.\nSi nous cliquons sur un bloc, lorsque nous sommes en mode soustraction, nous devons le supprimer:\n$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);","html":"<p>Nous recherchons un certain nombre de touches de modification, car différents systèmes d&#039;exploitation interceptent différents modificateurs. Par exemple, touche Alt et metaKey travailler sur macOS, alors que clé de contrôle fonctionne sur Ubuntu.\nSi nous cliquons sur un bloc, lorsque nous sommes en mode soustraction, nous devons le supprimer:\n$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);</p>"},{"id":"text-122","type":"text","heading":"","plain_text":"  if ($ body.hasClass (&quot;soustraction&quot;)) \n    previous.block.remove ();\n    précédent = null;\n   autre \n    const coordonnées = createCoordinatesFrom (\n      $ this.data (&quot;type&quot;),\n      previous.x,\n      previous.y,\n      previous.z\n    );","html":"<p>  if ($ body.hasClass (&quot;soustraction&quot;)) \n    previous.block.remove ();\n    précédent = null;\n   autre \n    const coordonnées = createCoordinatesFrom (\n      $ this.data (&quot;type&quot;),\n      previous.x,\n      previous.y,\n      previous.z\n    );</p>"},{"id":"text-123","type":"text","heading":"","plain_text":"    const suivant = nouveau Block.Dirt (... coordonnées);\n    next.block.appendTo ($ scene);\n  \n);","html":"<p>    const suivant = nouveau Block.Dirt (... coordonnées);\n    next.block.appendTo ($ scene);\n  \n);</p>"},{"id":"text-124","type":"text","heading":"","plain_text":"C&#39;est pareil .côté écouteur d&#39;événements de clic que nous avions auparavant, mais au lieu d&#39;ajouter simplement de nouveaux blocs lorsqu&#39;un côté est cliqué, nous vérifions d&#39;abord si nous sommes en mode de soustraction. Si c&#39;est le cas, le bloc sur lequel nous venons de cliquer est supprimé de la scène.\nLa démo finale\nLa démo finale est merveilleuse pour jouer avec:\nVoir le stylo Suppression de blocs par SitePoint (@SitePoint) sur CodePen.\nIl y a un long chemin à parcourir avant de prendre en charge autant de blocs et d&#39;interactions que Minecraft, mais c&#39;est un bon début. De plus, nous avons réussi à atteindre cet objectif sans avoir à étudier des techniques 3D avancées. C’est une utilisation non conventionnelle (et créative) des transformations CSS!\nSi vous souhaitez en faire plus avec ce code, passez à l&#39;autre moitié de cette aventure. Vous n&#39;avez pas besoin d&#39;être un expert PHP pour interagir avec les serveurs Minecraft. Et imaginez les choses incroyables que vous pouvez faire avec ces connaissances…\nEt n&#39;oubliez pas: ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.","html":"<p>C&#039;est pareil .côté écouteur d&#039;événements de clic que nous avions auparavant, mais au lieu d&#039;ajouter simplement de nouveaux blocs lorsqu&#039;un côté est cliqué, nous vérifions d&#039;abord si nous sommes en mode de soustraction. Si c&#039;est le cas, le bloc sur lequel nous venons de cliquer est supprimé de la scène.\nLa démo finale\nLa démo finale est merveilleuse pour jouer avec:\nVoir le stylo Suppression de blocs par SitePoint (@SitePoint) sur CodePen.\nIl y a un long chemin à parcourir avant de prendre en charge autant de blocs et d&#039;interactions que Minecraft, mais c&#039;est un bon début. De plus, nous avons réussi à atteindre cet objectif sans avoir à étudier des techniques 3D avancées. C’est une utilisation non conventionnelle (et créative) des transformations CSS!\nSi vous souhaitez en faire plus avec ce code, passez à l&#039;autre moitié de cette aventure. Vous n&#039;avez pas besoin d&#039;être un expert PHP pour interagir avec les serveurs Minecraft. Et imaginez les choses incroyables que vous pouvez faire avec ces connaissances…\nEt n&#039;oubliez pas: ce n&#039;est que la moitié de l&#039;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#039;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.</p>"},{"id":"text-125","type":"text","heading":"","plain_text":"Christopher est un écrivain et codeur travaillant chez Over. Il travaille généralement sur l&#39;architecture des applications, mais parfois vous le trouverez en train de construire des compilateurs ou des robots.","html":"<p>Christopher est un écrivain et codeur travaillant chez Over. Il travaille généralement sur l&#039;architecture des applications, mais parfois vous le trouverez en train de construire des compilateurs ou des robots.</p>"},{"id":"text-126","type":"text","heading":"","plain_text":"Click to rate this post!\n                                   \n                               [Total: 0  Average: 0]","html":"<p>Click to rate this post!\n                                   \n                               [Total: 0  Average: 0]</p>"}],"sections":[{"id":"text-1","heading":"Text","content":"Cet article a été révisé par des pairs par Paul O’Brien. Merci à tous les pairs examinateurs de SitePoint pour avoir rendu le contenu SitePoint le meilleur possible!"},{"id":"text-2","heading":"Text","content":"J&#39;ai toujours voulu créer un jeu 3D. Je n&#39;ai tout simplement jamais eu le temps et l&#39;énergie pour apprendre les subtilités de la programmation 3D. J&#39;ai découvert que je n&#39;avais pas besoin de &#8230;\nEn bricolant un jour, j&#39;ai pensé que je pourrais peut-être simuler un environnement 3D en utilisant des transformations CSS. Je suis tombé sur un vieil article sur la création de mondes 3D avec HTML et CSS.\nJe voulais simuler un monde Minecraft (ou une infime partie de celui-ci au moins). Minecraft est un jeu de bac à sable, dans lequel vous pouvez casser et placer des blocs. Je voulais le même type de fonctionnalité, mais avec HTML, JavaScript et CSS.\nVenez décrire ce que j&#39;ai appris et comment cela peut vous aider à être plus créatif avec vos transformations CSS!"},{"id":"text-3","heading":"Text","content":"Remarque: La plupart du code de ce didacticiel se trouve sur Github. Je l&#39;ai testé dans la dernière version de Chrome. Je ne peux pas vous promettre que cela aura exactement la même apparence dans d&#39;autres navigateurs, mais les concepts de base sont universels."},{"id":"text-4","heading":"Text","content":"Ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs.\nLes choses que nous faisons déjà\nJ&#39;ai écrit ma juste part de CSS et j&#39;en suis venu à bien le comprendre, dans le but de créer des sites Web. Mais cette compréhension repose sur l&#39;hypothèse que je vais travailler dans un espace 2D.\nPrenons un exemple:\n.outils \n  position: absolue;\n  gauche: 35px;\n  haut: 25px;\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;"},{"id":"text-5","heading":"Text","content":".Toile \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 100%;\n  hauteur: 100%;\n  indice z: 2;"},{"id":"text-6","heading":"Text","content":"Ici, nous avons un élément canvas, commençant dans le coin supérieur gauche de la page et s&#39;étendant jusqu&#39;en bas à droite. En plus de cela, nous ajoutons un élément outils. Il commence 25px de gauche et 35px en haut de la page et les mesures 200px large par 400px haute.\nSelon la commande div.tools et div.canvas sont ajoutés au balisage, il est tout à fait possible que div.canvas pourrait se chevaucher div.tools. C&#39;est à l&#39;exception de la z-index styles appliqués à chacun.\nVous êtes probablement habitué à considérer les éléments ainsi conçus comme des surfaces 2D susceptibles de se chevaucher. Mais ce chevauchement est essentiellement une troisième dimension. la gauche, Haut, et z-index peut aussi bien être renommé X, y, et z. Tant que nous supposons que chaque élément a une profondeur fixe de 1px, et z-index a un implicite px unité, nous pensons déjà en termes 3D.\nCertains d&#39;entre nous ont tendance à avoir du mal avec les concepts de rotation et de traduction dans cette troisième dimension…\nLa théorie des transformations\nLes traductions CSS dupliquent cette fonctionnalité familière, dans une API qui dépasse les limites Haut, la gauche, et z-index placer sur nous. Il est possible de remplacer certains de nos styles précédents par des traductions:\n.outils \n  position: absolue;\n  fond: vert;\n  / *\n    gauche: 35px;\n    haut: 25px;\n  * /\n  transform-origin: 0 0;\n  transformer: traduire (35px, 25px);\n  largeur: 200 px;\n  hauteur: 400px;\n  indice z: 3;"},{"id":"text-7","heading":"Text","content":"Au lieu de définir la gauche et Haut compensations (avec une origine supposée de 0px de gauche et 0px par le haut), nous pouvons déclarer une origine explicite. Nous pouvons effectuer toutes sortes de transformations sur cet élément, pour lesquelles utiliser 0 0 comme centre. traduire (35px, 25px) déplace l&#39;élément 35px à droite et 25px vers le bas. Nous pouvons utiliser des valeurs négatives pour déplacer l&#39;élément vers la gauche et / ou vers le haut.\nAvec la possibilité de définir une origine pour nos transformations, nous pouvons également commencer à faire d&#39;autres choses intéressantes. Par exemple, nous pouvons faire pivoter et mettre à l&#39;échelle des éléments:\ntransform-origin: centre;\ntransformer: l&#39;échelle (0,5) tourne (45 degrés);"},{"id":"text-8","heading":"Text","content":"Chaque élément commence par un défaut transform-origin de 50% 50% 0, mais une valeur de centre ensembles X, y, et z à l&#39;équivalent de 50%. Nous pouvons adapter notre élément à une valeur comprise entre 0 et 1et faites-le pivoter (dans le sens horaire) de degrés ou de radians. Et nous pouvons convertir entre les deux avec:"},{"id":"text-9","heading":"Text","content":"45deg = (45 * Math.PI) / 180 ≅ 0.79rad\n0.79rad = (0,79 * 180) / Math.PI ≅ 45deg"},{"id":"text-10","heading":"Text","content":"Pour faire pivoter un élément dans le sens inverse des aiguilles d&#39;une montre, il suffit d&#39;utiliser un négatif deg ou rad valeur.\nCe qui est encore plus intéressant, à propos de ces transformations, c&#39;est que nous pouvons en utiliser des versions 3D."},{"id":"text-11","heading":"Text","content":"Les navigateurs Evergreen prennent assez bien en charge ces styles, bien qu&#39;ils puissent nécessiter des préfixes de fournisseurs. CodePen a une option &quot;autoprefix&quot; soignée, mais vous pouvez ajouter des bibliothèques comme PostCSS à votre code local pour obtenir la même chose."},{"id":"text-12","heading":"Text","content":"Le premier bloc\nCommençons à créer notre monde 3D. Nous allons commencer par créer un espace dans lequel placer nos blocs. Créez un nouveau fichier, appelé index.html:"},{"id":"text-13","heading":"Text","content":"Ici, nous étirons le corps sur toute la largeur et la hauteur, réinitialisant le rembourrage 0px. Ensuite, nous créons un petit div.scene, que nous utiliserons pour contenir différents blocs. Nous utilisons 50% la gauche et Haut, ainsi qu&#39;une gauche et un haut négatifs marge (égal à la moitié du largeur et la taille) pour le centrer horizontalement et verticalement. Ensuite, nous l&#39;inclinons légèrement (en utilisant la rotation 3D) afin d&#39;avoir une vue en perspective de l&#39;emplacement des blocs."},{"id":"text-14","heading":"Text","content":"Remarquez comment nous définissons style de transformation: préserver-3d. C&#39;est ainsi que les éléments enfants peuvent également être manipulés dans un espace 3D."},{"id":"text-15","heading":"Text","content":"Le résultat devrait ressembler à ceci:\nVoir la scène vide du stylo par SitePoint (@SitePoint) sur CodePen.\nMaintenant, commençons à ajouter une forme de bloc à la scène. Nous devons créer un nouveau fichier JavaScript, appelé block.js:\n&quot;utiliser strictement&quot;"},{"id":"text-16","heading":"Text","content":"bloc de classe \n  constructeur (x, y, z) \n    this.x = x;\n    this.y = y;\n    this.z = z;"},{"id":"text-17","heading":"Text","content":"    this.build ();\n  "},{"id":"text-18","heading":"Text","content":"  construire() \n    // TODO: construire le bloc\n  "},{"id":"text-19","heading":"Text","content":"  createFace (type, x, y, z, rx, ry, rz) \n    // TODO: retourne une face de bloc\n  "},{"id":"text-20","heading":"Text","content":"  createTexture (type) \n    // TODO: obtenez la texture\n  "},{"id":"text-21","heading":"Text","content":"Chaque bloc doit avoir une forme 3D à 6 faces. Nous pouvons diviser les différentes parties de la construction en méthodes pour (1) construire le bloc entier, (2) construire chaque surface et (3) obtenir la texture de chaque surface.\nChacun de ces comportements (ou méthodes) est contenu dans une classe ES6. C’est une bonne façon de regrouper les structures de données et les méthodes qui les opèrent ensemble. Vous connaissez peut-être la forme traditionnelle:\nbloc fonction (x, y, z) \n  this.x = x;\n  this.y = y;\n  this.z = z;"},{"id":"text-22","heading":"Text","content":"  this.build ();"},{"id":"text-23","heading":"Text","content":"var proto = Block.prototype;"},{"id":"text-24","heading":"Text","content":"proto.build = function () \n  // TODO: construire le bloc\n;"},{"id":"text-25","heading":"Text","content":"proto.createFace = fonction (type, x, y, z, rx, ry, rz) \n  // TODO: retourne une face de bloc"},{"id":"text-26","heading":"Text","content":"proto.createTexture = fonction (type) \n  // TODO: obtenez la texture"},{"id":"text-27","heading":"Text","content":"Cela peut sembler un peu différent, mais c&#39;est à peu près la même chose. En plus d&#39;une syntaxe plus courte, les classes ES6 fournissent également des raccourcis pour étendre les prototypes et appeler les méthodes remplacées. Mais je m&#39;égare…\nTravaillons de bas en haut:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    );"},{"id":"text-28","heading":"Text","content":"createTexture (type) \n  retour `rgba (100, 100, 255, 0,2)`;"},{"id":"text-29","heading":"Text","content":"Chaque surface (ou face) est constituée d&#39;un div tourné et translaté. Nous ne pouvons pas rendre les éléments plus épais que 1px, mais nous pouvons simuler la profondeur en couvrant tous les trous et en utilisant plusieurs éléments parallèles les uns aux autres. On peut donner au bloc l&#39;illusion de profondeur, même s&#39;il est creux.\nÀ cette fin, le createFace prend un ensemble de coordonnées:  X, y, et z pour la position du visage. Nous fournissons également des rotations pour chaque axe, afin que nous puissions appeler createFace avec n&#39;importe quelle configuration et il traduira et fera pivoter le visage comme nous le voulons.\nConstruisons la forme de base:\nconstruire() \n  taille const = 64;\n  const x = this.x * taille;\n  const y = this.y * taille;\n  const z = this.z * taille;"},{"id":"text-30","heading":"Text","content":"  const block = this.block = $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n      &quot;\n    );"},{"id":"text-31","heading":"Text","content":"  $ (`&#39;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (90deg)\n        rotationY (0deg)\n        rotationZ (0deg)\n      &quot;\n    );"},{"id":"text-32","heading":"Text","content":"  $ (`&#39;)\n    .appendTo (bloquer)\n    .css (\n      transformer: `\n        rotationX (0deg)\n        rotationY (90deg)\n        rotationZ (0deg)\n      &quot;\n    );"},{"id":"text-33","heading":"Text","content":"  $ (`&#39;)\n    .appendTo (block);"},{"id":"text-34","heading":"Text","content":"Nous avons l&#39;habitude de penser en termes de positions d&#39;un pixel, mais un jeu comme Minecraft fonctionne à plus grande échelle. Chaque bloc est plus grand et le système de coordonnées traite de la position du bloc, pas des pixels individuels qui le composent. Je veux transmettre le même genre d&#39;idée ici…\nLorsque quelqu&#39;un crée un nouveau bloc, à 1 × 2 × 3, Je veux que cela signifie 0px × 64px × 128px. Nous multiplions donc chaque coordonnée par la taille par défaut (dans ce cas 64px, car c&#39;est la taille des textures du pack de textures que nous utiliserons).\nEnsuite, nous créons un conteneur div (que nous appelons div.block). À l&#39;intérieur, nous plaçons encore 3 divisions. Ceux-ci nous montreront l&#39;axe de notre bloc &#8211; ils sont comme des guides dans un programme de rendu 3D. Nous devons également ajouter de nouveaux CSS pour notre bloc:\n.bloquer \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  transformer-style: préserver-3d;\n  origine de transformation: 50% 50% 50%;"},{"id":"text-35","heading":"Text","content":"axe .x,\naxe-y,\n.Axe z \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 66px;\n  hauteur: 66px;\n  origine de transformation: 50% 50% 50%;"},{"id":"text-36","heading":"Text","content":"Axe .x \n  bordure: solide 2px rgba (255, 0, 0, 0,3);"},{"id":"text-37","heading":"Text","content":"Axe .y \n  bordure: solide 2px rgba (0, 255, 0, 0,3);"},{"id":"text-38","heading":"Text","content":".Axe z \n  bordure: solide 2px rgba (0, 0, 255, 0,3);"},{"id":"text-39","heading":"Text","content":"Ce style est similaire à ce que nous avons vu auparavant. Nous devons nous rappeler de définir style de transformation: préserver-3d sur le .bloquer, afin que les axes soient rendus dans leur propre espace 3D. Nous leur donnons une couleur différente et les rendons légèrement plus grands que le bloc dans lequel ils sont contenus. C&#39;est pour qu&#39;ils soient visibles même lorsque le bloc a des côtés.\nCréons un nouveau bloc et ajoutons-le au div.scene:\nlet first = new Block (1, 1, 1);"},{"id":"text-40","heading":"Text","content":"$ (&quot;. scene&quot;). append (first.block);"},{"id":"text-41","heading":"Text","content":"Le résultat devrait ressembler à ceci:\nVoir le Pen Basic 3D Block de SitePoint (@SitePoint) sur CodePen.\nMaintenant, ajoutons ces visages:\ncette\n  .createFace (&quot;top&quot;, 0, 0, size / 2, 0, 0, 0)\n  .appendTo (block);"},{"id":"text-42","heading":"Text","content":"cette\n  .createFace (&quot;side-1&quot;, 0, taille / 2, 0, 270, 0, 0)\n  .appendTo (block);"},{"id":"text-43","heading":"Text","content":"cette\n  .createFace (&quot;side-2&quot;, taille / 2, 0, 0, 0, 90, 0)\n  .appendTo (block);"},{"id":"text-44","heading":"Text","content":"cette\n  .createFace (&quot;côté-3&quot;, 0, taille / -2, 0, -270, 0, 0)\n  .appendTo (block);"},{"id":"text-45","heading":"Text","content":"cette\n  .createFace (&quot;side-4&quot;, taille / -2, 0, 0, 0, -90, 0)\n  .appendTo (block);"},{"id":"text-46","heading":"Text","content":"cette\n  .createFace (&quot;bas&quot;, 0, 0, taille / -2, 0, 180, 0)\n  .appendTo (block);"},{"id":"text-47","heading":"Text","content":"J&#39;ai trouvé ce code un peu d&#39;essai et d&#39;erreur (en raison de mon expérience limitée avec la perspective 3D). Chaque élément commence exactement dans la même position que le div.z-axe élément. Autrement dit, dans le centre vertical de la div.block et face au sommet.\nDonc, pour l&#39;élément &quot;top&quot;, j&#39;ai dû le traduire &quot;vers le haut&quot; de la moitié de la taille du bloc, mais je n&#39;ai pas eu à le faire pivoter de quelque façon que ce soit. Pour l&#39;élément «bas», j&#39;ai dû le faire pivoter de 180 degrés (le long de l&#39;axe x ou y), et le déplacer de la moitié de la taille du bloc.\nEn utilisant une pensée similaire, j&#39;ai tourné et traduit chacun des côtés restants. J&#39;ai également dû leur ajouter du CSS correspondant:\n.side \n  position: absolue;\n  gauche: 0;\n  en haut: 0;\n  largeur: 64px;\n  hauteur: 64px;\n  visibilité arrière: cachée;\n  contour: 1px rgba solide (0, 0, 0, 0,3);"},{"id":"text-48","heading":"Text","content":"Ajouter Visibilité arrière: caché empêche le rendu du côté «bas» des éléments. Habituellement, ils semblaient juste les mêmes (seulement en miroir), peu importe la façon dont ils étaient tournés. Avec les faces arrière cachées, seul le côté «supérieur» est rendu. Soyez prudent lors de l&#39;activation: vos surfaces doivent être tournées dans le bon sens ou les côtés du bloc disparaîtront. C’est la raison des rotations 90/270 / -90 / -270 que j’ai données aux côtés.\nVoir le Pen 3D Block Sides by SitePoint (@SitePoint) sur CodePen.\nRendons ce bloc un peu plus réaliste. Nous devons créer un nouveau fichier, appelé block.dirt.jset remplacer la createTexture méthode:\n&quot;utiliser strictement&quot;"},{"id":"text-49","heading":"Text","content":"const DIRT_TEXTURES = \n  &quot;Haut&quot;: [\n    \"textures/dirt-top-1.png\",\n    \"textures/dirt-top-2.png\",\n    \"textures/dirt-top-3.png\"\n  ],\n  &quot;côté&quot;: [\n    \"textures/dirt-side-1.png\",\n    \"textures/dirt-side-2.png\",\n    \"textures/dirt-side-3.png\",\n    \"textures/dirt-side-4.png\",\n    \"textures/dirt-side-5.png\"\n  ]\n;"},{"id":"text-50","heading":"Text","content":"classe Dirt étend le bloc \n  createTexture (type)  type === &quot;bottom&quot;) \n      const texture = DIRT_TEXTURES.top.random ();"},{"id":"text-51","heading":"Text","content":"      return `url ($ texture)`;\n    "},{"id":"text-52","heading":"Text","content":"    const texture = DIRT_TEXTURES.side.random ();"},{"id":"text-53","heading":"Text","content":"    return `url ($ texture)`;\n  "},{"id":"text-54","heading":"Text","content":"Block.Dirt = Dirt;"},{"id":"text-55","heading":"Text","content":"Nous allons utiliser un pack de texture populaire, appelé Sphax PureBDCraft. Il est gratuit à télécharger et à utiliser (à condition que vous n&#39;essayiez pas de le vendre), et il existe en différentes tailles. J&#39;utilise le x64 version."},{"id":"text-56","heading":"Text","content":"Nous commençons par définir une table de correspondance pour les textures des côtés et du haut du bloc. Le pack de textures ne spécifie pas les textures à utiliser pour le bas, nous allons donc simplement réutiliser les textures du haut.\nSi le côté ayant besoin d&#39;une texture est «haut» ou «bas», alors nous récupérons une texture aléatoire dans la liste «haut». La méthode aléatoire n&#39;existe pas jusqu&#39;à ce que nous la définissions:\nArray.prototype.random = function () \n  retourner ceci[Math.floor(Math.random() * this.length)];\n;"},{"id":"text-57","heading":"Text","content":"De même, si nous avons besoin d&#39;une texture pour un côté, nous en récupérons une au hasard. Ces textures sont sans couture, donc la randomisation fonctionne en notre faveur.\nLe résultat devrait ressembler à ceci:\nVoir les textures de bloc 3D Pen par SitePoint (@SitePoint) sur CodePen.\nFaire une scène\nComment rendons-nous cela interactif? Eh bien, un bon endroit pour commencer est avec une scène. Nous avons déjà placé des blocs dans la scène, il ne nous reste plus qu&#39;à activer le placement dynamique!\nPour commencer, nous pouvons rendre une surface plane de blocs:\nconst $ scene = $ (&quot;. scene&quot;);"},{"id":"text-58","heading":"Text","content":"pour (var x = 0; x &lt;6; x ++) \n  pour (var y = 0; y &lt;6; y ++) \n    let next = new Block.Dirt (x, y, 0);\n    next.block.appendTo ($ scene);\n  "},{"id":"text-59","heading":"Text","content":"Génial, cela nous donne une surface plane pour commencer à ajouter des blocs. Maintenant, mettons en surbrillance les surfaces lorsque nous les survolons avec notre curseur:\n.block: hover .side \n  contour: 1px rgba solide (0, 255, 0, 0,5);"},{"id":"text-60","heading":"Text","content":"Il se passe cependant quelque chose d&#39;étrange:"},{"id":"text-61","heading":"Text","content":"En effet, les surfaces se coupent de manière aléatoire. Il n&#39;y a pas de bon moyen de résoudre ce problème, mais nous pouvons l&#39;empêcher de se produire en redimensionnant légèrement les blocs:\nconst block = this.block = $ (`&#39;)\n  .css (\n    transformer: `\n      translateX ($ x px)\n      translateY ($ y px)\n      translateZ ($ z px)\n      échelle (0.99)\n    &quot;\n  );"},{"id":"text-62","heading":"Text","content":"Bien que cela améliore l&#39;apparence, cela affectera les performances plus il y aura de blocs dans la scène. Marchez légèrement lors de la mise à l&#39;échelle de nombreux éléments à la fois…\nÉtiquetons chaque surface avec le bloc et le type qui lui appartiennent:\ncreateFace (type, x, y, z, rx, ry, rz) \n  retourner $ (`&#39;)\n    .css (\n      transformer: `\n        translateX ($ x px)\n        translateY ($ y px)\n        translateZ ($ z px)\n        rotationX ($ rx deg)\n        rotationY ($ ry deg)\n        rotationZ ($ rz deg)\n      &quot;,\n      fond: this.createTexture (type)\n    )\n    .data (&quot;bloquer&quot;, ceci)\n    .data (&quot;type&quot;, type);"},{"id":"text-63","heading":"Text","content":"Ensuite, en cliquant sur une surface, nous pouvons dériver un nouvel ensemble de coordonnées et créer un nouveau bloc:\nfunction createCoordinatesFrom (côté, x, y, z) \n  if (side == &quot;top&quot;) \n    z + = 1;\n  "},{"id":"text-64","heading":"Text","content":"  si (côté == &quot;côté-1&quot;) \n    y + = 1;\n  "},{"id":"text-65","heading":"Text","content":"  si (côté == &quot;côté-2&quot;) \n    x + = 1;\n  "},{"id":"text-66","heading":"Text","content":"  si (côté == &quot;côté-3&quot;) \n    y - = 1;\n  "},{"id":"text-67","heading":"Text","content":"  si (côté == &quot;côté-4&quot;) \n    x - = 1;\n  "},{"id":"text-68","heading":"Text","content":"  si (côté == &quot;bas&quot;) \n    z - = 1;\n  "},{"id":"text-69","heading":"Text","content":"  revenir [x, y, z];"},{"id":"text-70","heading":"Text","content":"const $ body = $ (&quot;corps&quot;);"},{"id":"text-71","heading":"Text","content":"$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);"},{"id":"text-72","heading":"Text","content":"  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );"},{"id":"text-73","heading":"Text","content":"  const suivant = nouveau Block.Dirt (... coordonnées);"},{"id":"text-74","heading":"Text","content":"  next.block.appendTo ($ scene);\n);"},{"id":"text-75","heading":"Text","content":"createCoordinatesFrom a une tâche simple mais importante. Compte tenu du type de côté et des coordonnées du bloc auquel il appartient, createCoordinatesFrom devrait renvoyer un nouvel ensemble de coordonnées. C&#39;est là que le nouveau bloc sera placé.\nEnsuite, nous avons joint un écouteur d&#39;événements. Il sera déclenché pour chaque côté div. qui obtient cliqué. Lorsque cela se produit, nous obtenons le bloc auquel appartient le côté et dérivons un nouvel ensemble de coordonnées pour le bloc suivant. Une fois que nous les avons, nous créons le bloc et l&#39;ajoutons à la scène.\nLe résultat est merveilleusement interactif:\nVoir la scène pré-remplie du stylo par SitePoint (@SitePoint) sur CodePen.\nVoir les fantômes\nIl serait utile de voir un aperçu du bloc que nous allons placer avant de le placer. On parle parfois de «montrer un fantôme» de la chose que nous allons faire.\nLe code pour l&#39;activer est assez similaire à celui que nous avons déjà vu:\nlaissez ghost = null;"},{"id":"text-76","heading":"Text","content":"fonction removeGhost () \n  si (fantôme) \n    ghost.block.remove ();\n    fantôme = null;\n  "},{"id":"text-77","heading":"Text","content":"fonction createGhostAt (x, y, z) \n  const next = new Block.Dirt (x, y, z);"},{"id":"text-78","heading":"Text","content":"  next.block\n    .addClass (&quot;fantôme&quot;)\n    .appendTo ($ scene);"},{"id":"text-79","heading":"Text","content":"  fantôme = suivant;"},{"id":"text-80","heading":"Text","content":"$ body.on (&quot;mouseenter&quot;, &quot;.side&quot;, function (e) \n  removeGhost ();"},{"id":"text-81","heading":"Text","content":"  const $ this = jQuery (this);\n  const previous = $ this.data (&quot;block&quot;);"},{"id":"text-82","heading":"Text","content":"  const coordonnées = createCoordinatesFrom (\n    $ this.data (&quot;type&quot;),\n    previous.x,\n    previous.y,\n    previous.z\n  );"},{"id":"text-83","heading":"Text","content":"  createGhostAt (... coordonnées);\n);"},{"id":"text-84","heading":"Text","content":"$ body.on (&quot;mouseleave&quot;, &quot;.side&quot;, fonction (e) \n  removeGhost ();\n);"},{"id":"text-85","heading":"Text","content":"La principale différence est que nous conservons une seule instance du bloc fantôme. Au fur et à mesure que chaque nouveau est créé, l&#39;ancien est supprimé. Cela pourrait bénéficier de quelques styles supplémentaires:\n.fantôme \n  événements de pointeur: aucun;"},{"id":"text-86","heading":"Text","content":".ghost .side \n  opacité: 0,6;\n  événements de pointeur: aucun;\n  -filtre Web: luminosité (1,5);"},{"id":"text-87","heading":"Text","content":"Laissés actifs, les événements de pointeur associés aux éléments du fantôme contrecarreraient la mouseenter et mouseleave événements du côté en dessous. Comme nous n&#39;avons pas besoin d&#39;interagir avec les éléments fantômes, nous pouvons désactiver ces événements de pointeur.\nCe résultat est plutôt soigné:\nVoir le Pen 3D Block Ghosts de SitePoint (@SitePoint) sur CodePen.\nChanger de perspective\nPlus nous ajoutons d&#39;interactivité, plus il est difficile de voir ce qui se passe. Cela semble être le bon moment pour faire quelque chose à ce sujet. Ce serait génial si nous pouvions zoomer et faire pivoter la fenêtre pour pouvoir voir ce qui se passe un peu mieux…\nCommençons par le zoom. De nombreuses interfaces (et jeux) permettent un zoom dans la fenêtre en faisant défiler la molette de la souris. Différents navigateurs gèrent les événements de la molette de la souris de différentes manières, il est donc judicieux d&#39;utiliser une bibliothèque d&#39;abstraction. \nUne fois installé, nous pouvons nous connecter aux événements:\nlaissez sceneTransformScale = 1;"},{"id":"text-88","heading":"Text","content":"$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  "},{"id":"text-89","heading":"Text","content":"  $ scene.css (\n    &quot;transformer&quot;: `\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n);"},{"id":"text-90","heading":"Text","content":"Maintenant, nous pouvons contrôler l&#39;échelle de la scène entière, simplement en faisant défiler la molette de la souris. Malheureusement, au moment où nous le faisons, les rotations sont annulées. Nous devons prendre en compte la rotation, car nous permettons de faire glisser la fenêtre avec la souris pour l&#39;ajuster:\nlaissez sceneTransformX = 60;\nlaissez sceneTransformY = 0;\nlaissez sceneTransformZ = 60;\nlaissez sceneTransformScale = 1;"},{"id":"text-91","heading":"Text","content":"const changeViewport = function () \n  $ scene.css (\n    &quot;transformer&quot;: `\n      rotationX ($ sceneTransformX deg)\n      rotationY ($ sceneTransformY deg)\n      rotationZ ($ sceneTransformZ deg)\n      scaleX ($ sceneTransformScale)\n      scaleY ($ sceneTransformScale)\n      scaleZ ($ sceneTransformScale)\n    &quot;\n  );\n;"},{"id":"text-92","heading":"Text","content":"Cette fonction ne tient pas seulement compte du facteur d&#39;échelle de la scène, mais également des facteurs de rotation x, y et z. Nous devons également modifier notre écouteur d&#39;événement de zoom:\n$ body.on (&quot;molette&quot;, fonction (événement) \n  if (event.originalEvent.deltaY&gt; 0) \n    sceneTransformScale - = 0,05;\n   autre \n    sceneTransformScale + = 0,05;\n  "},{"id":"text-93","heading":"Text","content":"  changeViewport ();\n);"},{"id":"text-94","heading":"Text","content":"Maintenant, nous pouvons commencer à faire pivoter la scène. Nous avons besoin:"},{"id":"text-95","heading":"Text","content":"Un écouteur d&#39;événements pour le début de l&#39;action de glissement\nUn écouteur d&#39;événements lorsque la souris se déplace (tout en faisant glisser)\nUn écouteur d&#39;événements pour quand l&#39;action de glisser s&#39;arrête"},{"id":"text-96","heading":"Text","content":"Quelque chose comme ça devrait faire l&#39;affaire:\nNumber.prototype.toInt = String.prototype.toInt = function () \n  return parseInt (this, 10);\n;"},{"id":"text-97","heading":"Text","content":"laissez lastMouseX = null;\nlaissez lastMouseY = null;"},{"id":"text-98","heading":"Text","content":"$ body.on (&quot;mousedown&quot;, fonction (e) \n  lastMouseX = e.clientX / 10;\n  lastMouseY = e.clientY / 10;\n);"},{"id":"text-99","heading":"Text","content":"$ body.on (&quot;mousemove&quot;, fonction (e) \n  si (! lastMouseX) \n    revenir;\n  "},{"id":"text-100","heading":"Text","content":"  laissez nextMouseX = e.clientX / 10;\n  laissez nextMouseY = e.clientY / 10;"},{"id":"text-101","heading":"Text","content":"  if (nextMouseX! == lastMouseX) \n    deltaX = nextMouseX.toInt () - lastMouseX.toInt ();\n    degrés = sceneTransformZ - deltaX;"},{"id":"text-102","heading":"Text","content":"    si (degrés&gt; 360) \n        degrés - = 360;\n    "},{"id":"text-103","heading":"Text","content":"    si (degrés < 0) \n        degrees += 360;"},{"id":"text-104","heading":"Text","content":"sceneTransformZ = degrees;\n    lastMouseX = nextMouseX;"},{"id":"text-105","heading":"Text","content":"changeViewport();"},{"id":"text-106","heading":"Text","content":"if (nextMouseY !== lastMouseY) \n    deltaY = nextMouseY.toInt() - lastMouseY.toInt();\n    degrees = sceneTransformX - deltaY;"},{"id":"text-107","heading":"Text","content":"if (degrees > 360) \n        degrés - = 360;\n    "},{"id":"text-108","heading":"Text","content":"    si (degrés &lt;0) \n        degrés + = 360;\n    "},{"id":"text-109","heading":"Text","content":"    sceneTransformX = degrés;\n    lastMouseY = nextMouseY;"},{"id":"text-110","heading":"Text","content":"    changeViewport ();\n  \n);"},{"id":"text-111","heading":"Text","content":"$ body.on (&quot;mouseup&quot;, fonction (e) \n  lastMouseX = null;\n  lastMouseY = null;\n);"},{"id":"text-112","heading":"Text","content":"Sur souris vers le bas nous capturons la souris initiale X et y coordonnées. Lorsque la souris se déplace (si le bouton est toujours enfoncé), nous ajustons la sceneTransformZ et sceneTransformX d&#39;un montant réduit. Il n&#39;y a pas de mal à laisser passer les valeurs 360 degrés ou moins 0 degrés, mais ceux-ci seraient terribles si nous voulions les afficher à l&#39;écran."},{"id":"text-113","heading":"Text","content":"Calcul à l&#39;intérieur d&#39;un déplacer la souris l&#39;écouteur d&#39;événements peut être coûteux en calcul en raison de la quantité de déclencheurs qui peuvent être déclenchés. Il y a potentiellement des millions de pixels sur l&#39;écran, et cet écouteur peut être déclenché lorsque la souris se déplace vers chacun d&#39;eux. C’est pourquoi nous quittons tôt si le bouton de la souris n’est pas maintenu enfoncé."},{"id":"text-114","heading":"Text","content":"Lorsque le bouton de la souris est relâché, nous désactivons lastMouseX et lastMouseY, de sorte que la déplacer la souris l&#39;auditeur arrête de calculer les choses. On pourrait juste effacer lastMouseX, mais effacer les deux me semble plus propre.\nMalheureusement, l&#39;événement mousedown peut interférer avec l&#39;événement click sur les côtés du bloc. Nous pouvons contourner cela en empêchant le bouillonnement des événements:\n$ scene.on (&quot;mousedown&quot;, fonction (e) \n  e.stopPropagation ();\n);"},{"id":"text-115","heading":"Text","content":"Essayer…\nVoir le zoom et la rotation du stylet par SitePoint (@SitePoint) sur CodePen.\nSuppression de blocs\nComplétons l&#39;expérience en ajoutant la possibilité de supprimer des blocs. Nous devons faire quelques choses subtiles mais importantes:"},{"id":"text-116","heading":"Text","content":"Changer la couleur de la bordure de survol du vert au rouge\nDésactivez les fantômes bloqués"},{"id":"text-117","heading":"Text","content":"Ce sera plus facile à faire avec CSS, tant que nous avons une classe de corps pour indiquer si nous sommes en mode additionnel (normal) ou en mode soustraction:\n$ body.on (&quot;keydown&quot;, fonction (e)  e.controlKey );"},{"id":"text-118","heading":"Text","content":"$ body.on (&quot;keyup&quot;, fonction (e) \n  $ body.removeClass (&quot;soustraction&quot;);\n);"},{"id":"text-119","heading":"Text","content":"Lorsqu&#39;une touche de modification est enfoncée (alt, contrôle, ou commander), ce code s&#39;assurera corps a un soustraction classe. Cela facilite le ciblage de divers éléments à l&#39;aide de cette classe:\n.subtraction .block: hover .side \n  contour: 1px rgba solide (255, 0, 0, 0,5);"},{"id":"text-120","heading":"Text","content":".soustraction .ghost \n  affichage: aucun;"},{"id":"text-121","heading":"Text","content":"Nous recherchons un certain nombre de touches de modification, car différents systèmes d&#39;exploitation interceptent différents modificateurs. Par exemple, touche Alt et metaKey travailler sur macOS, alors que clé de contrôle fonctionne sur Ubuntu.\nSi nous cliquons sur un bloc, lorsque nous sommes en mode soustraction, nous devons le supprimer:\n$ body.on (&quot;clic&quot;, &quot;.side&quot;, fonction (e) \n  const $ this = $ (this);\n  const previous = $ this.data (&quot;block&quot;);"},{"id":"text-122","heading":"Text","content":"  if ($ body.hasClass (&quot;soustraction&quot;)) \n    previous.block.remove ();\n    précédent = null;\n   autre \n    const coordonnées = createCoordinatesFrom (\n      $ this.data (&quot;type&quot;),\n      previous.x,\n      previous.y,\n      previous.z\n    );"},{"id":"text-123","heading":"Text","content":"    const suivant = nouveau Block.Dirt (... coordonnées);\n    next.block.appendTo ($ scene);\n  \n);"},{"id":"text-124","heading":"Text","content":"C&#39;est pareil .côté écouteur d&#39;événements de clic que nous avions auparavant, mais au lieu d&#39;ajouter simplement de nouveaux blocs lorsqu&#39;un côté est cliqué, nous vérifions d&#39;abord si nous sommes en mode de soustraction. Si c&#39;est le cas, le bloc sur lequel nous venons de cliquer est supprimé de la scène.\nLa démo finale\nLa démo finale est merveilleuse pour jouer avec:\nVoir le stylo Suppression de blocs par SitePoint (@SitePoint) sur CodePen.\nIl y a un long chemin à parcourir avant de prendre en charge autant de blocs et d&#39;interactions que Minecraft, mais c&#39;est un bon début. De plus, nous avons réussi à atteindre cet objectif sans avoir à étudier des techniques 3D avancées. C’est une utilisation non conventionnelle (et créative) des transformations CSS!\nSi vous souhaitez en faire plus avec ce code, passez à l&#39;autre moitié de cette aventure. Vous n&#39;avez pas besoin d&#39;être un expert PHP pour interagir avec les serveurs Minecraft. Et imaginez les choses incroyables que vous pouvez faire avec ces connaissances…\nEt n&#39;oubliez pas: ce n&#39;est que la moitié de l&#39;aventure. Si vous souhaitez savoir comment conserver les conceptions sur un serveur réel, consultez la publication sœur, PHP Minecraft Mod. Nous y explorons des moyens d&#39;interagir avec un serveur Minecraft, de le manipuler en temps réel et de répondre aux entrées des utilisateurs."},{"id":"text-125","heading":"Text","content":"Christopher est un écrivain et codeur travaillant chez Over. Il travaille généralement sur l&#39;architecture des applications, mais parfois vous le trouverez en train de construire des compilateurs ou des robots."},{"id":"text-126","heading":"Text","content":"Click to rate this post!\n                                   \n                               [Total: 0  Average: 0]"}],"media":{"primary_image":"https://tutos-gameserver.fr/wp-content/uploads/2020/02/icon.javascript.png"},"relations":[{"rel":"canonical","href":"https://tutos-gameserver.fr/2020/02/09/creation-dun-editeur-javascript-minecraft-3d-sitepoint-un-bon-serveur-minecraft/"},{"rel":"alternate","href":"https://tutos-gameserver.fr/2020/02/09/creation-dun-editeur-javascript-minecraft-3d-sitepoint-un-bon-serveur-minecraft/llm","type":"text/html"},{"rel":"alternate","href":"https://tutos-gameserver.fr/2020/02/09/creation-dun-editeur-javascript-minecraft-3d-sitepoint-un-bon-serveur-minecraft/llm.json","type":"application/json"},{"rel":"llm-manifest","href":"https://tutos-gameserver.fr/llm-endpoints-manifest.json","type":"application/json"}],"http_headers":{"X-LLM-Friendly":"1","X-LLM-Schema":"1.1.0","Content-Security-Policy":"default-src 'none'; img-src * data:; style-src 'unsafe-inline'"},"license":"CC BY-ND 4.0","attribution_required":true,"allow_cors":false}