JeuWeb - Crée ton jeu par navigateur
Application de la 3D isométrique sur des tuiles hexagonales - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : Application de la 3D isométrique sur des tuiles hexagonales (/showthread.php?tid=1209)



Application de la 3D isométrique sur des tuiles hexagonales - Harparine - 22-05-2007

A la suite d'un tuto d'Oncle James sur des maps hexagonales, je propose ici un moyen de mêler hexagones et 3D isométrique sur une même carte. A l'heure actuelle, les explications sont très théoriques mais j'essaierai de proposer 3 cas d'application à la fin : un basé sur les possibilités offertes par le CSS, un utilisant la librairie GD (librairie graphique en php) et un autre en Flash qui reste la technologie que j'affectionne le plus pour ce genre de projets... Bonne lecture ! Wink


1. Créer des tuiles isométriques


Avant tout, je rappelle la procédure pour créer des tuiles isométriques. C'est le standard utilisé dans le pixel-art, entre autres, et la même manipulation est applicable pour tout objet en 2D, quelle que soit sa forme. Il suffit d'appliquer une rotation de 45° sur la droite puis compresser la figure obtenue en divisant sa hauteur par 2. Ce qui donne ceci pour un carré et un hexagone :

[Image: hexaiso1hd4.png]

On se retrouve donc avec des images rectangulaires contenant des formes tordues, d'où la difficulté de les coller les unes aux autres. Je rappelle que si l'on considère la carte comme un plan orthonormé, l'origine est en haut à gauche (standard en CSS, GD, Flash et j'imagine bien d'autres langages). Idem pour nos images de tuiles qui ressemblent à ça :
[Image: 31185584yu7.png]


2. Des formules différentes


En prenant l'exemple d'un carré en 2D en iso, on voit bien que la position de chaque case ne peut être déterminée de la même façon si l'on est en 2D ou en 3D isométrique. L'exemple suivant montre le problème que l'on rencontre si l'on place les tuiles de la même manière en utilisant la formule pour la 2D qui est la suivante :
Code :
posX = X*n
posY = Y*n
où n est la longueur d'un côté et X et Y les coordonnées à partir de 0 :

[Image: 28302829cd0.png]

Avant d'attaquer les hexotuiles, je donne juste la formule permettant d'aligner les carrés iso sur une carte, comme ceci (je marque l'entourage des images pour que vous repériez bien où est l'origine de la tuile) :

[Image: 51696465um3.png]

(pour cet exemple, je ne vais pas utiliser la taille théorique des côtés - c'est à dire la taille en vue 2D - car il est plus facile de se baser sur l'image mais je le ferai pour les hexagones).
Il y a quelques temps j'avais trouvé sur jeflash.com la formule suivante (facilement vérifiable si vous étudiez le schéma précédent) pour placer les tuiles isométriques de forme carrée :
Code :
posX = (X-Y) * largeur tuile
posY = (X+Y)/2 * largeur tuile
(note l'isométrique place des tuiles en abscisses négatives. Il est donc souvent utile de décaler l'ensemble de la carte pour que l'on voit également la zone situées à gauche. Pour celà, il suffit de rajouter une variable "decalage", par exemple à posX. Ce décalage correspond à nbLignes-1*largeurTuiles/2).


3. Etude des hexagones


Maintenant que l'on connait la formule de positionnement de tuiles iso carrées et que l'on sait les contraintes de la représentation isométrique appliquée à la forme la plus simple, on peut commencer à regarder du côté des hexagones. En regardant des maps d'hexagones isométriques, je me suis rendu compte qu'il serait peu commode de travailler de façon abstraite sur la taille des images et qu'il valait mieux tenir compte des dimensions réelles de la figure contenue dans l'image. La formule d'Oncle James contenue dans son tuto est très bien pour des images aux dimensions bien précises. Je l'ai cependant réécrite pour quelle soit applicable d'après les dimensions de l'hexagone en lui même et non pas selon la taille de l'image qui le contient. Tout d'abord, voilà les dimensions d'un hexagone en vue 2D :

[Image: 89702766yx6.png]

On constate donc qu'un hexagone de côté n prend une largeur valant n + n/2*2 soit simplement n*2. Pour résumer, un hexagone de 50 pixels de côté prendra 100 pixels de large. Pour calculer la hauteur, je calcule tout d'abord une moitiée de hauteur en me basant sur un triangle rectangle dont l'hypoténuse vaut n et les deux autres angles 60° (la moitié d'un angle d'hexagone) et 30°. Avec un peu de trigonométrie, on apprend que la moitié de la hauteur d'un hexagone fait sin(60)*n (on aurait pu utiliser cos(30)*n aussi). Un hexagone fait donc sin(60)*n*2 unités de hauteur.
Code :
LargeurHexa = n*2
HauteurHexa = sin(60)*n*2

Sachant celà, réfléchissons à la création d'une carte hexagonale simple (en 2D) dans ce style là :

[Image: 41120092ar0.png]

Je n'ai pas tracé de repères mais en regardant bien, on constate qu'en abscisse, une tuile est placée un peu sur la tuile précédente : elle débute à l'endroit où arrive le côté le plus haut de sa voisine (qui vaut n, si on regarde le schéma précédent). Il ne faut pas oublier de compter le coté adjacent de gauche qui fait n/2 de largeur. On a donc une tuile qui se place n+n/2 pixels plus loin que sa voisine de gauche. Si on multiplie par le numéro de colonne (X), on obtient la formule suivante :
Code :
posX = X*1.5*n;

Pour la position de la tuile en Y, c'est à peine plus compliqué : on place les hexagones les uns sur les autres. Il nous suffit donc de multiplier le numéro de la ligne (Y, qui commence à 0, ne l'oublions pas) par la hauteur d'un hexagone, ce qui nous donne : posY = Y*sin(60)*2*n. Oui mais là, notre formule est incomplète car les cartes hexagonales nécessitent d'alterner les cases paires et impaires (c'est cet effet dentelé que l'on a en haut et en bas de la carte). Pour celà, on rajoute va simplement décaler d'une demie hauteur de plus les hexagones impairs en X. La formule X%2 renvoie 0 ou 1 : l'opérateur % (le modulo) retourne le reste d'une division euclidienne. A nous de multiplier ce chiffre par une demie hauteur (si la case est paire, ce décalage sera nul). La formule finale de posY est donc la suivante :
Code :
posY = Y*sin(60)*2*n + (X%2)*sin(60)*n
.

4. Passons à l'isométrique

Bien, enfin le coeur du sujet ! La difficulté va maintenant être l'association des contraintes liées à l'isométrique (que nous avons vues plus haut) et des contraintes liées à l'hexagonal. Comme précédemment, j'ai passé beaucoup de temps sur l'étude des hexagones en eux-mêmes. Voici le schéma d'un hexagone ayant subi une simple rotation à 45° vers la droite :

[Image: 78016242jf1.png]

Cette figure va nous servir jusqu'à la fin pour trouver la formule. J'ai tracé des triangles rectangles dans la figure pour pouvoir connaitre les hauteurs et largeurs des différents côtés de notre hexagone tourné grâce à la trigonométrie. Je ne m'attarde pas sur la figure et je continue. Dans un premier temps, voilà ce que je cherche à obtenir :

[Image: 91924168so5.png]

Si vous avez l'oeil exercé à la vue isométrique, vous constaterez que cette carte est fausse car elle part trop vers le haut. Mais si nous parvenons à placer les tuiles de cette façon, une très légère modification, tenant compte du crènelage propre à l'hexagonal, permettra d'avoir une carte parfaite. Bon, je rajoute un schéma de plus (encore^^!) pour voir où est l'origine de nos hexotuiles isométriques :
[Image: 10vk2.png]

Alors, si on regarde la première ligne et que l'on cherche la position en X, on constate qu'une tuile commence là où se terminent les deux côtés les plus bas de sa voisine de gauche (le deuxième trait rouge, en haut). Il faut donc mesurer la largeur prise par ces deux côtés (qui sont tordus). Pour celà, on va utiliser le schéma de l'hexagone tourné (même si il n'est pas écrasé, la largeur reste la même). Comme les côtés opposés sont identiques, on peut utiliser les formules que j'ai notées et la somme des largeurs des deux côtés fait donc : sin(75)*n + sin(45)*n. Il suffit de multiplier par le numéro de colonne, et on place les tuiles à la suite les unes des autres.

Mais ce positionnement en abscisse est valable uniquement pour la première ligne : si vous vous rappelez la formule utilisée par les carrés isométriques, chaque nouvelle ligne est décalée sur la gauche. On va donc reprendre ce principe et regarder de combien sont décalées les lignes les unes par rapport au autres. Un peu d'observation nous apprend que le point le plus à gauche de la deuxième ligne est séparé par deux côtés du point le plus à gauche de la première ligne. Un coup d'oeil au schéma précédent et on sait que ce décalage vaut sin(15)*n + sin(75)*n. Puisque l'on reproduit ce décalage à chaque nouvelle ligne créée, on multiplie cette petite formule par le numéro de la ligne. La formule finale est donc la suivante :
Code :
posX = X*(sin(75)+sin(45))*n - Y*(sin(15) + sin(75))*n

Il reste maintenant à trouver le positionnement vertical. Même chose que précédemment : on regarde le nombre de côtés d'écart. Par exemple, le côté le plus haut de l'hexagone situé en bas à gauche est à 2 côtés de distance du coin le plus haut de son voisin de dessus. On se reporte au schéma des distances et on calcule l'ensemble, ce qui nous donne sin(75)*n + sin(15)*n unités de distance. Mais ce schéma n'était pas écrasé (la dernière étape pour transformer une forme en son équivalent isométrique). On divise donc la valeur obtenue par deux, sans oublier de la multiplier par le nombre de lignes, ce qui nous donne
posY = Y(sin(75)+sin(15))*n/2

Bon, mais on a encore un léger problème, lié à la représentation isométrique. On est obligé de décaler légèrement une case vers le bas quand on ajoute une nouvelle colonne. Si on observe la première ligne bleue sur le schéma contenant les quatre tuiles hexagonales, on se rend compte que, pour la première fois, on ne tombe pas sur un angle. On ne va donc pas pouvoir utiliser la même technique que précédemment (celle du comptage de côtés^^) pour mesurer ce décalage. J'ai beaucoup chercher un moyen de trouver la valeur exacte (sin(75)-sin(45) s'en rapproche beaucoup) mais je ne l'ai pas trouvé (si un super matheux est motivé, il peut chercher Wink). J'ai donc recherché la valeur approchée la plus précise possible à l'aide de sin-1 sur un cas d'application. Bref, le décalage est de sin(13.5)*n à peu de choses près, et comme on en tient compte à chaque nouvelle colonne, il ne faut pas oublier de la multiplier pour chaque valeur de X. On se retrouve donc avec la formule finale suivante :
Code :
posX = X*(sin(75)+sin(45))*n - Y*(sin(15) + sin(75))*n
posY = Y*(sin(75)+sin(15))*n/2 +X*sin(13.5)*n

Ouf. On y est presque ! Cette formule permet donc de générer la carte (fausse) que je vous ai présenté au début du point n°4. Mais une vraie carte hexagonale isométrique ressemble plutôt à ça :
[Image: 76727680dy2.png]

Nous devons donc respecter le crènelage des bords et les hexagones doivent suivre une ligne plus naturelle. Pour celà, il suffit juste de modifier la formule précédente. Si on veut le crènelage en abscisses, on décale d'une hauteur d'hexagone toutes les deux colonnes. Pour celà, il suffit remplacer la valeur Y des deux formules précédentes par Y+un arrondi au supérieur de X/2, ce qui nous donne :
Code :
posX = X*(sin(75)+sin(45))*n - (Y+arrondi.sup(X/2))*(sin(15) + sin(75))*n
posY = (Y+arrondi.sup(X/2))*(sin(75)+sin(15))*n/2 + X*sin(13.5)*n
Même principe si on veut le crénelage sur l'axe des ordonnées : on remplace X par X+arrondi.sup(Y/2).

Enfin, la dernière chose à prendre en compte est le décalage à gauche occasionné par la 3D isométrique. Pour compenser tout ça, nous devons donc ajouter une variable "decalage" à posX, pour forcer le placement plus loin vers la droite. Si le crènelage est en abscisse, le décalage est facile à trouver car il est basé sur le décalage négatif de chaque nouvelle ligne (sin(15)+sin(75))*n. Si on multiplie cela par le nombre total de lignes - 1 (on commence à 0), on a le décalage final.
Code :
decalage = (H-1)*(sin(15)+sin(75))*n
Par contre, si notre crènelage est en ordonnées (à gauche), il faut en tenir compte dans la variable décalage. Le calcul est un peu plus compliqué, mais pas bien sorcier : on décale de la largeur du côté de gauche (sin(15)*n) et on double le chiffre une fois sur deux (le crènelage), ce qui donne :
Code :
decalage = arrondi.inf((H-1)*1.5)*sin(15)*n

Voilà ! Normalement, on a une carte nickel ! Ouf, pas fâché d'avoir terminé^^ C'est un vrai tuto, finalement ! Je précise une dernière chose : n'oubliez pas que la valeur n (taille des côtés de l'hexagone) est toujours basée sur la représentation à plat de la tuile (en 2D). C'est à dire qu'il suffit de connaitre la taille de notre hexagone avant la transformation pour utiliser cette formule.

Enfin, prenez garde : beaucoup de langages de programmation utilisent les radians plutôt que les degrés dans les fonctions de trigo. Si nécessaire, vous devez donc convertir vos degrés en radians en multipliant par PI et en divisant par 180.

Bon, j'arrête pour l'instant. J'expliquerai juste à la suite quelles mesures prendre pour dessiner du relief et des éléments de décor utilisables. (je posterai plus loin). J'essaierai aussi de proposer un vrai code PHP et pas seulement de la théorie. Mais le mieux est de créer deux fonctions prenant les coordonnées de la case en paramètre et renvoyant l'abscisse pour l'une et l'ordonnée pour l'autre. Après, une simple boucle suffit à assembler la carte. (J'essaierai peut-être de transformer ces explications en un vrai tuto plus tard) !

@+ (pour ceux qui m'ont lu jusqu'au bout) Wink


RE: Application de la 3D isométrique sur des tuiles hexagonales - orditeck - 22-05-2007

L'aide pour ce tutoriel ce trouve à cette adresse :
http://www.jeuweb.org/board/showthread.php?tid=1715