Unity 2D kachelbasiertes isometrisches und hexagonales Sokoban-Spiel

Was Sie erstellen werden

In diesem Tutorial konvertieren wir ein herkömmliches Sokoban-Spiel auf 2D-Fliese in isometrische und hexagonale Ansichten. Wenn Sie mit isometrischen oder sechseckigen Spielen noch nicht vertraut sind, kann es zunächst überwältigend sein, beide gleichzeitig zu verfolgen. In diesem Fall empfehle ich, zuerst isometrisch zu wählen und später für die sechseckige Version zurückzukehren.

Wir werden auf dem früheren Unity-Tutorial aufbauen: Unity 2D Tile-Based Sokoban Game. Bitte gehen Sie zuerst das Tutorial durch, da der Code weitgehend unverändert bleibt und alle Kernkonzepte gleich bleiben. Ich werde auch auf andere Tutorials verweisen, in denen einige der zugrunde liegenden Konzepte erläutert werden.

Der wichtigste Aspekt beim Erstellen von isometrischen oder hexagonalen Versionen aus einer 2D-Version ist das Ermitteln der Positionierung der Elemente. Wir werden Umwandlungsmethoden verwenden, die auf Gleichungen basieren, um zwischen den verschiedenen Koordinatensystemen zu konvertieren.

Dieses Tutorial enthält zwei Abschnitte, einen für die isometrische Version und den anderen für die sechseckige Version.

1. Isometrisches Sokoban-Spiel

Lassen Sie uns direkt in die isometrische Version eintauchen, nachdem Sie das ursprüngliche Tutorial durchlaufen haben. Das Bild unten zeigt, wie die isometrische Version aussehen würde, vorausgesetzt, wir verwenden die gleichen Ebeneninformationen wie im ursprünglichen Tutorial.

Isometrische Ansicht

Isometrische Theorie, Umrechnungsgleichung und Implementierung werden in mehreren Tutorials zu Envato Tuts + erläutert. In diesem ausführlichen Tutorial finden Sie eine alte Flash-basierte Erklärung. Ich würde dieses Phaser-basierte Tutorial empfehlen, da es jünger und zukunftssicher ist.

Obwohl die in diesen Lernprogrammen verwendeten Skriptsprachen ActionScript 3 bzw. JavaScript sind, ist die Theorie unabhängig von Programmiersprachen überall anwendbar. Im Wesentlichen läuft es auf diese Umwandlungsgleichungen hinaus, die verwendet werden sollen, um kartesische 2D-Koordinaten in isometrische Koordinaten umzuwandeln oder umgekehrt.

// kartesisch zu isometrisch: isoX = cartX - cartY; isoY = (cartX + cartY) / 2; // Isometrisch zu Kartesisch: cartX = (2 * isoY + isoX) / 2; cartY = (2 * isoY - isoX) / 2;

Wir verwenden die folgende Unity-Funktion für die Konvertierung in isometrische Koordinaten.

Vector2 CartesianToIsometric (Vector2 cartPt) Vector2 tempPt = new Vector2 (); tempPt.x = cartPt.x-cartPt.y; tempPt.y = (cartPt.x + cartPt.y) / 2; return (tempPt); 

Änderungen in der Kunst

Wir werden die gleichen Ebeneninformationen verwenden, um unser 2D-Array zu erstellen, levelData, was die isometrische Darstellung antreiben wird. Der größte Teil des Codes bleibt auch anders als in der isometrischen Ansicht.

Der Stand der Technik muss jedoch einige Änderungen in Bezug auf die Drehpunkte haben. Bitte beachten Sie das Bild unten und die folgende Erklärung.

Das IsometricSokoban Spielskript verwendet modifizierte Sprites als heroSprite, ballSprite, und blockSprite. Das Bild zeigt die neuen Drehpunkte, die für diese Sprites verwendet werden. Diese Änderung verleiht dem Pseudo-3D-Look, den wir mit der isometrischen Ansicht anstreben. Das blockSprite ist ein neues Sprite, das wir hinzufügen, wenn wir ein invalidTile.

Es wird mir dabei helfen, den wichtigsten Aspekt der isometrischen Spiele, die Tiefensortierung, zu erklären. Obwohl das Sprite nur ein Sechseck ist, betrachten wir es als 3D-Würfel, bei dem sich der Drehpunkt in der Mitte der unteren Fläche des Würfels befindet.

Änderungen im Code

Laden Sie den gemeinsam genutzten Code über das verknüpfte git-Repository herunter, bevor Sie fortfahren. Das CreateLevel Die Methode hat einige Änderungen, die sich auf den Maßstab und die Positionierung der Kacheln und das Hinzufügen der Kacheln beziehen blockTile. Das Ausmaß der tileSprite, Dies ist nur ein rautenförmiges Bild, das unsere Bodenplatte darstellt, und muss wie folgt geändert werden.

tile.transform.localScale = new Vector2 (tileSize-1, (tileSize-1) / 2); // Die Größe ist für die isometrische Form entscheidend

Dies spiegelt die Tatsache wider, dass eine isometrische Kachel die Hälfte ihrer Breite hat. Das heroSprite und das ballSprite haben eine Größe von tileSize / 2.

hero.transform.localScale = Vector2.one * (tileSize / 2); // verwenden wir die Hälfte der Kachelgröße für Insassen

Wo auch immer wir eine finden invalidTile, wir fügen ein blockTile mit dem folgenden Code.

tile = new GameObject ("block" + i.ToString () + "_" + j.ToString ()); // Neue Fliese für Kacheln erstellen rootThree = Mathf.Sqrt (3); float newDimension = 2 * tileSize / rootThree; tile.transform.localScale = new Vector2 (newDimension, tileSize); // müssen einige Höhen gesetzt werden sr = tile.AddComponent(); // füge einen Sprite-Renderer hinzu sr.sprite = blockSprite; // ordne Block Sprite sr.sortingOrder = 1; // muss auch eine höhere Sortierreihenfolge haben Farbe c = Color.gray; c.a = 0,9f; sr.color = c; tile.transform.position = GetScreenPointFromLevelIndices (i, j); // Platz in der Szene basierend auf den Ebenenindizes besetzen.Add (Kachel, neuer Vector2 (i, j)); // die Ebenenindizes des Blocks in dict speichern

Das Sechseck muss unterschiedlich skaliert werden, um das isometrische Aussehen zu erhalten. Dies wird kein Problem sein, wenn die Kunst von Künstlern bearbeitet wird. Wir wenden einen etwas niedrigeren Alpha-Wert an blockSprite so dass wir durchschauen können, was uns ermöglicht, die Sortierung der Tiefe richtig zu erkennen. Beachten Sie, dass wir diese Kacheln zum hinzufügen Insassen auch Wörterbuch, das später für die Tiefensortierung verwendet wird.

Die Positionierung der Kacheln erfolgt mit GetScreenPointFromLevelIndices Methode, die wiederum die CartesianToIsometric Konvertierungsmethode, die zuvor erläutert wurde. Das Y Achse zeigt für Unity in die entgegengesetzte Richtung, was beim Hinzufügen von berücksichtigt werden muss middleOffset um die Ebene in der Mitte des Bildschirms zu positionieren.

Vector2 GetScreenPointFromLevelIndices (int row, int col) // Indizes in Positionswerte konvertieren, col bestimmt x und row bestimmen y Vector2 tempPt = CartesianToIsometric (neuer Vector2 (col * tileSize / 2, row * tileSize / 2)); // entfernt das '-' im y-Teil, da die Achsenkorrektur nach der Überdeckung tempPt.x- = middleOffset.x erfolgen kann: // Wir wenden den Versatz außerhalb der Koordinatenumwandlung an, um den Pegel in screen middle tempPt.y * = - 1 auszurichten. // Y-Achsenkorrektur tempPt.y + = middleOffset.y; // wir wenden den Versatz außerhalb der Koordinatenumrechnung an, um den Pegel in der mittleren Bildschirmausgabe tempPt auszurichten. 

Am Ende von CreateLevel Methode sowie am Ende der TryMoveHero Methode nennen wir die DepthSort Methode. Die Tiefensortierung ist der wichtigste Aspekt einer isometrischen Implementierung. Im Wesentlichen bestimmen wir, welche Kacheln hinter oder vor anderen Kacheln im Level liegen. Das DepthSort Methode ist wie unten gezeigt.

private void DepthSort () int depth = 1; SpriteRenderer sr; Vector2 pos = new Vector2 (); für (int i = 0; i < rows; i++)  for (int j = 0; j < cols; j++)  int val=levelData[i,j]; if(val!=groundTile && val!=destinationTile)//a tile which needs depth sorting pos.x=i; pos.y=j; GameObject occupant=GetOccupantAtPosition(pos);//find the occupant at this position if(occupant==null)Debug.Log("no occupant"); sr=occupant.GetComponent(); sr.sortingOrder = Tiefe; // Neue Tiefe zuweisen Tiefe ++; // Tiefe erhöhen

Das Schöne an einer 2D-Array-basierten Implementierung ist, dass wir für die richtige isometrische Tiefensortierung nur sequentiell höhere Tiefe zuweisen müssen, während wir die Ebene der Reihe nach durchlaufen und sequenziell für Schleifen verwenden. Dies funktioniert für unsere einfache Ebene mit nur einer einzigen Bodenschicht. Wenn wir mehrere Bodenebenen in verschiedenen Höhen hätten, könnte die Sortierung der Tiefe kompliziert werden.

Alles andere bleibt wie bei der im vorherigen Tutorial erläuterten 2D-Implementierung.

Level abgeschlossen

Sie können dieselbe Tastatursteuerung verwenden, um das Spiel zu spielen. Der einzige Unterschied ist, dass der Held sich nicht vertikal oder horizontal bewegt, sondern isometrisch. Das fertige Level würde wie das Bild unten aussehen.

Prüfen Sie mit unserer neuen, wie die Tiefensortierung deutlich sichtbar ist blockTiles.

Das war nicht schwer, oder? Ich lade Sie ein, die Ebenendaten in der Textdatei zu ändern, um neue Stufen auszuprobieren. Als nächstes kommt die sechseckige Version, die etwas komplizierter ist, und ich würde Ihnen raten, eine Pause zu machen, um mit der isometrischen Version zu spielen, bevor Sie fortfahren.

2. Sechseckiges Sokoban-Spiel

Die sechseckige Version des Sokoban-Levels würde wie das Bild unten aussehen.

Hexagonale Ansicht

Wir verwenden für dieses Tutorial die horizontale Ausrichtung des hexagonalen Gitters. Die Theorie hinter der hexagonalen Implementierung erfordert viel weiterführende Lektüre. Bitte beziehen Sie sich für ein grundlegendes Verständnis auf diese Tutorialserie. Die Theorie wird in der Hilfsklasse implementiert HexHelperHorizontal, das kann in gefunden werden utils Mappe.

Hexagonale Koordinatenumrechnung

Das HexagonalSokoban Das Spieleskript verwendet für die Koordinatenkonvertierung und andere sechseckige Funktionen praktische Methoden aus der Helfer-Klasse. Die Helferklasse HexHelperHorizontal funktioniert nur mit einem horizontal ausgerichteten Sechseckraster. Es enthält Methoden zum Konvertieren von Koordinaten zwischen versetzten, axialen und kubischen Systemen.

Die Versatzkoordinate ist dieselbe kartesische 2D-Koordinate. Es beinhaltet auch eine Nachbarn bekommen Methode, die eine axiale Koordinate einnimmt und a zurückgibt Liste mit allen sechs Nachbarn dieser Zelle koordinieren. Die Reihenfolge der Liste ist im Uhrzeigersinn, beginnend mit der Zellkoordinate des nordöstlichen Nachbarn.

Änderungen in den Steuerelementen

Bei einem Sechseckraster haben wir sechs Bewegungsrichtungen statt vier, da das Sechseck sechs Seiten hat, während ein Quadrat vier hat. Wir haben also sechs Tastaturtasten, um die Bewegung unseres Helden zu steuern, wie in der Abbildung unten dargestellt.

Die Tasten sind im gleichen Layout wie ein sechseckiges Raster angeordnet, wenn Sie die Tastaturtaste berücksichtigen S als mittlere Zelle, mit allen Steuertasten als sechseckigen Nachbarn. Es hilft, die Verwirrung bei der Steuerung der Bewegung zu reduzieren. Die entsprechenden Änderungen am Eingabecode sind wie folgt.

private void ApplyUserInput () // Wir haben 6 Bewegungsrichtungen, die von e, d, x, z, a, w in einer zyklischen Sequenz gesteuert werden, beginnend mit NE bis NW if (Input.GetKeyUp (userInputKeys [0])) TryMoveHero (0); // Nordosten else if (Input.GetKeyUp (userInputKeys [1])) TryMoveHero (1); // east else if (Input.GetKeyUp (userInputKeys [2])) TryMoveHero (2) // Südosten else if (Input.GetKeyUp (userInputKeys [3])) TryMoveHero (3); // Südwesten else if (Input.GetKeyUp (userInputKeys [4])) TryMoveHero (4); / / west else if (Input.GetKeyUp (userInputKeys [5])) TryMoveHero (5); // Nordwesten

Es gibt keine Änderungen in der Kunst und es sind keine Pivotänderungen erforderlich.

Andere Änderungen im Code

Ich werde die Codeänderungen in Bezug auf das ursprüngliche 2D Sokoban-Tutorial und nicht die isometrische Version oben erläutern. Bitte beziehen Sie sich auf den verlinkten Quellcode für dieses Tutorial. Die interessanteste Tatsache ist, dass fast der gesamte Code gleich bleibt. Das CreateLevel Methode hat nur eine Änderung, nämlich die middleOffset Berechnung.

middleOffset.x = cols * tileWidth + tileWidth * 0.5f; // Dies wird für hexagonal geändert. middleOffset.y = Zeilen * tileSize * 3/4 ​​+ tileSize * 0.75f; // dies wird für Isometrie geändert 

Eine wesentliche Änderung ist offensichtlich die Art und Weise, wie die Bildschirmkoordinaten im gefunden werden GetScreenPointFromLevelIndices Methode.

Vector2 GetScreenPointFromLevelIndices (int row, int col) // Indizes in Positionswerte konvertieren, col bestimmt X und Zeile bestimmen y Vector2 tempPt = new Vector2 (row, col); tempPt = HexHelperHorizontal.offsetToAxial (tempPt); // Konvertieren von Versatz in Axial // Konvertieren des Axialpunkts in Bildschirmpunkt tempPt = HexHelperHorizontal.axialToScreen (tempPt, sideLength); tempPt.x- = middleOffset.x-Screen.width / 2; // Add-Offsets für die mittlere Ausrichtung tempPt.y * = - 1; // Unity Y-Achsenkorrektur tempPt.y + = middleOffset.y-Screen.height / 2; return tempPt; 

Hier verwenden wir die Helfer-Klasse, um zuerst die Koordinate in axial zu konvertieren und dann die entsprechende Bildschirmkoordinate zu finden. Bitte beachten Sie die Verwendung der sideLength Variable für die zweite Konvertierung. Dies ist der Wert der Länge einer Seite des Sechsecks, der wiederum der Hälfte des Abstands zwischen den beiden spitzen Enden des Sechsecks entspricht. Daher:

sideLength = tileSize * 0.5f;

Die einzige andere Änderung ist die GetNextPositionAlong Methode, die von der verwendet wird TryMoveHero Methode, um die nächste Zelle in einer bestimmten Richtung zu finden. Diese Methode wurde vollständig geändert, um das völlig neue Layout unseres Gitters zu berücksichtigen.

private Vector2 GetNextPositionAlong (Vector2 objPos, int direction) // Diese Methode wurde vollständig geändert, um die unterschiedlichen Möglichkeiten zu berücksichtigen, in denen Nachbarn in der hexagonalen Logik gefunden werden Nachbarn = HexHelperHorizontal.getNeighbors (objPos); objPos = Nachbarn [Richtung]; // Die Nachbarliste folgt der gleichen Reihenfolge. ObjPos = HexHelperHorizontal.axialToOffset (ObjPos); // Konvertierung von Axial in Offset zurückkehren ObjPos; 

Mit Hilfe der Helfer-Klasse können wir leicht die Koordinaten des Nachbarn in die angegebene Richtung zurückgeben.

Alles andere bleibt wie bei der ursprünglichen 2D-Implementierung. Das war nicht schwer, oder? Allerdings ist es nicht leicht, zu verstehen, wie wir zu den Umwandlungsgleichungen gelangten, indem wir das sechseckige Tutorial befolgten, das der Kern des gesamten Prozesses ist. Wenn Sie spielen und das Level beenden, erhalten Sie das Ergebnis wie folgt.

Fazit

Das Hauptelement in beiden Konvertierungen waren die Koordinatenkonvertierungen. Die isometrische Version beinhaltet zusätzliche Änderungen in der Technik mit ihrem Drehpunkt sowie der Notwendigkeit einer Tiefensortierung.

Ich glaube, Sie haben herausgefunden, wie einfach es ist, Grid-basierte Spiele mit nur zweidimensionalen Array-basierten Pegeldaten und einem Kachel-basierten Ansatz zu erstellen. Es gibt unbegrenzte Möglichkeiten und Spiele, die Sie mit diesem neuen Verständnis erstellen können.

Wenn Sie alle Konzepte, die wir bisher besprochen haben, verstanden haben, möchte ich Sie dazu auffordern, die Kontrollmethode zu ändern, indem Sie auf tippen und Pfadfindung hinzufügen. Viel Glück.