7 days of WordPress themes, graphics & videos - for free!* Unlimited asset downloads! Start 7-Day Free Trial
Advertisement
  1. Game Development
  2. Programming

Erstellen isometrischer Welten: Eine Einführung für Spieleentwickler, Fortsetzung

Scroll to top
Read Time: 29 mins

German (Deutsch) translation by Alex Grigorovich (you can also view the original English article)

In diesem Tutorial bauen wir auf der ursprünglichen Einführung in die Erstellung isometrischer Welten auf und lernen die Implementierung von Pickups, Trigger-Kacheln, Level-Swapping, Pfadfindung und -verfolgung, Level-Scrolling, isometrische Höhe und isometrische Projektile kennen.


1. Pickups

Pickups sind Gegenstände, die innerhalb des Levels gesammelt werden können, normalerweise indem man einfach darüber geht - zum Beispiel Münzen, Edelsteine, Bargeld und Munition.

Pickup-Daten können auch direkt in unsere Level-Daten aufgenommen werden:

In diesen Leveldaten verwenden wir 8 zur Bezeichnung eines Pickups (1 und 0 stehen wie zuvor für Wände bzw. begehbare Kacheln).

Es ist wichtig zu verstehen, dass 8 eigentlich zwei Kacheln bezeichnet, nicht nur eine: Es bedeutet, dass wir zuerst eine begehbare Graskachel platzieren und dann einen Pickup darauf platzieren müssen. Das bedeutet, dass sich jeder Pickup immer auf einem Grasplättchen befindet. Wenn es auf einem begehbaren Ziegelstein liegen soll, brauchen wir einen weiteren Ziegel, der mit einer anderen Zahl gekennzeichnet ist, z. B. 9, die für "Pickup auf Ziegelstein" steht.

Typische isometrische Kunst hat mehrere begehbare Kacheln - nehmen wir an, wir haben 30. Der obige Ansatz bedeutet, dass wir, wenn wir N Tonabnehmer haben, zusätzlich zu den 30 Originalkacheln (N * 30) Kacheln benötigen, da jede Kachel eine Version haben muss mit Pickups und einer ohne. Dies ist nicht sehr effizient; Stattdessen sollten wir versuchen, diese Kombinationen dynamisch zu erstellen.

Dazu können wir ein anderes Array allein mit den Pickupdaten verwenden und dieses verwenden, um Aufnahmekacheln über den Ebenenlayoutdaten zu platzieren:

...Plus:

...ergibt:

pickups in levelpickups in levelpickups in level

Dieser Ansatz stellt sicher, dass wir zusätzlich zu N Pickup-Kacheln nur die 30 Originalkacheln benötigen, da wir jede Kombination erstellen können, indem wir beide Kunstwerke beim Rendern des Levels mischen.

Abholung von Pickups

Das Erkennen von Pickups erfolgt auf die gleiche Weise wie das Erkennen von Kollisionskacheln, jedoch nach dem Bewegen des Charakters.

In der Funktion isPickup(tile coordinate) prüfen wir, ob der Wert des Pickup-Datenarrays an der angegebenen Koordinate eine Pickup-Kachel ist oder nicht. Die Zahl im Pickup-Array an dieser Kachelkoordinate gibt den Pickup-Typ an.

Wir prüfen auf Kollisionen, bevor wir den Charakter bewegen, prüfen aber danach auf Pickups, denn bei Kollisionen sollte der Charakter den Platz nicht besetzen, wenn er bereits von der Kollisionsplättchen besetzt ist, aber bei Pickups kann sich der Charakter frei darüber bewegen .

Zu beachten ist auch, dass sich die Kollisionsdaten normalerweise nie ändern, aber die Pickupldaten ändern sich, wenn wir einen Artikel abholen. (Dies beinhaltet normalerweise nur das Ändern des Werts im Pickup-Array von beispielsweise 8 auf 0.)

Dies führt zu einem Problem: Was passiert, wenn wir das Level neu starten müssen und damit alle Pickups auf ihre ursprünglichen Positionen zurücksetzen? Wir haben nicht die Informationen dazu, da das Pickup-Array geändert wurde, wenn der Spieler Gegenstände aufhob. Die Lösung besteht darin, während des Spiels ein doppeltes Array für Tonabnehmer zu verwenden und das ursprüngliche Pickup-Array intakt zu halten. Beispielsweise verwenden wir pickupsArray[] und pickupsLive[], klonen das letztere zu Beginn des Levels von dem ersteren und nur während des Spiels pickupsLive[] ändern.

Klicken Sie, um den SWF-Fokus festzulegen, und verwenden Sie dann die Pfeiltasten. Klicken Sie hier für die Vollversion.

Sie sollten beachten, dass wir immer dann nach Pickups suchen, wenn sich der Charakter auf dieser Kachel befindet. Dies kann innerhalb einer Sekunde mehrmals vorkommen (wir prüfen nur, wann sich der Benutzer bewegt, aber wir können innerhalb einer Kachel herumlaufen), aber die obige Logik schlägt nicht fehl. Da wir die Daten des Pickup-Arrays beim ersten Erkennen eines Pickups auf 0 setzen, geben alle nachfolgenden isPickup(tile)-Prüfungen für diese Tile false zurück.


2. Kacheln auslösen

Wie der Name schon sagt, verursachen Trigger-Kacheln etwas, wenn der Spieler darauf tritt oder eine Taste drückt, während er darauf tritt. Sie können den Spieler an einen anderen Ort teleportieren, ein Tor öffnen oder einen Feind hervorbringen, um nur einige Beispiele zu nennen. Pickups sind gewissermaßen nur eine Sonderform von Triggerplättchen: Wenn der Spieler auf ein Plättchen mit einer Münze tritt, verschwindet die Münze und sein Münzzähler erhöht sich.

Schauen wir uns an, wie wir eine Tür implementieren könnten, die den Spieler auf ein anderes Level bringt. Das Plättchen neben der Tür ist ein Triggerplättchen; Wenn der Spieler die Leertaste drückt, geht er zum nächsten Level.

hero at doorhero at doorhero at door

Um die Level zu ändern, müssen wir nur das aktuelle Level-Datenarray mit dem des neuen Levels austauschen und die neue Kachelposition und -richtung für den Heldencharakter festlegen.

Angenommen, es gibt zwei Ebenen mit Türen, die den Durchgang ermöglichen. Da das Bodenplättchen neben der Tür in beiden Levels das Auslöserplättchen ist, können wir dies als neue Position für den Charakter verwenden, wenn er im Level erscheint.

Die Implementierungslogik ist hier dieselbe wie bei Pickups, und wieder verwenden wir ein Array, um Triggerwerte zu speichern. Dies ist ineffizient und Sie sollten zu diesem Zweck andere Datenstrukturen in Betracht ziehen, aber lassen Sie uns dies für das Tutorial einfach halten. Lassen Sie die neuen Level-Arrays wie folgt aussehen (7 bezeichnet eine Tür):

Lassen Sie die Levels einige Pickups haben, wie in den folgenden Pickup-Arrays beschrieben:

Lassen Sie die entsprechenden Trigger-Kachel-Arrays für jede Ebene wie folgt aussehen:

Die Werte (1 und 2) bezeichnen den Level, der geladen wird, wenn der Spieler die Leertaste drückt.

Hier ist der Code, der ausgeführt wird, wenn der Spieler diese Taste drückt:

Die Funktion isTrigger() prüft, ob der Triggerdaten-Array-Wert an der angegebenen Koordinate größer als Null ist. Wenn ja, übergibt unser Code diesen Wert an doRelevantAction(), das entscheidet, welche Funktion als nächstes aufgerufen wird. Für unsere Zwecke verwenden wir die einfache Regel, dass es sich um eine Tür handelt, wenn der Wert zwischen 1 und 10 liegt, und daher wird diese Funktion aufgerufen:

Da der Wert der Kachel im Trigger-Array auch den zu ladenden Level angibt, können wir ihn einfach an swapLevel() übergeben. Dies impliziert wiederum, dass unser Spiel zehn Level hat.

Hier ist eine funktionierende Demo. Versuchen Sie, Gegenstände aufzuheben, indem Sie über sie gehen und die Ebenen wechseln, indem Sie neben Türen stehen und die Leertaste drücken.

Klicken Sie auf den SWF-Fokus, und verwenden Sie dann die Pfeiltasten. Klicken Sie hier für die Vollversion.

Ich habe den Auslöser aktiviert, wenn Space freigegeben wird; Wenn wir nur auf das Drücken der Taste warten, landen wir in einer Schleife, in der wir zwischen den Ebenen wechseln, solange die Taste gedrückt gehalten wird, da der Charakter immer in der neuen Ebene auf einem Triggerplättchen erscheint.

Hier ist der vollständige Code (in AS3):


3. Pfadfindung

Pfadfindung und Pfadverfolgung ist ein ziemlich komplizierter Prozess. Es gibt verschiedene Ansätze, die verschiedene Algorithmen verwenden, um den Weg zwischen zwei Punkten zu finden, aber unsere Level-Daten sind ein 2D-Array. Die Dinge sind einfacher als sie es sonst sein könnten - wir haben gut definierte und einzigartige Knoten, die der Spieler besetzen kann, und wir können leicht überprüfen, ob Sie sind begehbar.

Ein detaillierter Überblick über Pfadfindungsalgorithmen würde den Rahmen dieses Artikels sprengen, aber ich werde versuchen, die gebräuchlichste Funktionsweise zu erklären: den kürzesten Pfad-Algorithmus, von dem A* und Dijkstras Algorithmen berühmte Implementierungen sind.

Unser Ziel ist es, Knoten zu finden, die einen Startknoten und einen Endknoten verbinden. Vom Startknoten aus besuchen wir alle acht benachbarten Knoten und markieren sie alle als besucht; Dieser Kernprozess wird für jeden neu besuchten Knoten rekursiv wiederholt. Jeder Thread verfolgt die besuchten Knoten. Beim Springen zu benachbarten Knoten werden bereits besuchte Knoten übersprungen (die Rekursion stoppt); andernfalls wird der Prozess fortgesetzt, bis wir den Endknoten erreichen, wo die Rekursion endet und der vollständige gefolgte Pfad als Knotenarray zurückgegeben wird. Manchmal wird der Endknoten nie erreicht, in diesem Fall schlägt die Pfadfindung fehl. Normalerweise finden wir mehrere Pfade zwischen den beiden Knoten. In diesem Fall nehmen wir den mit der geringsten Anzahl von Knoten.

Es gibt viele Standardlösungen für die Pfadfindung basierend auf 2D-Arrays. Daher überspringen wir die Neuerfindung dieses Rads. Lassen Sie uns diese AS3-Lösung verwenden (ich empfehle Ihnen, diese großartige erklärende Demo zu lesen)).

Die Lösung gibt ein Array mit Punkten zurück, die den Pfad vom Startpunkt zum Endpunkt bilden:

Pfad folgend

Sobald wir den Pfad als Node-Array haben, müssen wir das Zeichen dazu bringen, ihm zu folgen.

Angenommen, wir möchten, dass der Charakter zu einer Kachel geht, auf die wir klicken. Wir müssen zuerst nach einem Pfad zwischen dem Knoten suchen, den das Zeichen derzeit belegt, und dem Knoten, auf den wir geklickt haben. Wenn ein erfolgreicher Pfad gefunden wird, müssen wir das Zeichen auf den ersten Knoten im Knotenarray verschieben, indem wir als Ziel festlegen. Sobald wir den Zielknoten erreicht haben, prüfen wir, wo sich noch weitere Knoten im Knotenarray befinden und setzen, wenn ja, den nächsten Knoten als Ziel - und so weiter, bis wir den letzten Knoten erreichen.

Wir werden auch die Richtung des Spielers basierend auf dem aktuellen Knoten und dem neuen Zielknoten jedes Mal ändern, wenn wir einen Knoten erreichen. Zwischen den Knoten gehen wir einfach in die gewünschte Richtung, bis wir den Zielknoten erreichen. Dies ist eine sehr einfache KI.

Sehen Sie sich dieses Arbeitsbeispiel an:

Klicken Sie, um das Zeichen zu verschieben. Klicken Sie hier für die Vollversion.

Hier ist die vollständige Quelle:

Sie haben vielleicht bemerkt, dass ich die Kollisionsprüfungslogik entfernt habe; es wird nicht mehr benötigt, da wir unseren Charakter nicht manuell mit der Tastatur bewegen können. Wir müssen jedoch gültige Klickpunkte herausfiltern, indem wir feststellen, ob wir innerhalb des begehbaren Bereichs geklickt haben und nicht in einer Wandfliese oder einer anderen nicht begehbaren Fliese.

Ein weiterer interessanter Punkt für die Codierung der KI: Wir möchten nicht, dass sich der Charakter zum nächsten Plättchen im Node-Array umdreht, sobald er im aktuellen angekommen ist, da ein solcher sofortiger Zug dazu führt, dass unser Charakter an den Grenzen von Fliesen. Stattdessen sollten wir warten, bis sich der Charakter einige Schritte innerhalb der Kachel befindet, bevor wir nach dem nächsten Ziel suchen. Es ist auch besser, den Helden kurz vor dem Drehen manuell in die Mitte des aktuellen Plättchens zu platzieren, damit sich alles perfekt anfühlt.

Wenn Sie sich die obige Demo ansehen, werden Sie möglicherweise feststellen, dass unsere Zeichenlogik unterbrochen wird, wenn sich der Held diagonal in die Nähe einer Wandfliese bewegt. Dies ist ein Extremfall, bei dem unser Held für einen Rahmen in der Wandfliese zu sein scheint. Dies geschieht, weil wir die Kollisionsprüfung deaktiviert haben. Eine Problemumgehung besteht darin, einen Pfadfindungsalgorithmus zu verwenden, der die diagonalen Lösungen ignoriert. (Fast alle Pfadfindungsalgorithmen haben Optionen zum Aktivieren oder Deaktivieren von Diagonallauflösungen.)


4. Projektile

Ein Projektil ist etwas, das sich mit einer bestimmten Geschwindigkeit in eine bestimmte Richtung bewegt, wie eine Kugel, ein Zauberspruch, eine Kugel usw.

Alles an dem Projektil ist identisch mit dem Heldencharakter, abgesehen von der Höhe: Anstatt über den Boden zu rollen, schweben Projektile oft in einer bestimmten Höhe darüber. Eine Kugel bewegt sich über die Taillenhöhe des Charakters, und möglicherweise muss sogar ein Ball herumspringen.

Interessant ist, dass die isometrische Höhe der Höhe in einer 2D-Seitenansicht entspricht. Es sind keine komplizierten Konvertierungen erforderlich. Wenn sich ein Ball in kartesischen Koordinaten 10 Pixel über dem Boden befindet, befindet er sich in isometrischen Koordinaten 10 Pixel über dem Boden. (In unserem Fall ist die relevante Achse die y-Achse.)

Versuchen wir, einen Ball zu implementieren, der in unserem ummauerten Grasland herumhüpft. Wir ignorieren Dämpfungseffekte (und lassen das Hüpfen endlos weiterlaufen) und für einen Hauch von Realismus fügen wir dem Ball einen Schatten hinzu. Wir bewegen den Schatten genauso wie wir den Heldencharakter (d. h. ohne einen Höhenwert zu verwenden), aber für den Ball müssen wir den Höhenwert zum isometrischen Y-Wert addieren. Der Höhenwert ändert sich von Frame zu Frame je nach Schwerkraft, und sobald der Ball den Boden berührt, drehen wir die aktuelle Geschwindigkeit entlang der y-Achse.

Bevor wir uns mit dem Bouncen in einem isometrischen System befassen, werden wir sehen, wie wir es in einem kartesischen 2D-System implementieren können. Lassen Sie uns die Höhe des Balls durch eine Variable zValue darstellen. Stellen Sie sich vor, der Ball ist zunächst zehn Pixel hoch, also zValue = 10. Wir verwenden zwei weitere Variablen: incrementValue, das bei 0 beginnt, und die gravity, die den Wert 1 hat.

In jedem Frame addieren wir incrementValue zu zValue und subtrahieren gravity von IncrementValue. Wenn zValue 0 erreicht, bedeutet dies, dass der Ball den Boden erreicht hat; An dieser Stelle kehren wir das Vorzeichen von IncrementValue um, indem wir es mit -1 multiplizieren und in eine positive Zahl verwandeln. Dies bedeutet, dass sich der Ball ab dem nächsten Frame nach oben bewegt und somit abprallt.

So sieht das im Code aus:

Wir werden tatsächlich eine leicht modifizierte Version davon verwenden:

Dies entfernt den Dämpfungseffekt und lässt den Ball für immer hüpfen.

Wenden wir dies auf unseren Ball an, erhalten wir die folgende Demo:

Klicken Sie auf den SWF-Fokus, und verwenden Sie dann die Pfeiltasten. Klicken Sie hier für die Vollversion.

Hier ist der vollständige AS3-Code:

Verstehen Sie, dass die Rolle, die der Schatten spielt, eine sehr wichtige ist, die zum Realismus dieser Illusion beiträgt. Im obigen Beispiel habe ich die halbe Ballhöhe zur Y-Position des Balls addiert, damit er in Bezug auf den Schatten an der richtigen Position springt.

Beachten Sie außerdem, dass wir jetzt die beiden Bildschirmkoordinaten (x und y) verwenden, um drei Dimensionen in isometrischen Koordinaten darzustellen. Die y-Achse in Bildschirmkoordinaten ist auch die z-Achse in isometrischen Koordinaten. Das kann verwirrend sein!


5. Isometrisches Scrollen

Wenn der Ebenenbereich viel größer als der sichtbare Bildschirmbereich ist, müssen wir ihn scrollen lassen.

Der sichtbare Bildschirmbereich kann als kleineres Rechteck innerhalb des größeren Rechtecks des gesamten Levelbereichs betrachtet werden. Scrollen ist im Wesentlichen nur das Verschieben des inneren Rechtecks in das größere:

Scrolling StartScrolling StartScrolling Start

Normalerweise bleibt die Position des Spielers in Bezug auf das Bildschirmrechteck, üblicherweise in der Bildschirmmitte, gleich, wenn ein solches Scrollen stattfindet. Um das Scrollen zu implementieren, müssen wir lediglich den Eckpunkt des inneren Rechtecks verfolgen:

Scrolling NextScrolling NextScrolling Next

Dieser Eckpunkt, der in kartesischen Koordinaten angegeben ist (im Bild können wir nur die isometrischen Werte zeigen), fällt in eine Kachel in den Höhendaten. Zum Scrollen inkrementieren wir die x- und y-Position des Eckpunktes in kartesischen Koordinaten. Jetzt können wir diesen Punkt in isometrische Koordinaten umwandeln und damit den Bildschirm zeichnen.

Die neu konvertierten Werte im isometrischen Raum müssen auch die Ecke unseres Bildschirms sein, was bedeutet, dass sie die neuen sind (0, 0). Beim Analysieren und Zeichnen der Ebenendaten ziehen wir diesen Wert von der isometrischen Position jeder Kachel ab und zeichnen ihn nur, wenn die neue Position der Kachel innerhalb des Bildschirms liegt. Wir können dies in Schritten wie folgt ausdrücken:

  • Aktualisieren Sie die x- und y-Koordinaten des kartesischen Eckpunkts.
  • Wandeln Sie dies in einen isometrischen Raum um.
  • Subtrahieren Sie diesen Wert von der isometrischen Zeichenposition jeder Kachel.
  • Zeichnen Sie die Kachel nur, wenn die neue isometrische Zeichenposition innerhalb des Bildschirms liegt.

Sehen Sie sich dieses Beispiel an (verwenden Sie die Pfeile zum Scrollen):

Klicken Sie, um den SWF-Fokus festzulegen, und verwenden Sie dann die Pfeiltasten. Klicken Sie hier für die Vollversion.

Hier ist der vollständige AS3-Quellcode:

Bitte beachten Sie, dass der Eckpunkt in die entgegengesetzte Richtung zur Positionsaktualisierung des Helden erhöht wird, während er sich bewegt. Dies stellt sicher, dass der Held in Bezug auf den Bildschirm dort bleibt, wo er ist:

Die Zeichenlogik ändert sich nur in zwei Zeilen, in denen wir die kartesischen Koordinaten jeder Kachel bestimmen. Wir übergeben einfach den Eckpunkt an den ursprünglichen Punkt, der tatsächlich die obigen Punkte 1, 2 und 3 kombiniert:

Ein paar Anmerkungen:

  • Während des Scrollens müssen wir möglicherweise zusätzliche Kacheln an den Bildschirmrändern zeichnen, oder wir sehen, dass Kacheln verschwinden und an den Bildschirmextremen erscheinen.
  • Wenn Sie Kacheln haben, die mehr als einen Platz einnehmen, müssen Sie mehr Kacheln an den Rändern ziehen. Wenn beispielsweise die größte Kachel im gesamten Set X mal Y misst, müssen Sie X weitere Kacheln nach links und rechts und Y weitere Kacheln nach oben und unten ziehen. Dadurch wird sichergestellt, dass die Ecken der größeren Kachel beim Scrollen in oder aus dem Bildschirm weiterhin sichtbar sind.
  • Wir müssen immer noch sicherstellen, dass wir keine leeren Bereiche auf dem Bildschirm haben, während wir in der Nähe der Grenzen des Levels zeichnen.
  • Der Level sollte nur so lange scrollen, bis die extremste Kachel am entsprechenden Bildschirmextrem gezeichnet wird - danach sollte sich der Charakter im Bildschirmraum weiter bewegen, ohne dass der Level scrollt. Dazu müssen wir alle vier Ecken des inneren Bildschirmrechtecks verfolgen und die Scroll- und Spielerbewegungslogik entsprechend drosseln. Sind Sie bereit für die Herausforderung, dies selbst umzusetzen?

Abschluss

Diese Serie richtet sich insbesondere an Einsteiger, die versuchen, isometrische Spielwelten zu erkunden. Viele der erläuterten Konzepte haben alternative Ansätze, die etwas komplizierter sind, und ich habe absichtlich die einfachsten ausgewählt. Sie erfüllen möglicherweise nicht alle Szenarien, denen Sie begegnen können, aber die gewonnenen Erkenntnisse können verwendet werden, um auf diesen Konzepten aufzubauen, um viel kompliziertere Lösungen zu erstellen.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.