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?

170
demandé sur Timotei 2009-05-21 16:59:27

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:

Image of tile map

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:

Resulting image from incorrect rendering order

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:

Resulting image from correct rendering order

"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":

Zig-zag approach to rendering seems compact

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:

Coordinates on a zig-zag order rendering

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:

Image showing the tile width and height

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.

483
répondu coobird 2017-02-21 22:04:37

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é.

10
répondu zaratustra 2009-05-21 17:39:44

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...
1
répondu Wouter Lievens 2014-09-24 21:59:02

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;
}
0
répondu Olie 2014-11-11 16:30:33

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".

0
répondu Carlos Lopez 2015-01-09 16:22:01