Le dessin des mondes de jeu isométrique
Quelle est la bonne façon de dessiner des tuiles isométriques dans un jeu 2D?
j'ai lu des références (telles que celle-ci ) qui suggèrent que les carreaux soient rendus d'une manière qui zig-zag chaque colonne dans le tableau 2D de la représentation de la carte. J'imagine qu'ils devraient être dessinés plus en diamant, où ce qui est dessiné sur l'écran se rapporte plus étroitement à ce à quoi ressemblerait le tableau 2D, juste tourné un peu.
sont il y a des avantages ou des inconvénients soit pour la méthode?
5 réponses
mise à Jour: correction de la carte algorithme de rendu, a ajouté plus d'illustrations, changé le formatage.
peut-être l'avantage de la technique "zig-zag" pour la cartographie des tuiles à l'écran peut être dit que les coordonnées x
et y
de la tuile sont sur les axes verticaux et horizontaux.
"Dessin dans un diamant":
par dessin carte isométrique en utilisant "dessin dans un diamant", qui je crois se réfère à rendre juste la carte en utilisant un emboîté for
- boucle sur le tableau bidimensionnel, tel que 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 l'approche est qu'il s'agit d'une simple boucle imbriquée for
- avec une logique assez simple en avant qui fonctionne uniformément dans tous les carreaux.
Désavantage:
un inconvénient de cette approche est que les coordonnées x
et y
des tuiles sur la carte vont augmenter en lignes diagonales, ce qui pourrait rendre plus difficile de cartographier visuellement l'emplacement sur l'écran à la carte représentée comme un tableau:
cependant, il va y avoir un obstacle à la mise en œuvre de ce qui précède exemple de code -- l'ordre de rendu fera que les tuiles qui sont censées être derrière certaines tuiles seront dessinées sur les tuiles devant:
pour corriger ce problème, l'ordre interne de la boucle for
doit être inversé -- à partir de la valeur la plus élevée, et le rendu vers la valeur la plus basse:
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 la fixation ci-dessus, le rendu de la carte doit être corrigée:
"Zigzag":
avantage:
peut-être que l'avantage de l'approche "zig-zag" est que la carte rendue peut sembler un peu plus compacte verticalement que l'approche "diamond":
désavantage:
D'essayer de mettre en œuvre la technique zig-zag, l'inconvénient peut être qu'il est un peu plus difficile d'écrire le code de rendu parce qu'il ne peut pas être écrit aussi simple qu'une boucle for
imbriquée sur chaque élément d'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
)
aussi, il peut être un peu difficile d'essayer de comprendre la coordonnée d'une tuile en raison de le caractère échelonné de l'ordre d'équarrissage:
Note: les illustrations incluses dans cette réponse ont été créées avec une implémentation Java du code de rendu des 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 la tuile 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.
Une Note sur les Carreaux des Largeurs et des Hauteurs
les variables tile_width
et tile_height
qui sont utilisées dans les exemples de code ci-dessus se rapportent à la largeur et à la hauteur de la tuile de sol dans l'image représentant la tuile:
utilisant les dimensions de l'image fonctionnera, aussi longtemps que les dimensions de l'image et les dimensions de la tuile correspondent. Sinon, la carte des carreaux pourrait être rendue avec des espaces entre les carreaux.
de toute façon fait le travail. Je suppose que par zigzag vous voulez dire quelque chose comme ceci: (les nombres sont l'ordre du 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 diamant 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 nécessite plus de tuiles rendues de sorte que le plein écran est dessiné, mais vous pouvez facilement faire une vérification des limites et sauter toutes les tuiles complètement hors-écran. Les deux méthodes nécessitent quelques calculs pour savoir quel est l'emplacement de la tuile 01. Dans le fin, les deux méthodes sont à peu près égales en termes de maths pour un certain niveau d'efficacité.
si vous avez quelques tuiles qui dépassent les limites de votre diamant, je recommande le dessin en profondeur ordre:
...1...
..234..
.56789.
..abc..
...d...
la réponse de Coobird est la bonne, remplissez-en une. Cependant, j'ai combiné ses conseils avec ceux d'un autre site pour créer du code qui fonctionne dans mon application (iOS/Objective-C), que je voulais partager avec quiconque vient ici à la recherche d'une telle chose. S'il vous plaît, si vous aimez/-voix de cette réponse, faites de même pour les originaux; je n'ai fait que "debout sur les épaules de géants."
quant à l'ordre de tri, ma technique est un algorithme de peintre modifié: chaque objet a (a) un altitude de la base (j'appelle "niveau") et (b) Un X/Y pour la "base" ou "pied" de l'image (exemples: la base d'avatar est à ses pieds; La base de l'arbre est à ses racines; la base de l'avion est au centre de l'image, etc.) Puis je trie juste le plus bas au plus haut niveau, puis le plus bas (le plus haut à l'écran) au plus haut niveau de base-Y, puis le plus bas (left-most) au plus haut niveau de base-X. Cela rend les tuiles de la façon dont on s'attendrait.
Code pour convertir l'écran (point) en tuile (cellule) et retour:
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;
}
le vrai problème est quand vous avez besoin de dessiner des tuiles/sprites intersectant/enjambant deux ou plusieurs autres tuiles.
après 2 mois d'analyses personnelles de problèmes, j'ai finalement trouvé et implémenté un" rendu correct " pour mon nouveau jeu cocos2d-js. Solution consiste en mapping, pour chaque tuile (susceptible), qui sprites sont "avant, arrière, haut et derrière". Une fois cela fait, vous pouvez les dessiner en suivant une "logique récursive".