Primer zum Erstellen von isometrischen Welten, Teil 2, aktualisiert

Was Sie erstellen werden

In diesem letzten Teil der Tutorialserie bauen wir auf dem ersten Tutorial auf und erfahren, wie Sie Pickups, Trigger, Level-Swapping, Pathfinding, Pfadverfolgung, Level-Scrolling, isometrische Höhe und isometrische Geschosse implementieren.

1. Pickups

Tonabnehmer sind Gegenstände, die innerhalb des Levels abgeholt werden können, normalerweise durch einfaches Übergehen - beispielsweise Münzen, Edelsteine, Bargeld, Munition usw.

Abholdaten können wie folgt in unsere Pegeldaten integriert werden:

[[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,8,0,0,1], [1,0,0, 8,0,1], [1,0,0,0,0,1], [1,1,1,1,1,1]]

In dieser Ebene verwenden wir Daten 8 einen Pickup auf einer Grasplatte zu bezeichnen (1 und 0 repräsentieren Wände bzw. begehbare Fliesen (wie zuvor). Dies könnte ein einzelnes Kachelbild sein, bei dem ein Grasplättchen mit dem Pickup-Bild überlagert ist. Nach dieser Logik werden wir zwei verschiedene Kachelzustände für jede Kachel benötigen, die eine Abholung hat, d. H. Eine mit Abholung und eine ohne, die nach dem Abholen der Abholung angezeigt werden soll.

Typische isometrische Bilder haben mehrere begehbare Kacheln. Angenommen, wir haben 30. Wenn wir N Tonabnehmer haben, benötigen wir zusätzlich zu den 30 ursprünglichen Kacheln N x 30 Kacheln, da jede Kachel eine Version mit haben muss Pickups und einer ohne. Das ist nicht sehr effizient. Stattdessen sollten wir versuchen, diese Kombinationen dynamisch zu erstellen. 

Um dieses Problem zu lösen, könnten wir dieselbe Methode verwenden, um den Helden im ersten Tutorial zu platzieren. Wann immer wir auf ein Pickup-Plättchen stoßen, legen wir zuerst ein Grasplättchen und dann den Pickup auf das Grasplättchen. Auf diese Weise benötigen wir neben 30 begehbaren Kacheln nur N Pickup-Kacheln, aber wir benötigen Zahlenwerte, um jede Kombination in den Ebenendaten darzustellen. Um den Bedarf an N x 30 Repräsentationswerten zu lösen, können wir einen separaten aufbewahren AbholungArray um die Abholdaten ausschließlich außerhalb des zu speichern levelData. Das abgeschlossene Level mit der Abholung ist unten dargestellt:

In unserem Beispiel halte ich die Dinge einfach und verwende kein zusätzliches Array für Tonabnehmer.

Pickups abholen

Die Erkennung von Abnehmern erfolgt auf dieselbe Weise wie die Erkennung von Kollisionskacheln nach dem den Charakter bewegen.

if (onPickupTile ()) pickupItem ();  function onPickupTile () // prüfe, ob es eine Abholung der Heldenplättchen gibt (levelData [heroMapTile.y] [heroMapTile.x] == 8);  

In der Funktion onPickupTile (), wir prüfen ob das levelData Array-Wert am heroMapTile Koordinate ist ein Abholfeld oder nicht. Die Nummer in der levelData Array an dieser Kachelkoordinate bezeichnet die Art der Aufnahme. Wir prüfen vor dem Bewegen des Charakters auf Kollisionen, müssen aber danach auf Abholungen prüfen, denn bei Kollisionen sollte der Charakter nicht die Stelle einnehmen, wenn er bereits von der Kollisionskachel besetzt ist, aber bei Abnehmern kann sich der Charakter frei bewegen darüber.

Zu beachten ist auch, dass sich die Kollisionsdaten normalerweise nie ändern, die Abholdaten sich jedoch ändern, wenn wir einen Artikel abholen. (Dies erfordert normalerweise nur das Ändern des Werts in levelData Array von sagen wir, 8 zu 0.)

Dies führt zu einem Problem: Was passiert, wenn wir den Pegel neu starten und somit alle Tonabnehmer auf ihre ursprünglichen Positionen zurücksetzen müssen? Wir haben keine Informationen, um dies zu tun, wie das levelData Array wurde geändert, als der Spieler Gegenstände abholte. Die Lösung besteht darin, während des Spiels ein dupliziertes Array für das Level zu verwenden und das Original zu behalten levelData Array intakt. Zum Beispiel verwenden wir levelData und levelDataLive [], Letzteres aus dem ersteren zu Beginn des Levels klonen und nur ändern levelDataLive [] während des Spiels.

Für das Beispiel spawning ich nach jedem Abholen eine zufällige Abholung auf einer freien Grasfläche und erhöht die Abholzahl. Das pickupItem Funktion sieht so aus.

function pickupItem () pickupCount ++; levelData [heroMapTile.y] [heroMapTile.x] = 0; // Spawn nächster Pickup SpawnNewPickup (); 

Sie sollten feststellen, dass wir immer dann nach Tonabnehmern suchen, wenn sich der Charakter auf dieser Kachel befindet. Dies kann innerhalb einer Sekunde mehrmals vorkommen (wir prüfen nur, wenn sich der Benutzer bewegt, aber wir können innerhalb einer Kachel herumgehen), aber die obige Logik schlägt nicht fehl. da haben wir die eingestellt levelData Array-Daten an 0 Wenn wir zum ersten Mal eine Abholung feststellen, werden alle darauffolgend onPickupTile () Schecks werden zurückkehren falsch für diese Fliese. Schauen Sie sich das interaktive Beispiel unten an:

2. Triggerfliesen

Wie der Name vermuten lässt, lösen Trigger-Kacheln etwas aus, wenn der Spieler darauf tritt oder eine Taste drückt. Sie können den Spieler an einen anderen Ort teleportieren, ein Tor öffnen oder einen Feind erzeugen, um einige Beispiele zu nennen. Pickups sind gewissermaßen eine besondere Form von Triggerplättchen: Wenn der Spieler auf ein Plättchen mit einer Münze tritt, verschwindet die Münze und der Münzzähler steigt.

Schauen wir uns an, wie wir eine Tür implementieren könnten, die den Spieler auf eine andere Ebene bringt. Die Kachel neben der Tür ist eine Auslöserplatte. wenn der Spieler die Taste drückt x Schlüssel, werden sie zum nächsten Level übergehen.

Um die Pegel zu ändern, müssen wir nur den Strom austauschen levelData Array mit dem der neuen Ebene, und setzen Sie das neue heroMapTile Position und Richtung für den Heldencharakter. Angenommen, es gibt zwei Ebenen mit Türen, um den Übergang zwischen ihnen zu ermöglichen. Da die Bodenplatte neben der Tür in beiden Ebenen die Triggerplatte ist, können Sie diese als neue Position für den Charakter verwenden, wenn sie in der Ebene angezeigt wird.

Die Implementierungslogik ist hier dieselbe wie für Pickups, und wir verwenden erneut die levelData Array zum Speichern von Triggerwerten. Für unser Beispiel, 2 bezeichnet eine Türfliese und der Wert daneben ist der Auslöser. Ich habe benutzt 101 und 102 mit der grundlegenden Konvention, dass jede Kachel mit einem Wert größer als 100 eine Trigger-Kachel ist und der Wert minus 100 der Pegel sein kann, zu dem sie führt:

var level1Data = [[1,1,1,1,1,1], [1,1,0,0,0,1], [1,0,0,0,0,1], [2,102,0 0,0,1], [1,0,0,0,1,1], [1,1,1,1,1,1]]; var level2Data = [[1,1,1,1,1,1], [1,0,0,0,0,1], [1,0,8,0,0,1], [1,0 0,0,101,2], [1,0,1,0,0,1], [1,1,1,1,1,1]];

Der Code zum Prüfen auf ein Triggerereignis ist unten dargestellt:

var xKey = game.input.keyboard.addKey (Phaser.Keyboard.X); xKey.onUp.add (triggerListener); // füge einen Signal Listener für die Ereignisfunktion up hinzu triggerListener () var trigger = levelData [heroMapTile.y] [heroMapTile.x]; if (Auslöser> 100) // gültige Auslöserfliese Auslöser- = 100; if (auslöser == 1) // auf Ebene 1 wechseln levelData = level1Data;  else // zu Ebene 2 wechseln levelData = level2Data;  für (var i = 0; i < levelData.length; i++)  for (var j = 0; j < levelData[0].length; j++)  trigger=levelData[i][j]; if(trigger>100) // Finde das neue Triggerplättchen und platziere den Helden dort heroMapTile.y = j; heroMapTile.x = i; heroMapPos = new Phaser.Point (heroMapTile.y * tileWidth, heroMapTile.x * tileWidth); heroMapPos.x + = (tileWidth / 2); heroMapPos.y + = (tileWidth / 2); 

Die Funktion triggerListener () prüft, ob der Wert des Trigger-Datenfelds an der angegebenen Koordinate größer als 100 ist. Wenn ja, ermitteln wir, zu welchem ​​Pegel wir wechseln müssen, indem wir 100 vom Kachelwert abziehen. Die Funktion findet die Trigger-Kachel im Neuen levelData, Dies ist die Spawn-Position für unseren Helden. Ich habe den Auslöser gemacht, um aktiviert zu werden x ist veröffentlicht; 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 wird, da der Charakter immer auf der neuen Ebene auf einem Trigger-Kachel erscheint.

Hier ist eine funktionierende Demo. Versuchen Sie, Gegenstände aufzusammeln, indem Sie darüber gehen und Ebenen tauschen, indem Sie neben Türen stehen und schlagen x.

3. Geschosse

EIN Projektil ist etwas, das sich mit einer bestimmten Geschwindigkeit in eine bestimmte Richtung bewegt, z. B. eine Kugel, ein Zauberspruch, eine Kugel usw. Alles an dem Geschoss ist, abgesehen von der Höhe, das gleiche wie der Heldencharakter: anstatt über den Boden zu rollen, Geschosse schweben oft in einer bestimmten Höhe darüber. Eine Kugel bewegt sich über die Hüfthöhe des Charakters, und sogar ein Ball muss möglicherweise herumspringen.

Interessanterweise ist zu beachten, dass die isometrische Höhe in einer 2D-Seitenansicht der Höhe entspricht, jedoch mit einem geringeren Wert. Es sind keine komplizierten Konvertierungen erforderlich. Wenn sich ein Ball in kartesischen Koordinaten 10 Pixel über dem Boden befindet, kann er sich in isometrischen Koordinaten um 10 oder 6 Pixel über dem Boden befinden. (In unserem Fall ist die relevante Achse die Y-Achse.)

Versuchen wir, einen Ball auf unserer ummauerten Wiese aufzubauen. Als ein Hauch von Realismus fügen wir dem Ball einen Schatten hinzu. Alles, was wir tun müssen, ist, den Bounce-Höhenwert zum isometrischen Y-Wert unseres Balls zu addieren. Der Wert für die Sprunghöhe ändert sich je nach Schwerkraft von Frame zu Frame. Sobald der Ball den Boden berührt, drehen wir die aktuelle Geschwindigkeit entlang der y-Achse.

Bevor wir das Abprallen in einem isometrischen System in Angriff nehmen, werden wir sehen, wie wir es in einem kartesischen 2D-System implementieren können. Lassen Sie uns die Sprungkraft des Balls mit einer Variablen darstellen zValue. Stellen Sie sich vor, dass der Ball eine Sprungkraft von 100 hat zValue = 100

Wir werden zwei weitere Variablen verwenden: incrementValue, was beginnt um 0, und Schwere, was hat einen Wert von -1. Jedes Bild wird von uns subtrahiert incrementValue von zValue, und subtrahieren Schwere von incrementValue um einen Dämpfungseffekt zu erzeugen. Wann zValue erreicht 0, es bedeutet, dass der Ball den Boden erreicht hat; An diesem Punkt wenden wir das Zeichen von incrementValue durch Multiplikation mit -1, es in eine positive Zahl umwandeln. Dies bedeutet, dass sich der Ball ab dem nächsten Bild nach oben bewegt und somit abprallt.

So sieht das im Code aus:

if (game.input.keyboard.isDown (Phaser.Keyboard.X)) zValue = 100;  incrementValue- = Schwerkraft; zValue- = incrementValue; if (zValue<=0) zValue=0; incrementValue*=-1; 

Der Code bleibt auch für die isometrische Ansicht derselbe, mit dem geringfügigen Unterschied, für den Sie einen niedrigeren Wert verwenden können zValue beginnen mit. Siehe unten, wie die zValue wird zu der isometrischen hinzugefügt y Wert des Balls beim Rendern.

function drawBallIso () var isoPt = new Phaser.Point (); // Es ist nicht ratsam, Punkte in der Aktualisierungsschleife zu erstellen. var ballCornerPt = new Phaser.Point (ballMapPos.x-ball2DVolume.x / 2, ballMapPos.y-ball2DVolume) .y / 2); isoPt = cartesianToIsometric (ballCornerPt); // Neue isometrische Position für den Helden aus der 2D-Kartenposition gameScene.renderXY finden (ballShadowSprite, isoPt.x + borderOffset.x + shadowOffset.x, isoPt.y + borderOffset.y, false //; Schatten zeichnen, um die Textur zu retten gameScene.renderXY (ballSprite, isoPt.x + borderOffset.x + ballOffset.x, isoPt.y + borderOffset.y-ballOffset.y-zValue, false); // Zeichne den Helden zum Rendern Textur

Schauen Sie sich das interaktive Beispiel unten an:

Verstehen Sie, dass die Rolle des Schattens eine sehr wichtige Rolle spielt, die zum Realismus dieser Illusion beiträgt. Beachten Sie auch, 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!

4. Einen Pfad finden und ihm folgen

Pfadfindung und Pfadverfolgung sind ziemlich komplizierte Prozesse. Es gibt verschiedene Ansätze, die verschiedene Algorithmen verwenden, um den Pfad zwischen zwei Punkten zu finden, jedoch als unsere levelData ist ein 2D-Array, die Dinge sind einfacher als es sonst sein könnte. Wir haben gut definierte und einzigartige Knoten, die der Spieler belegen kann, und wir können leicht überprüfen, ob sie begehbar sind.

zusammenhängende Posts

  • Ein Wegweiser für Anfänger
  • Zielbasierte Vektorfeldfindung
  • Beschleunigen Sie eine * Wegfindung mit dem Jump Point-Suchalgorithmus
  • Das "Pfad folgt" Lenkverhalten

Eine detaillierte Übersicht über Pfadfindungsalgorithmen liegt nicht im Rahmen dieses Artikels, aber ich werde versuchen, die am häufigsten verwendete Funktionsweise zu erläutern: Der kürzeste Pfadalgorithmus, von dem die Algorithmen von A * und Dijkstra berühmte Implementierungen sind.

Wir möchten Knoten 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 rekursiv für jeden neu besuchten Knoten 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, an dem die Rekursion endet und der vollständige Pfad als Knotenarray zurückgegeben wird. Manchmal wird der Endknoten nie erreicht. In diesem Fall schlägt die Pfadfindung fehl. Am Ende finden wir normalerweise mehrere Pfade zwischen den beiden Knoten. In diesem Fall nehmen wir den mit der kleinsten Anzahl von Knoten.

Wegfindung

Es ist nicht ratsam, das Rad neu zu erfinden, wenn es sich um genau definierte Algorithmen handelt. Daher würden wir vorhandene Lösungen für unsere Pfadfindungszwecke verwenden. Um Phaser verwenden zu können, benötigen wir eine JavaScript-Lösung, und ich habe EasyStarJS ausgewählt. Wir initialisieren die Pfadsuchmaschine wie folgt.

easystar = new EasyStar.js (); easystar.setGrid (levelData); easystar.setAcceptableTiles ([0]); easystar.enableDiagonals (); // wir möchten, dass der Pfad Diagonalen hat easystar.disableCornerCutting (); // kein diagonaler Pfad, wenn er an Wandecken geht

Als unser levelData hat nur 0 und 1, Wir können es direkt als Node-Array übergeben. Wir setzen den Wert von 0 als begehbarer Knoten. Wir ermöglichen das diagonale Gehen, deaktivieren sie jedoch, wenn Sie in der Nähe von Ecken von nicht begehbaren Fliesen laufen. 

Dies liegt daran, dass der Held, wenn er aktiviert ist, während eines diagonalen Spaziergangs in die nicht begehbare Kachel schneiden kann. In diesem Fall lässt die Kollisionserkennung den Helden nicht durch. Bitte beachten Sie auch, dass ich in diesem Beispiel die Kollisionserkennung vollständig entfernt habe, da dies für ein AI-basiertes Beispiel nicht mehr erforderlich ist. 

Wir werden den Abgriff auf ein beliebiges freies Feld innerhalb des Levels erkennen und den Pfad mithilfe von berechnen findPath Funktion. Die Rückrufmethode plotAndMove empfängt das Knotenfeld des resultierenden Pfads. Wir markieren das Minikarte mit dem neu gefundenen Pfad.

game.input.activePointer.leftButton.onUp.add (findPath) Funktion findPath () if (isFindingPath || isWalking) return; var pos = game.input.activePointer.position; var isoPt = new Phaser.Point (pos.x-borderOffset.x, pos.y-borderOffset.y); tapPos = isometricToCartesian (isoPt); tapPos.x- = tileWidth / 2; // Anpassung, um die richtige Fliese für Fehler aufgrund der Abrundung zu finden tapPos.y + = tileWidth / 2; tapPos = getTileCoordinates (tapPos, tileWidth); if (tapPos.x> -1 && tapPos.y> -1 && tapPos.x<7&&tapPos.y<7)//tapped within grid if(levelData[tapPos.y][tapPos.x]!=1)//not wall tile isFindingPath=true; //let the algorithm do the magic easystar.findPath(heroMapTile.x, heroMapTile.y, tapPos.x, tapPos.y, plotAndMove); easystar.calculate();    function plotAndMove(newPath) destination=heroMapTile; path=newPath; isFindingPath=false; repaintMinimap(); if (path === null)  console.log("No Path was found."); else path.push(tapPos); path.reverse(); path.pop(); for (var i = 0; i < path.length; i++)  var tmpSpr=minimap.getByName("tile"+path[i].y+"_"+path[i].x); tmpSpr.tint=0x0000ff; //console.log("p "+path[i].x+":"+path[i].y);   

Pfad folgt

Sobald wir den Pfad als Knotenarray haben, müssen wir das Zeichen dahinter bringen.

Angenommen, wir möchten, dass der Charakter zu einem Feld geht, auf das wir klicken. Wir müssen zuerst nach einem Pfad zwischen dem Knoten, den der Charakter aktuell belegt, und dem Knoten, auf den wir geklickt haben, suchen. Wenn ein erfolgreicher Pfad gefunden wurde, müssen wir das Zeichen zum ersten Knoten im Knotenarray verschieben, indem wir es als Ziel festlegen. Sobald wir den Zielknoten erreicht haben, prüfen wir, ob weitere Knoten im Knotenarray vorhanden sind. Wenn ja, legen Sie den nächsten Knoten als Ziel fest - und so weiter, bis wir den endgültigen Knoten erreichen.

Bei jedem Erreichen eines Knotens ändern wir auch die Richtung des Players basierend auf dem aktuellen Knoten und dem neuen Zielknoten. Zwischen Knoten gehen wir einfach in die gewünschte Richtung, bis wir den Zielknoten erreichen. Dies ist eine sehr einfache KI, und im Beispiel erfolgt dies in der Methode aiWalk teilweise unten gezeigt.

Funktion aiWalk () if (path.length == 0) // Pfad wurde beendet if (heroMapTile.x == destination.x && heroMapTile.y == destination.y) dX = 0; dY = 0; isWalking = false; Rückkehr;  isWalking = true; if (heroMapTile.x == destination.x && heroMapTile.y == destination.y) // aktuelles Ziel erreicht, neues setzen, Richtung ändern // Warten Sie, bis wir ein paar Schritte in die Kachel sind, bevor wir stepTaken ++ drehen; if (Schrittedestination.x) dX = -1;  else dX = 0;  if (heroMapTile.ydestination.y) dY = -1;  else dY = 0;  if (heroMapTile.x == destination.x) dX = 0;  else if (heroMapTile.y == destination.y) dY = 0;  //…

Wir tun Sie müssen gültige Klickpunkte herausfiltern, indem Sie bestimmen, ob wir innerhalb des begehbaren Bereichs geklickt haben, anstatt eine Wandfliese oder eine andere nicht begehbare Fliese.

Ein weiterer interessanter Punkt für die Kodierung der KI: Wir möchten nicht, dass sich der Charakter, sobald er im aktuellen Feld angekommen ist, dem nächsten Teil des Knotenfelds zugewandt 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 Abbiegen manuell in die Mitte des aktuellen Plättchens zu legen, damit sich alles perfekt anfühlt.

Schauen Sie sich die Arbeitsdemo unten an:

5. Isometrisches Scrollen

Wenn der Ebenenbereich viel größer als der verfügbare Bildschirmbereich ist, müssen wir ihn erstellen scrollen.

Der sichtbare Bildschirmbereich kann als kleineres Rechteck innerhalb des größeren Rechtecks ​​des gesamten Ebenenbereichs betrachtet werden. Beim Scrollen wird im Wesentlichen nur das innere Rechteck innerhalb des größeren Rechtecks ​​verschoben. Normalerweise bleibt die Position des Helden in Bezug auf das Bildschirmrechteck, normalerweise in der Bildschirmmitte, unverändert. Interessanterweise müssen Sie beim Scrollen nur den Eckpunkt des inneren Rechtecks ​​verfolgen.

Dieser Eckpunkt, den wir in kartesischen Koordinaten darstellen, wird in den Ebenendaten in einer Kachel liegen. Zum Scrollen erhöhen wir die x- und y-Position des Eckpunkts in kartesischen Koordinaten. Jetzt können wir diesen Punkt in isometrische Koordinaten konvertieren und damit den Bildschirm zeichnen. 

Die neu konvertierten Werte im isometrischen Raum müssen auch die Ecke unseres Bildschirms sein, dh sie sind die neuen (0, 0). Beim Parsen und Zeichnen der Pegeldaten subtrahieren wir diesen Wert also von der isometrischen Position der einzelnen Kacheln und können feststellen, ob die neue Position der Kachel innerhalb des Bildschirms liegt. 

Alternativ können wir entscheiden, dass wir nur einen zeichnen werden X x Y isometrisches Kachelgitter auf dem Bildschirm, um die Zeichnungsschleife für größere Ebenen effizienter zu gestalten. 

Wir können dies in Schritten so ausdrücken:

  • Aktualisieren Sie die x- und y-Koordinaten des kartesischen Eckpunkts.
  • Wandeln Sie dies in den isometrischen Raum um.
  • Subtrahieren Sie diesen Wert von der isometrischen Zeichenposition jeder Kachel.
  • Zeichnen Sie ab dieser neuen Ecke nur eine begrenzte Anzahl vordefinierter Kacheln auf dem Bildschirm.
  • Optional: Zeichnen Sie die Kachel nur, wenn die neue isometrische Zeichnungsposition innerhalb des Bildschirms liegt.
var cornerMapPos = new Phaser.Point (0,0); var cornerMapTile = new Phaser.Point (0,0); var visibleTiles = neuer Phaser.Point (6,6); //… function update () //… if (isWalkable ()) heroMapPos.x + = heroSpeed ​​* dX; heroMapPos.y + = heroSpeed ​​* dY; // verschiebe die Ecke in die entgegengesetzte Richtung cornerMapPos.x - = heroSpeed ​​* dX; cornerMapPos.y - = heroSpeed ​​* dY; cornerMapTile = getTileCoordinates (cornerMapPos, tileWidth); // Holen Sie sich die neue Heldenkarte-Kachel heroMapTile = getTileCoordinates (heroMapPos, tileWidth); // depthsort & neue Szene zeichnen renderScene ();  function renderScene () gameScene.clear (); // lösche den vorherigen Frame und zeichne dann erneut var tileType = 0; // lasst uns die Schleifen innerhalb des sichtbaren Bereichs begrenzen var startTileX = Math.max (0,0-cornerMapTile.x); var startTileY = Math.max (0,0-cornerMapTile.y); var endTileX = Math.min (levelData [0] .length, startTileX + visibleTiles.x); var endTileY = Math.min (levelData.length, startTileY + visibleTiles.y); startTileX = Math.max (0, endTileX-visibleTiles.x); startTileY = Math.max (0, endTileY-visibleTiles.y); // auf Randbedingung prüfen für (var i = startTileY; i < endTileY; i++)  for (var j = startTileX; j < endTileX; j++)  tileType=levelData[i][j]; drawTileIso(tileType,i,j); if(i==heroMapTile.y&&j==heroMapTile.x) drawHeroIso();     function drawHeroIso() var isoPt= new Phaser.Point();//It is not advisable to create points in update loop var heroCornerPt=new Phaser.Point(heroMapPos.x-hero2DVolume.x/2+cornerMapPos.x,heroMapPos.y-hero2DVolume.y/2+cornerMapPos.y); isoPt=cartesianToIsometric(heroCornerPt);//find new isometric position for hero from 2D map position gameScene.renderXY(sorcererShadow,isoPt.x+borderOffset.x+shadowOffset.x, isoPt.y+borderOffset.y+shadowOffset.y, false);//draw shadow to render texture gameScene.renderXY(sorcerer,isoPt.x+borderOffset.x+heroWidth, isoPt.y+borderOffset.y-heroHeight, false);//draw hero to render texture  function drawTileIso(tileType,i,j)//place isometric level tiles var isoPt= new Phaser.Point();//It is not advisable to create point in update loop var cartPt=new Phaser.Point();//This is here for better code readability. cartPt.x=j*tileWidth+cornerMapPos.x; cartPt.y=i*tileWidth+cornerMapPos.y; isoPt=cartesianToIsometric(cartPt); //we could further optimise by not drawing if tile is outside screen. if(tileType==1) gameScene.renderXY(wallSprite, isoPt.x+borderOffset.x, isoPt.y+borderOffset.y-wallHeight, false); else gameScene.renderXY(floorSprite, isoPt.x+borderOffset.x, isoPt.y+borderOffset.y, false);  

Bitte beachten Sie, dass der Eckpunkt im erhöht wird Gegenteil Richtung zum Positionsupdate des Helden, während er sich bewegt. Dies stellt sicher, dass der Held in Bezug auf den Bildschirm dort bleibt, wo er ist. Schauen Sie sich dieses Beispiel an (scrollen Sie mit den Pfeilen, tippen Sie auf, um das sichtbare Gitter zu vergrößern).

Ein paar Anmerkungen:

  • Während des Bildlaufs müssen wir möglicherweise zusätzliche Kacheln an den Bildschirmrändern zeichnen. Andernfalls können Kacheln verschwinden und an den Bildschirmextremen erscheinen.
  • Wenn Sie mehr als ein Feld mit Kacheln haben, müssen Sie an den Rändern mehr Kacheln zeichnen. Wenn beispielsweise die größte Kachel im gesamten Satz X nach Y misst, müssen Sie X weitere Kacheln nach links und rechts und Y weitere Kacheln nach oben und unten zeichnen. Dadurch wird sichergestellt, dass die Ecken der größeren Kachel beim Scrollen innerhalb oder außerhalb des Bildschirms weiterhin sichtbar sind.
  • Wir müssen immer noch sicherstellen, dass keine leeren Bereiche auf dem Bildschirm angezeigt werden, während wir nahe an den Rand der Ebene zeichnen.
  • Das Level sollte nur so lange gescrollt werden, bis das extremste Plättchen extrem auf dem entsprechenden Bildschirm gezeichnet wird. Danach sollte sich der Charakter im Bildschirmbereich bewegen ohne die Ebene scrollen. Dazu müssen wir alle vier Ecken des inneren Bildschirmrechtecks ​​verfolgen und die Bildlauf- und Bewegungslogik entsprechend drosseln. Sind Sie bereit für die Herausforderung, dies für sich selbst umzusetzen??

Fazit

Diese Serie richtet sich insbesondere an Anfänger, die versuchen, isometrische Spielwelten zu erkunden. Viele der erläuterten Konzepte haben alternative Ansätze, die etwas komplizierter sind, und ich habe bewusst die einfachsten ausgewählt. 

Sie erfüllen möglicherweise nicht die meisten Szenarien, auf die Sie möglicherweise stoßen, aber das gewonnene Wissen kann verwendet werden, um auf diesen Konzepten aufzubauen und kompliziertere Lösungen zu erstellen. Zum Beispiel wird die Implementierung der einfachen Tiefensortierung unterbrochen, wenn sich mehrere Stockwerke und Plattformkacheln von einer Story zur anderen bewegen. 

Aber das ist ein Tutorial für ein anderes Mal.