2009-05-21 5 views
166

Wie ist die richtige Art isometrische Kacheln in einem 2D Spiel zu zeichnen?Zeichnen isometrische Spielwelten

Ich habe Referenzen gelesen (wie this one), die darauf hindeuten, dass die Kacheln auf eine Weise gerendert werden, die jede Spalte in der 2D-Array-Darstellung der Karte zick-zack. Ich stelle mir vor, dass sie eher diamantförmig gezeichnet sein sollten, wo sich das, was auf den Bildschirm gezogen wird, eher auf das bezieht, wie das 2D-Array aussehen würde, nur ein wenig gedreht.

Gibt es Vorteile oder Nachteile für beide Methoden?

Antwort

470

Aktualisierung: Korrigierter Kartenrendering-Algorithmus, weitere Illustrationen hinzugefügt, Formatierung geändert.

Vielleicht ist der Vorteil für die „Zick-Zack“ -Technik zur Abbildung der Kacheln auf den Bildschirm kann, dass die x und y Koordinaten auf den vertikalen und horizontalen Achsen sind die Fliesen gesagt werden.

„Zeichnung in einem Diamanten“ -Ansatz:

Durch eine isometrische Karte Zeichnung mit „in einem diamantenen“, das ich glaube, bezieht sich auf nur die Karte Rendering durch einen verschachtelten for -loop über die beide mit -dimensional Array, wie dieses Beispiel:

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) 
     ) 

Vorteil:

der Vorteil für den Ansatz ist, dass es eine einfache verschachtelte for-Schleife mit ziemlich geradliniger Logik, die durchgängig in allen Kacheln funktioniert.

Nachteil:

Ein Nachteil dieses Ansatzes ist, dass die x und y Koordinaten der Fliesen auf der Karte in diagonalen Linien zu erhöhen, was es optisch schwieriger machen könnte, um die Position auf dem Bildschirm abzubilden auf die Karte als Array dargestellt wird:

Image of tile map

jedoch wird ein pitfall zur Umsetzung des obigen Beispielcode, dort sein - die Render-Reihenfolge verursacht Fliesen, die soll hinter bestimmten Fliesen sein oben auf den Fliesen vor gezogen werden:

Resulting image from incorrect rendering order

Um dieses Problem zu ändern, der inner for -loop der Reihenfolge umgekehrt werden muss - vom höchsten Wert ausgehend und in Richtung des unteren Wert Rendering:

Resulting image from correct rendering order

:

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) 
     ) 

Mit der obigen Lösung, die Wiedergabe der Karte soll korrigiert werden

„Zig-Zag“ -Ansatz:

Vorteil:

Vielleicht ist der Vorteil der „Zick-Zack“ -Ansatz ist, dass die übertragene Karte ein wenig mehr vertikal kompakt erscheinen als der "Diamant" Ansatz:

Zig-zag approach to rendering seems compact

Nachteil:

Aus dem Versuch, die Zick-Zack-Technik zu implementieren, ist der Nachteil, dass es ein wenig schwieriger ist, den Rendering-Code zu schreiben, weil es nicht verschachtelt for -loop über jedes Element in einem Array geschrieben werden kann:

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 
     ) 

auch kann es ein wenig schwierig zu versuchen, die Koordinate einer Fliese aufgrund der gestaffelten Art des Renderings, um herauszufinden:

Coordinates on a zig-zag order rendering

Hinweis: die Abbildungen enthalten in diesem s Antwort wurde mit einer Java-Implementierung des Kachel-Rendering-Code präsentiert, mit der folgenden int Array als Karte erstellt:

tileMap = new int[][] { 
    {0, 1, 2, 3}, 
    {3, 2, 1, 0}, 
    {0, 0, 1, 1}, 
    {2, 2, 3, 3} 
}; 

Die Kachelbilder sind:

  • tileImage[0] -> Ein Kästchen mit einem inneren Kasten.
  • tileImage[1] -> Eine Blackbox.
  • tileImage[2] -> Eine weiße Box.
  • tileImage[3] -> Eine Box mit einem hohen grauen Objekt drin.

Ein Hinweis auf Fliese Breiten und Höhen

Die Variablen tile_width und tile_height, die in den obigen Code Beispielen verwendet werden, beziehen sich auf die Breite und die Höhe der Bodenfliese in dem Bild, die die Fliese:

Image showing the tile width and height

die Abmessungen des Bildes Mit funktionieren wird, solange die Bildabmessungen und der Fliesenmaße übereinstimmen. Andernfalls könnte die Kachelkarte mit Lücken zwischen den Kacheln gerendert werden.

+124

Sie zeichnete sogar Bilder. Das ist Anstrengung. – zaratustra

+0

Prost coolabird. Ich verwende den Diamond-Ansatz für das aktuelle Spiel, das ich gerade entwickle, und es funktioniert. Danke noch einmal. –

+0

Gut zu hören, dass die Dinge funktionieren :) Viel Glück! – coobird

10

In jedem Fall wird die Arbeit erledigt. Ich gehe davon aus, dass durch Zickzack Sie so etwas wie dies bedeuten: (Zahlen Reihenfolge der Rendering sind)

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

Und Diamant meinen Sie:

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

Die erste Methode gerendert mehr Steine ​​benötigt, so dass die Vollbild wird gezeichnet, aber Sie können leicht eine Grenzkontrolle durchführen und alle Kacheln vollständig aus dem Bildschirm überspringen. Beide Methoden erfordern ein paar Zahlenkalkulationen, um herauszufinden, wo sich die Kachel 01 befindet. Am Ende sind beide Methoden ungefähr gleich in der Mathematik, die für eine bestimmte Effizienzstufe erforderlich ist.

+14

Ich meinte eigentlich anders herum. Die Rautenform (die die Kanten der Karte glatt macht) und die Zick-Zack-Methode, wobei die Kanten spikey bleiben –

1

Wenn Sie einige Fliesen, die die Grenzen Ihres Diamanten überschreiten, empfehle ich in der Tiefe, um Zeichnung:

...1... 
..234.. 
.56789. 
..abc.. 
...d... 
0

Coobird Antwort die Richtigkeit, Vollständigkeit ist. Allerdings habe ich seine Hinweise mit denen von einer anderen Seite kombiniert, um Code zu erstellen, der in meiner App funktioniert (iOS/Objective-C), den ich mit jedem teilen wollte, der hier nach einer solchen Sache sucht. Bitte, wenn Sie diese Antwort mögen/stimmen, tun Sie dasselbe für die Originale; Ich habe nur "auf den Schultern von Riesen stehen".

Bei der Sortierreihenfolge ist meine Technik ein modifizierter Maleralgorithmus: Jedes Objekt hat (a) eine Höhe der Basis (ich nenne "Ebene") und (b) ein X/Y für die "Basis" oder "Fuß" des Bildes (Beispiele: die Basis des Avatars ist zu seinen Füßen; die Basis des Baums ist an seinen Wurzeln; die Basis des Flugzeugs ist das Zentrum-Bild, etc.) Dann sortiere ich die niedrigste bis zur höchsten Ebene, dann die niedrigste (höchste auf dem Bildschirm) zur höchsten Basis-Y, dann niedrigste (am weitesten links) zur höchsten Basis-X. Dies macht die Fliesen so, wie man es erwarten würde.

-Code-Bildschirm (Punkt) zu Kachel (Zelle) konvertieren und zurück:

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

eigentliche Problem ist, wenn Sie einige Fliesen/Sprites zeichnen müssen sich schneid/Spanning zwei oder mehreren anderen Fliesen.

Nach 2 (harten) Monaten persönlicher Problemanalyse habe ich endlich eine "korrekte Renderzeichnung" für mein neues cocos2d-js Spiel gefunden und implementiert. Die Lösung besteht darin, für jede Kachel (anfällig) zu mappen, welche Sprites "vorne, hinten, oben und hinten" sind. Sobald Sie das getan haben, können Sie sie nach einer "rekursiven Logik" zeichnen.