Tilemap-Grenzen komprimieren Gut gemacht, das Spielbrett ist einsatzbereit! Wir werden in Teil 3 darauf zurückkommen.
Teil 2. Level-Daten
Da wir dieselbe Szene und dasselbe Spielbrett wiederverwenden möchten, sollten die Level-Daten irgendwo aufbewahrt werden. Eine einfache Lösung dafür ist eine einfache JSON-Datei, die beschreibt, wie jede Ebene erstellt werden soll.
Es ist wichtig zu verstehen, was wir erreichen wollen, deshalb muss ich ein paar Worte über die Mechanik sagen. Im Spiel gibt es Objekte, die sich vom Anfang bis zum Ende entlang des Pfades bewegen (ähnlich wie in Zuma), und das Ziel des Spielers ist es, sie alle zu zerstören. In diesem Tutorial erstellen wir diesen Pfad, der für jedes Level einzigartig ist.
Okay, zurück zum Projekt.
Es gibt mehrere Möglichkeiten, über ein Skript auf externe Daten in einer Laufzeit zuzugreifen. Hier werden wir Ressourcenordner verwenden
Erstellen wir einen neuen Ordner — Dateien — und einen Unterordner Ressourcen. Das ist der Ort, wo wir die Daten behalten wollen, so erstellen Sie eine neue Datei — Ebenen.json und legen Sie es dort ab.
Für das Tutorial haben wir nur zwei Felder, um jede Ebene zu beschreiben:
number — ein int, um eine Ebene zu identifizieren
path — der kachelbasierte Pfad, den wir programmgesteuert erstellen möchten. Array von Werten, wobei der erste Wert der Startpunkt und der letzte Wert der Endpunkt ist.
Dies ist die Datei, die ich verwenden werde. Mach dir keine Sorgen über diese Werte in path , wir werden später darauf kommen.
{ "levels": }, { "number": 2, "path": }, { "number": 3, "path": } ] }
Erstellen wir einen weiteren Ordner — Skripte — und jetzt beginnt endlich die Codierung.
Wir möchten auf die Daten aus der Datei im Code zugreifen, daher benötigen wir eine Modellklasse dafür. Es ist Zeit, unser allererstes Skript zu erstellen – LevelsData
. Es soll nicht von Unity instanziiert werden, daher sollten MonoBehaviour
und Start
Update
Methoden entfernt werden. Aus der obigen Datei können wir sehen, dass das Wurzelelement eine array
von Ebenen ist, wobei jede Ebene eine int
Feldnummer und einen int array
Feldpfad haben sollte. Vergessen Sie auch nicht, die Annotation
.
public class LevelsData { public LevelData levels; public class LevelData { public int number; public int path; } }
Schön, jetzt haben wir die Datei und das Modell. Der nächste Schritt besteht darin, einen in einen anderen zu verwandeln. Lassen Sie uns ein weiteres Skript erstellen — GameZone
— und es an das GameZone
Objekt in der Szene anhängen. Dieses Skript wird später verwendet, um das gesamte Spielbrett einzurichten.
Folgen Sie dem Single responsibility principle Lassen Sie uns ein weiteres Skript erstellen — LevelsDataLoader
— das die gesamte Transformation durchführt. Hängen Sie es auch an das GameZone
-Objekt an.
public class LevelsDataLoader : MonoBehaviour { private const string LevelsPath = "Levels"; public Dictionary<int, LevelsData.LevelData> ReadLevelsData() { var jsonFile = Resources.Load(LevelsPath, typeof(TextAsset)) as TextAsset; if (jsonFile == null) { throw new ApplicationException("Levels file is not accessible"); } var loadedData = JsonUtility.FromJson<LevelsData>(jsonFile.text); return loadedData.levels.ToDictionary(level => level.number, level => level); } }
Diese Klasse lädt die Daten und gibt sie als Wörterbuch zurück, wobei der Schlüssel die Ebenennummer und die Daten die Ebenendaten selbst sind.
Jetzt sollten wir in GameZone
Skript auf die Daten zugreifen können.
public class GameZone : MonoBehaviour { private Dictionary<int, LevelsData.LevelData> _levelsData; private LevelsDataLoader _dataLoader;private void Awake() { _dataLoader = GetComponent<LevelsDataLoader>(); }private void Start() { _levelsData = _dataLoader.ReadLevelsData();Debug.Log(_levelsData.Count + " levels have been stored in the dictionary!"); } }
Wechseln Sie zurück zu Unity, drücken Sie die Play—Taste und überprüfen Sie die Konsole – Sie sollten die Meldung „3 Ebenen wurden im Wörterbuch gespeichert!“
Teil 3. Anschließen des Boards und der Daten
Herzlichen Glückwunsch, Sie haben den letzten und interessantesten Teil dieses Tutorials erreicht. Wie werden wir das Board und die Daten tatsächlich verbinden? Lesen Sie weiter, um es herauszufinden!
Lassen Sie uns zunächst horizontal
und start_stop
Kacheln, die im ersten Teil des Tutorials erstellt wurden, im Ressourcenordner unter dem Kachelordner ablegen. Fügen Sie dann ein neues Skript hinzu — TilesResourcesLoader
— eine statische Hilfsklasse, um Kacheln aus diesem Ordner zur Laufzeit zu laden.
public static class TilesResourcesLoader { private const string PathHorizontal = "horizontal"; private const string StartStop = "start_stop"; public static Tile GetPathHorizontalTile() { return GetTileByName(PathHorizontal); } public static Tile GetStartStopTile() { return GetTileByName(StartStop); } private static Tile GetTileByName(string name) { return (Tile) Resources.Load(name, typeof(Tile)); } }
Als letzten Schritt sollten wir diese Kacheln beim Start der Szene auf das Brett legen. Kehren wir zum Skript GameZone
zurück. Zuerst müssen wir die Levelauswahl simulieren, im realen Spiel passiert es normalerweise, wenn ein Benutzer eine Leveltaste drückt. Fügen wir der Einfachheit halber eine öffentliche Feldebene zu GameZone
hinzu und ändern Sie den Wert für start in 1. Ich zeige Ihnen zuerst das endgültige Skript:
public class GameZone : MonoBehaviour { public int Level; private const int FieldLineSize = 11; private const int FieldTotalTiles = FieldLineSize * FieldLineSize; private Dictionary<int, LevelsData.LevelData> _levelsData; private void Start() { _levelsData = GetComponent<LevelsDataLoader>().ReadLevelsData(); SetupTiles(); } private void SetupTiles() { var baseLevel = GetComponentsInChildren<Tilemap>(); var localTilesPositions = new List<Vector3Int>(FieldTotalTiles); foreach (var pos in baseLevel.cellBounds.allPositionsWithin) { Vector3Int localPlace = new Vector3Int(pos.x, pos.y, pos.z); localTilesPositions.Add(localPlace); } SetupPath(localTilesPositions, baseLevel); } private void SetupPath(List<Vector3Int> localTilesPositions, Tilemap baseLevel) { var path = _levelsData.path; var pathHorizontalTile = TilesResourcesLoader.GetPathHorizontalTile(); var first = path.First(); var last = path.Last(); foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last))) { baseLevel.SetTile(localPosition, pathHorizontalTile); } var startStopTile = TilesResourcesLoader.GetStartStopTile(); baseLevel.SetTile(localTilesPositions, startStopTile); baseLevel.SetTile(localTilesPositions, startStopTile); } }
Wow, das ist eine Menge Action! Lass mich dich durch sie führen.
In der SetupTiles
-Methode sollten wir zuerst die Kachelkarte selbst abrufen, da wir die Positionen der Kacheln kennen müssen, um sie ändern zu können. Um dies zu erreichen, verwenden wir die tilemap.cellBounds.allPositionsWithin
Methode, die alle Positionen von Kacheln beginnend mit der allerersten zurückgibt — in der gegebenen Konfiguration ist es eine Kachel ganz unten.
Siehe das folgende Bild, wobei jede Zahl den Index in der localTilesPositions
Liste darstellt.
Nummerierte Kachelkarte Erinnern Sie sich an die Werte, die wir im Pfad in Levels.json
? Wie Sie vielleicht schon erraten haben, sind diese Werte die Indizes der Kacheln im Array. Alles, was Sie jetzt tun müssen, ist ein Cheatsheet-Bild, das Ihnen beim Erstellen von Levels hilft. Das ist das, was ich während der Entwicklung verwendet habe:
Meine hässliche nummerierte Kachelkarte In diesem Beispiel richten wir eine horizontale Linie im Pfad ein, siehe SetupPath
Methode. Der Schlüsselteil ist die folgende Schleife:
foreach (var localPosition in localTilesPositions.GetRange(first, Math.Abs(first - last))) { baseLevel.SetTile(localPosition, pathHorizontalTile); }
Hier iterieren wir über localTilesPositions
, um diejenigen zu finden, die die gewünschte Kachel einstellen — in diesem Fall horizontal.
Hinweis! GetRange
Methode hat zwei Parameter — index und count.
Um die Start- und Endpositionen des Pfades zu markieren, wird die start_stop
-Kachel verwendet.
Hier ist das Ergebnis unserer harten Arbeit:
Endergebnis Versuchen Sie nun, das Ebenennummernfeld des GameZone
Skripts von 1 auf 2 oder 3 zu ändern, und Sie werden sehen, dass der Pfad für die angegebene Ebene ordnungsgemäß geladen ist.
Danach