Dessin mondes de jeu isométriques
Question
Quelle est la bonne façon de dessiner des tuiles isométriques dans un jeu 2D?
J'ai lu des références (comme celui-ci ) suggèrent que les tuiles rendue d'une manière qui va en zig-zag de chaque colonne dans la représentation du tableau 2D de la carte. J'imagine qu'ils devraient tirer plus d'une manière de diamant, où ce qui est dessiné à l'écran plus étroitement concerne ce que le tableau 2D ressemblerait, juste tourné un peu.
Y at-il des avantages ou des inconvénients à ces deux méthodes?
La solution
Mise à jour:. Correction algorithme de rendu de la carte, a ajouté plus d'illustrations, changé formating
Peut-être l'avantage de la technique « zig-zag » pour mettre en correspondance les tuiles à l'écran peut dire que les coordonnées de tuiles x
et y
sont sur les axes verticaux et horizontaux.
approche "Dessin dans un diamant":
En dessinant une carte isométrique en utilisant « dessin dans un diamant », qui je crois fait référence à la carte juste du rendu en utilisant une boucle for
reposant sur un tableau à deux dimensions, comme cet exemple:
tile_map[][] = [[...],...]
for (cellY = 0; cellY < tile_map.size; cellY++):
for (cellX = 0; cellX < tile_map[cellY].size cellX++):
draw(
tile_map[cellX][cellY],
screenX = (cellX * tile_width / 2) + (cellY * tile_width / 2)
screenY = (cellY * tile_height / 2) - (cellX * tile_height / 2)
)
Avantage:
L'avantage de cette approche est qu'il est un simple for
-boucle imbriquée avec une logique assez simple qui fonctionne de manière cohérente dans toutes les tuiles.
Inconvénient:
Un inconvénient de cette approche est que les x
et les coordonnées y
des tuiles sur la carte augmentera dans les lignes diagonales, ce qui pourrait rendre plus difficile à cartographier visuellement l'emplacement sur l'écran de la carte représentée comme un tableau:
Cependant, il va être un écueil à la mise en œuvre du code exemple ci-dessus - l'ordre de rendu causera des tuiles qui sont censées être derrière certaines tuiles à tirer sur le dessus des tuiles en face:
Pour modifier ce problème, l'ordre de for
boucle intérieure doit être inversée - à partir de la valeur la plus élevée, et le rendu vers la valeur inférieure:
tile_map[][] = [[...],...]
for (i = 0; i < tile_map.size; i++):
for (j = tile_map[i].size; j >= 0; j--): // Changed loop condition here.
draw(
tile_map[i][j],
x = (j * tile_width / 2) + (i * tile_width / 2)
y = (i * tile_height / 2) - (j * tile_height / 2)
)
Avec le correctif ci-dessus, le rendu de la carte doit être corrigée:
approche "Zig-Zag":
Avantage:
Peut-être l'avantage de l'approche « zig-zag » approche est que la carte rendue peut sembler être un peu plus verticalement compact que le « diamant »:
Inconvénient:
De essayer de mettre en œuvre la technique zig-zag, l'inconvénient est peut-être qu'il est un peu plus difficile à écrire le code de rendu, car il ne peut pas être écrit aussi simple que for
boucle imbriquée sur chaque élément dans un tableau:
tile_map[][] = [[...],...]
for (i = 0; i < tile_map.size; i++):
if i is odd:
offset_x = tile_width / 2
else:
offset_x = 0
for (j = 0; j < tile_map[i].size; j++):
draw(
tile_map[i][j],
x = (j * tile_width) + offset_x,
y = i * tile_height / 2
)
En outre, il peut être un peu difficile d'essayer de comprendre les coordonnées d'une tuile en raison de la nature décalée de l'ordre de rendu:
Note: Les illustrations fournies dans cette réponse ont été créés avec une implémentation Java du code de rendu de tuiles présenté, avec le tableau suivant int
comme la carte:
tileMap = new int[][] {
{0, 1, 2, 3},
{3, 2, 1, 0},
{0, 0, 1, 1},
{2, 2, 3, 3}
};
Les images de tuiles sont:
-
tileImage[0] ->
Une boîte avec une boîte à l'intérieur. -
tileImage[1] ->
Une boîte noire. -
tileImage[2] ->
Une boîte blanche. -
tileImage[3] ->
Une boîte avec un grand objet gris en elle.
Une note sur Largeurs de tuiles et hauteurs
Les variables tile_width
et tile_height
qui sont utilisés dans les exemples de code ci-dessus se réfèrent à la largeur et la hauteur de la dalle de sol dans l'image représentant la tuile:
En utilisant les dimensions de l'image fonctionnera, tant que les dimensions de l'image et les dimensions de tuiles correspondance. Dans le cas contraire, la carte de tuile pourrait être rendue avec des espaces entre les tuiles.
Autres conseils
De toute façon fait le travail. Je suppose que par zigzag vous dire quelque chose comme ceci: (les chiffres sont ordre de rendu)
.. .. 01 .. ..
.. 06 02 ..
.. 11 07 03 ..
16 12 08 04
21 17 13 09 05
22 18 14 10
.. 23 19 15 ..
.. 24 20 ..
.. .. 25 .. ..
Et par le diamant que vous voulez dire:
.. .. .. .. ..
01 02 03 04
.. 05 06 07 ..
08 09 10 11
.. 12 13 14 ..
15 16 17 18
.. 19 20 21 ..
22 23 24 25
.. .. .. .. ..
La première méthode a besoin plus de tuiles de sorte que l'rendue plein écran est dessiné, mais vous pouvez facilement faire une vérification limite et sauter toutes les tuiles complètement hors écran. Les deux méthodes, il faudra un certain crissement numéro pour savoir ce qui est l'emplacement de la tuile 01. En fin de compte, les deux méthodes sont à peu près égales en termes de mathématiques requis pour un certain niveau d'efficacité.
Si vous avez des tuiles qui dépassent les limites de votre diamant, je vous recommande de dessin pour la profondeur:
...1...
..234..
.56789.
..abc..
...d...
La réponse de Coobird est correcte, on complète. Cependant, je combiné ses notes avec celles d'un autre site pour créer un code qui fonctionne dans mon application (iOS / Objective-C), que je voulais partager avec tous ceux qui viennent ici à la recherche d'une telle chose. S'il vous plaît, si vous aimez / haut-vote cette réponse, faire la même chose pour les originaux; tout ce que je faisais était « debout sur les épaules de géants ».
En ce qui concerne ordre de tri, ma technique est un algorithme de peintre modifié: chaque objet a (a) une altitude de la base (je l'appelle « niveau ») et (b) un X / Y pour la « base » ou " pied » de l'image (exemples: la base de avatar est à ses pieds, la base de l'arbre est à ses racines, la base de l'avion est le centre-image, etc.) ensuite, je viens trier du plus bas au plus haut niveau, puis le plus bas (le plus haut sur l'écran) pour la plus élevée de base Y, alors le plus bas (le plus à gauche) à la base-X le plus élevé. Cela rend les tuiles comme on peut s'y attendre.
Code pour convertir l'écran (point) à carreaux (cellule) et arrière:
typedef struct ASIntCell { // like CGPoint, but with int-s vice float-s
int x;
int y;
} ASIntCell;
// Cell-math helper here:
// http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511
// Although we had to rotate the coordinates because...
// X increases NE (not SE)
// Y increases SE (not SW)
+ (ASIntCell) cellForPoint: (CGPoint) point
{
const float halfHeight = rfcRowHeight / 2.;
ASIntCell cell;
cell.x = ((point.x / rfcColWidth) - ((point.y - halfHeight) / rfcRowHeight));
cell.y = ((point.x / rfcColWidth) + ((point.y + halfHeight) / rfcRowHeight));
return cell;
}
// Cell-math helper here:
// http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds/893063
// X increases NE,
// Y increases SE
+ (CGPoint) centerForCell: (ASIntCell) cell
{
CGPoint result;
result.x = (cell.x * rfcColWidth / 2) + (cell.y * rfcColWidth / 2);
result.y = (cell.y * rfcRowHeight / 2) - (cell.x * rfcRowHeight / 2);
return result;
}
vrai problème est quand vous avez besoin tirer des tuiles / sprites intersection / couvrant deux ou plusieurs autres tuiles.
Après 2 (dur) mois de analisys personnels de problème j'ai finalement trouvé et mis en œuvre une « bonne render dessin » pour mon nouveau jeu cocos2d-js. Solution consiste dans la cartographie, pour chaque tuile (sensible), qui sont sprites « avant, arrière, haut et derrière ». Une fois que faire, vous pouvez les dessiner la suite d'une « logique récursive ».