Advertisement
  1. Game Development
  2. Phaser

Eine aktualisierte Grundierung zum Erstellen isometrischer Welten, Teil 1

by
Read Time:16 minsLanguages:
This post is part of a series called Primer for Creating Isometric Worlds.
Updated Primer for Creating Isometric Worlds, Part 2

German (Deutsch) translation by Władysław Łucyszyn (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Wir haben alle unseren fairen Anteil an erstaunlichen isometrischen Spielen gespielt, sei es das Original Diablo oder Age of Empires oder Commandos. Als Sie zum ersten Mal auf ein isometrisches Spiel gestoßen sind, haben Sie sich vielleicht gefragt, ob es sich um ein 2D-Spiel, ein 3D-Spiel oder etwas völlig anderes handelt. Die Welt der isometrischen Spiele hat auch für Spieleentwickler eine mystische Anziehungskraft. Lassen Sie uns versuchen, das Geheimnis der isometrischen Projektion zu lösen und in diesem Tutorial eine einfache isometrische Welt zu erstellen.

Dieses Tutorial ist eine aktualisierte Version meines vorhandenen Tutorials zum Erstellen isometrischer Welten. Das ursprüngliche Tutorial verwendete Flash mit ActionScript und ist weiterhin für Flash- oder OpenFL-Entwickler relevant. In diesem neuen Tutorial habe ich beschlossen, Phaser mit JS-Code zu verwenden und dabei eine interaktive HTML5-Ausgabe anstelle einer SWF-Ausgabe zu erstellen.

Bitte beachten Sie, dass dies kein Phaser-Entwicklungs-Tutorial ist, sondern dass wir Phaser nur verwenden, um die Kernkonzepte der Erstellung einer isometrischen Szene einfach zu kommunizieren. Außerdem gibt es viel bessere und einfachere Möglichkeiten, isometrische Inhalte in Phaser zu erstellen, wie beispielsweise das Phaser Isometric Plugin.

Der Einfachheit halber werden wir den kachelbasierten Ansatz verwenden, um unsere isometrische Szene zu erstellen.

1. Kachelbasierte Spiele

In 2D-Spielen, die den kachelbasierten Ansatz verwenden, wird jedes visuelle Element in kleinere Teile, sogenannte Kacheln, mit einer Standardgröße zerlegt. Diese Kacheln werden so angeordnet, dass sie die Spielwelt nach vorher festgelegten Leveldaten bilden - normalerweise nach einem zweidimensionalen Array.

Zusammenhängende Posts

Normalerweise verwenden kachelbasierte Spiele entweder eine Ansicht von oben nach unten oder eine Seitenansicht für die Spielszene. Betrachten wir eine Standard-2D-Ansicht von oben nach unten mit zwei Kacheln - einer Gras- und einer Wandkachel - wie hier gezeigt:

Green and Maroon tilesGreen and Maroon tilesGreen and Maroon tiles

Beide Kacheln sind quadratische Bilder derselben Größe, daher sind die Kachelhöhe und die Kachelbreite gleich. Betrachten wir ein Spiellevel, bei dem es sich um ein Grasland handelt, das allseitig von Mauern umgeben ist. In einem solchen Fall sehen die mit einem zweidimensionalen Array dargestellten Ebenendaten folgendermaßen aus:

Hier bezeichnet 0 eine Grasfliese und 1 eine Wandfliese. Wenn Sie die Kacheln gemäß den Niveaudaten anordnen, wird unser ummauertes Grasland wie im Bild unten gezeigt erzeugt:

Top view level - grass area surrounded by wallsTop view level - grass area surrounded by wallsTop view level - grass area surrounded by walls

Wir können noch ein bisschen weiter gehen, indem wir Eckkacheln hinzufügen und vertikale und horizontale Wandkacheln trennen, wobei fünf zusätzliche Kacheln erforderlich sind. Dies führt uns zu unseren aktualisierten Leveldaten:

Schauen Sie sich das Bild unten an, in dem ich die Kacheln mit den entsprechenden Kachelnummern in den Leveldaten markiert habe:

Better top view level with corner tiles and tile numbersBetter top view level with corner tiles and tile numbersBetter top view level with corner tiles and tile numbers

Nachdem wir das Konzept des kachelbasierten Ansatzes verstanden haben, möchte ich Ihnen zeigen, wie wir einen einfachen 2D-Gitter-Pseudocode verwenden können, um unser Level zu rendern:

Wenn wir die obigen Kachelbilder verwenden, sind die Kachelbreite und die Kachelhöhe gleich (und für alle Kacheln gleich) und stimmen mit den Abmessungen der Kachelbilder überein. Die Kachelbreite und die Kachelhöhe für dieses Beispiel betragen also jeweils 50 Pixel, was die Gesamtebenengröße von 300 x 300 Pixel ausmacht, d.h. sechs Zeilen und sechs Spalten mit jeweils 50 x 50 Pixel.

Wie bereits erwähnt, implementieren wir bei einem normalen kachelbasierten Ansatz entweder eine Ansicht von oben nach unten oder eine Seitenansicht. Für eine isometrische Ansicht müssen wir die isometrische Projektion implementieren.

2. Isometrische Projektion

Die beste technische Erklärung dafür, was isometrische Projektion bedeutet, ist meines Wissens aus diesem Artikel von Clint Bellanger:

Wir drehen unsere Kamera um zwei Achsen (schwenken Sie die Kamera um 45 Grad zur Seite und dann um 30 Grad nach unten). Dadurch entsteht ein rautenförmiges Gitter, bei dem die Gitterräume doppelt so breit wie hoch sind. Dieser Stil wurde durch Strategiespiele und Action-Rollenspiele populär gemacht. Wenn wir in dieser Ansicht einen Würfel betrachten, sind drei Seiten sichtbar (obere und zwei gegenüberliegende Seiten).

Obwohl es etwas kompliziert klingt, ist die Implementierung dieser Ansicht sehr einfach. Was wir verstehen müssen, ist die Beziehung zwischen dem 2D-Raum und dem isometrischen Raum, d.h. die Beziehung zwischen den Ebenendaten und der Ansicht. die Transformation von Kartesischen Koordinaten von oben nach unten zu isometrischen Koordinaten. Das Bild unten zeigt die visuelle Transformation:

Side by side view of top down and isometric gridsSide by side view of top down and isometric gridsSide by side view of top down and isometric grids

Platzieren von isometrischen Kacheln

Lassen Sie mich versuchen, die Beziehung zwischen als 2D-Array gespeicherten Ebenendaten und der isometrischen Ansicht zu vereinfachen - das heißt, wie wir kartesische Koordinaten in isometrische Koordinaten umwandeln. Wir werden versuchen, die isometrische Ansicht für unser jetzt berühmtes ummauertes Grasland zu erstellen. Die Implementierung der Ebene in der 2D-Ansicht war eine einfache Iteration mit zwei Schleifen, bei der quadratische Kacheln platziert wurden, die jeweils mit den Werten für feste Kachelhöhe und Kachelbreite versetzt waren. Für die isometrische Ansicht bleibt der Pseudocode gleich, aber die Funktion placeTile() ändert sich.

Die ursprüngliche Funktion zeichnet nur die Kachelbilder an den angegebenen Koordinaten x und y, aber für eine isometrische Ansicht müssen wir die entsprechenden isometrischen Koordinaten berechnen. Die Gleichungen, um dies zu tun, sind wie folgt, wobei isoX und isoY isometrische x- und y-Koordinaten darstellen und cartX und cartY kartesische x- und y-Koordinaten darstellen:

Ja das ist es. Diese einfachen Gleichungen sind die Magie hinter der isometrischen Projektion. Hier sind die Phaser-Hilfsfunktionen, mit denen Sie mithilfe der sehr praktischen Point-Klasse von einem System in ein anderes konvertieren können:

Daher können wir die cartesianToIsometric-Hilfsmethode verwenden, um die eingehenden 2D-Koordinaten in isometrische Koordinaten innerhalb der placeTile-Methode zu konvertieren. Abgesehen davon bleibt der Rendering-Code derselbe, aber wir benötigen neue Bilder für die Kacheln. Wir können die alten quadratischen Kacheln für unser Top-Down-Rendering nicht verwenden. Das Bild unten zeigt die neuen isometrischen Gras- und Wandfliesen zusammen mit der gerenderten isometrischen Ebene:

Isometric level walled grassland along with the isometric tiles usedIsometric level walled grassland along with the isometric tiles usedIsometric level walled grassland along with the isometric tiles used

Unglaublich, nicht wahr? Mal sehen, wie eine typische 2D-Position in eine isometrische Position umgewandelt wird:

In ähnlicher Weise führt eine Eingabe von [0, 0] zu [0, 0] und [10, 5] zu [5, 7.5].

Für unser ummauertes Grasland können wir eine begehbare Fläche bestimmen, indem wir prüfen, ob das Array-Element an dieser Koordinate 0 ist, wodurch Gras angezeigt wird. Dazu müssen wir die Array-Koordinaten bestimmen. Mit dieser Funktion können wir die Koordinaten der Kachel in den Ebenendaten aus ihren kartesischen Koordinaten ermitteln:

(Hier nehmen wir im Wesentlichen an, dass Fliesenhöhe und Fliesenbreite wie in den meisten Fällen gleich sind.)

Daher können wir aus einem Paar von Bildschirmkoordinaten (isometrischen Koordinaten) Kachelkoordinaten finden, indem wir Folgendes aufrufen:

Dieser Bildschirmpunkt kann beispielsweise eine Mausklickposition oder eine Aufnahmeposition sein.

Registrierungspunkte

In Flash können wir beliebige Punkte für eine Grafik als Mittelpunkt oder [0,0] festlegen. Das Phaser-Äquivalent ist Pivot. Wenn Sie die Grafik auf [10,20] platzieren, wird dieser Pivot-Punkt mit [10,20] ausgerichtet. Standardmäßig wird die obere linke Ecke einer Grafik als [0,0] oder Pivot betrachtet. Wenn Sie versuchen, die oben genannte Ebene mit dem bereitgestellten Code zu erstellen, wird das angezeigte Ergebnis nicht angezeigt. Stattdessen erhalten Sie ein flaches Land ohne Mauern, wie unten:

The issue with wall tiles when rendered normallyThe issue with wall tiles when rendered normallyThe issue with wall tiles when rendered normally

Dies liegt daran, dass die Kachelbilder unterschiedliche Größen haben und wir das Höhenattribut der Wandkachel nicht ansprechen. Das folgende Bild zeigt die verschiedenen Kachelbilder, die wir mit ihren Begrenzungsrahmen und einem weißen Kreis verwenden, wobei ihre Standardeinstellung [0,0] lautet:

How to properly align the different tiles along with their registration pointsHow to properly align the different tiles along with their registration pointsHow to properly align the different tiles along with their registration points

Sehen Sie, wie der Held beim Zeichnen mit den Standarddrehpunkten falsch ausgerichtet wird. Beachten Sie auch, wie wir die Höhe der Wandfliese verlieren, wenn wir mit Standardzapfen zeichnen. Das Bild rechts zeigt, wie sie richtig ausgerichtet werden müssen, damit die Wandfliese ihre Höhe erreicht und der Held in die Mitte der Grasfliese gelegt wird. Dieses Problem kann auf verschiedene Arten gelöst werden.

  1. Erstellen Sie alle Kacheln in derselben Bildgröße, wobei die Grafik im Bild richtig ausgerichtet ist. Dadurch werden viele leere Bereiche in jeder Kachelgrafik erstellt.
  2. Stellen Sie die Drehpunkte für jede Kachel manuell so ein, dass sie richtig ausgerichtet sind.
  3. Zeichnen Sie Kacheln mit bestimmten Versätzen, damit sie richtig ausgerichtet sind.

Für dieses Tutorial habe ich mich für die dritte Methode entschieden, damit dies auch mit einem Framework funktioniert, bei dem keine Drehpunkte festgelegt werden können.

3. Verschieben isometrischer Koordinaten

Wir werden niemals versuchen, unseren Charakter oder unser Projektil direkt in isometrischen Koordinaten zu bewegen. Stattdessen werden wir unsere Spielweltdaten in kartesischen Koordinaten bearbeiten und einfach die oben genannten Funktionen verwenden, um die auf dem Bildschirm angezeigten zu aktualisieren. Wenn Sie beispielsweise ein Zeichen in der positiven y-Richtung vorwärts bewegen möchten, können Sie seine y-Eigenschaft einfach in 2D-Koordinaten erhöhen und dann die resultierende Position in isometrische Koordinaten konvertieren:

Dies wird ein guter Zeitpunkt sein, um alle neuen Konzepte, die wir bisher gelernt haben, zu überprüfen und ein funktionierendes Beispiel für etwas zu erstellen, das sich in einer isometrischen Welt bewegt. Sie finden die erforderlichen Image-Assets im assets-Ordner des Quell-Git-Repositorys.

Tiefensortierung

Wenn Sie versuchen würden, das Ballbild in unserem ummauerten Garten zu verschieben, würden Sie auf die Probleme bei der Tiefensortierung stoßen. Zusätzlich zur normalen Platzierung müssen wir uns um die Tiefensortierung kümmern, um die isometrische Welt zu zeichnen, wenn sich bewegliche Elemente befinden. Durch die richtige Tiefensortierung wird sichergestellt, dass Elemente, die näher am Bildschirm liegen, über weiter entfernte Elemente gezeichnet werden.

Die einfachste Tiefensortierungsmethode besteht darin, einfach den kartesischen y-Koordinatenwert zu verwenden, wie in diesem Quick-Tipp erwähnt: Je weiter oben auf dem Bildschirm das Objekt ist, desto früher sollte es gezeichnet werden. Dies mag für sehr einfache isometrische Szenen gut funktionieren, aber ein besserer Weg ist es, die isometrische Szene neu zu zeichnen, sobald eine Bewegung stattfindet, entsprechend den Array-Koordinaten der Kachel. Lassen Sie mich dieses Konzept mit unserem Pseudocode für das Zeichnen von Ebenen ausführlich erläutern:

Stellen Sie sich vor, unser Gegenstand oder Charakter befindet sich auf der Kachel [1,1] - das ist die oberste grüne Kachel in der isometrischen Ansicht. Um das Level richtig zu zeichnen, muss der Charakter nach dem Zeichnen der Eckwandkachel, sowohl der linken als auch der rechten Wandkachel und der Bodenkachel wie folgt gezeichnet werden:

Hero standing on the corner tileHero standing on the corner tileHero standing on the corner tile

Wenn wir unserer Zeichenschleife gemäß dem obigen Pseudocode folgen, zeichnen wir zuerst die mittlere Eckwand und zeichnen dann weiterhin alle Wände im oberen rechten Bereich, bis sie die rechte Ecke erreichen.

In der nächsten Schleife wird dann die Wand links vom Charakter und dann die Grasplatte, auf der der Charakter steht, gezeichnet. Sobald wir festgestellt haben, dass dies das Plättchen ist, das unseren Charakter einnimmt, werden wir den Charakter nach dem Zeichnen des Grasplättchens zeichnen. Auf diese Weise überlappen sich die Wände des Charakters, wenn sich auf den drei freien Grasplättchen Wände befinden, die mit demjenigen verbunden sind, auf dem der Charakter steht, was zu einer korrekten tiefensortierten Darstellung führt.

4. Erstellen der Kunst

Isometrische Kunst kann Pixelkunst sein, muss es aber nicht sein. Wenn Sie sich mit isometrischer Pixelkunst befassen, finden Sie in RhysDs Handbuch fast alles, was Sie wissen müssen. Einige Theorien finden Sie auch auf Wikipedia.

Bei der Erstellung isometrischer Kunst gelten folgende allgemeine Regeln:

  • Beginnen Sie mit einem leeren isometrischen Raster und halten Sie sich an die pixelgenaue Präzision.
  • Versuchen Sie, Kunst in einzelne isometrische Kachelbilder zu zerlegen.
  • Stellen Sie sicher, dass jede Fliese begehbar oder nicht begehbar ist. Es wird kompliziert, wenn wir eine einzelne Fliese unterbringen müssen, die sowohl begehbare als auch nicht begehbare Bereiche enthält.
  • Die meisten Kacheln müssen nahtlos in eine oder mehrere Richtungen gekachelt werden.
  • Die Implementierung von Schatten kann schwierig sein, es sei denn, wir verwenden einen mehrschichtigen Ansatz, bei dem wir Schatten auf die Grundebene und dann den Helden (oder Bäume oder andere Objekte) auf die oberste Ebene zeichnen. Wenn der Ansatz, den Sie verwenden, nicht vielschichtig ist, stellen Sie sicher, dass die Schatten nach vorne fallen, damit sie nicht beispielsweise auf den Helden fallen, wenn er hinter einem Baum steht.
  • Wenn Sie ein Kachelbild verwenden müssen, das größer als die isometrische Standardkachelgröße ist, versuchen Sie, eine Bemaßung zu verwenden, die ein Vielfaches der Iso-Kachelgröße beträgt. In solchen Fällen ist es besser, einen mehrschichtigen Ansatz zu verfolgen, bei dem wir die Kunst je nach Höhe in verschiedene Teile aufteilen können. Zum Beispiel kann ein Baum in drei Teile geteilt werden: die Wurzel, den Stamm und das Laub. Dies erleichtert das Sortieren von Tiefen, da wir Teile in entsprechenden Ebenen zeichnen können, die ihren Höhen entsprechen.

Isometrische Kacheln, die größer als die einzelnen Kachelabmessungen sind, verursachen Probleme bei der Tiefensortierung. Einige der Probleme werden unter folgenden Links behandelt:

Zusammenhängende Posts

5. Isometrische Zeichen

Zuerst müssen wir festlegen, wie viele Bewegungsrichtungen in unserem Spiel zulässig sind. Normalerweise bieten Spiele eine Vier-Wege-Bewegung oder eine Acht-Wege-Bewegung. Schauen Sie sich das Bild unten an, um die Korrelation zwischen dem 2D-Raum und dem isometrischen Raum zu verstehen:

The directions of motion in top view and isometric viewThe directions of motion in top view and isometric viewThe directions of motion in top view and isometric view

Bitte beachten Sie, dass sich ein Charakter vertikal nach oben bewegt, wenn wir in einem Top-Down-Spiel die Aufwärtspfeiltaste drücken. Bei einem isometrischen Spiel bewegt sich der Charakter jedoch in einem 45-Grad-Winkel in Richtung der oberen rechten Ecke.

Für eine Ansicht von oben nach unten könnten wir einen Satz von Charakteranimationen erstellen, die in eine Richtung zeigen, und sie einfach für alle anderen drehen. Für die isometrische Zeichenkunst müssen wir jede Animation in jeder der zulässigen Richtungen neu rendern. Für eine Acht-Wege-Bewegung müssen wir also für jede Aktion acht Animationen erstellen.

Zum besseren Verständnis bezeichnen wir die Richtungen normalerweise als Nord, Nordwest, West, Südwest, Süd, Südost, Ost und Nordost. Die folgenden Zeichenrahmen zeigen Leerlaufrahmen ab Südosten im Uhrzeigersinn:

The different frames of the character facing the different directionsThe different frames of the character facing the different directionsThe different frames of the character facing the different directions

Wir werden Zeichen auf die gleiche Weise platzieren, wie wir Kacheln platziert haben. Die Bewegung eines Zeichens wird erreicht, indem die Bewegung in kartesischen Koordinaten berechnet und dann in isometrische Koordinaten konvertiert wird. Nehmen wir an, wir verwenden die Tastatur, um das Zeichen zu steuern.

Wir werden zwei Variablen einstellen, dX und dY, basierend auf den gedrückten Richtungstasten. Standardmäßig sind diese Variablen 0 und werden gemäß der folgenden Tabelle aktualisiert, wobei U, D, R und L die Aufwärts-, Abwärts-, Rechts- und Linkspfeiltasten bezeichnen. Ein Wert von 1 unter einer Taste bedeutet, dass die Taste gedrückt wird. 0 bedeutet, dass die Taste nicht gedrückt wird.

Mit den Werten von dX und dY können wir nun die kartesischen Koordinaten wie folgt aktualisieren:

dX und dY stehen also für die Änderung der x- und y-Position des Zeichens basierend auf den gedrückten Tasten. Wir können die neuen isometrischen Koordinaten leicht berechnen, wie wir bereits besprochen haben:

Sobald wir die neue isometrische Position haben, müssen wir den Charakter an diese Position verschieben. Basierend auf den Werten, die wir für dX und dY haben, können wir entscheiden, in welche Richtung das Zeichen zeigt, und die entsprechende Zeichengrafik verwenden. Vergessen Sie nach dem Verschieben des Charakters nicht, das Level mit der richtigen Tiefensortierung neu zu streichen, da sich die Kachelkoordinaten des Charakters möglicherweise geändert haben.

Kollisionserkennung

Die Kollisionserkennung erfolgt durch Überprüfen, ob die Kachel an der neu berechneten Position eine nicht begehbare Kachel ist. Sobald wir die neue Position gefunden haben, verschieben wir den Charakter nicht sofort dorthin, sondern überprüfen zuerst, welche Kachel diesen Platz einnimmt.

In der Funktion isWalkable() prüfen wir, ob der Wert des Level-Datenarrays an der angegebenen Koordinate eine begehbare Kachel ist oder nicht. Wir müssen darauf achten, die Richtung zu aktualisieren, in die der Charakter blickt - auch wenn er sich nicht bewegt, wie wenn er auf ein nicht begehbares Plättchen trifft.

Dies mag nach einer geeigneten Lösung klingen, funktioniert jedoch nur für Elemente ohne Lautstärke. Dies liegt daran, dass wir nur einen einzelnen Punkt betrachten, der der Mittelpunkt des Zeichens ist, um die Kollision zu berechnen. Was wir wirklich tun müssen, ist, alle vier Ecken des Zeichens aus seiner verfügbaren 2D-Mittelpunktskoordinate zu finden und Kollisionen für all diese zu berechnen. Wenn eine Ecke in ein nicht begehbares Plättchen fällt, sollten wir den Charakter nicht bewegen.

Tiefensortierung mit Zeichen

Stellen Sie sich ein Zeichen und eine Baumkachel in der isometrischen Welt vor, und beide haben die gleichen Bildgrößen, auch wenn dies unrealistisch klingt.

Um die Tiefensortierung richtig zu verstehen, müssen wir verstehen, dass der Baum das Zeichen immer dann überlappt, wenn die x- und y-Koordinaten des Zeichens kleiner als die des Baums sind. Immer wenn die x- und y-Koordinaten des Zeichens größer als die des Baums sind, überlappt das Zeichen den Baum. Wenn sie die gleiche x-Koordinate haben, entscheiden wir allein anhand der y-Koordinate: Je nachdem, welche höhere y-Koordinate vorliegt, überlappt sie die andere. Wenn sie die gleiche y-Koordinate haben, entscheiden wir allein anhand der x-Koordinate: Je nachdem, welche höhere x-Koordinate vorliegt, überlappt sie die andere.

Wie bereits erläutert, besteht eine vereinfachte Version darin, die Ebenen ausgehend von der am weitesten entfernten tile[0][0], nacheinander zu zeichnen und dann alle Kacheln in jeder Reihe nacheinander zu zeichnen. Wenn ein Charakter ein Plättchen belegt, zeichnen wir zuerst das Bodenplättchen und rendern dann das Charakterplättchen. Dies funktioniert gut, da der Charakter keine Wandkachel belegen kann.

6. Demo-Zeit!

Dies ist eine Demo in Phaser. Klicken Sie, um sich auf den interaktiven Bereich zu konzentrieren, und bewegen Sie den Charakter mit den Pfeiltasten. Sie können zwei Pfeiltasten verwenden, um sich in diagonale Richtungen zu bewegen.

Die vollständige Quelle für die Demo finden Sie im Quell-Repository für dieses Tutorial. Der Haupt-JS-Code wird unten bereitgestellt:

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.