Roguelike in Unity von Grund auf neu erstellen: Dungeon-Generator

Bild

Dieses Mal werden wir uns mit der Implementierung des Algorithmus des Dungeongenerators befassen. Im letzten Artikel haben wir den ersten Raum erstellt und jetzt werden wir den Rest des Dungeon-Levels generieren.

Aber bevor wir anfangen, möchte ich einen Fehler aus einem früheren Beitrag beheben. Tatsächlich habe ich in den letzten Wochen etwas Neues gelernt, weshalb einige meiner Arbeiten veraltet sind und ich darüber sprechen möchte.

Erinnerst du dich an die Positionsklasse, die wir erstellt haben? Tatsächlich verfügt Unity bereits über eine integrierte Klasse, die genau dieselben Funktionen ausführt, jedoch mit etwas besserer Kontrolle - es ist einfacher zu deklarieren und zu verarbeiten. Diese Klasse heißt Vector2Int. Daher entfernen wir vor dem Start die Positionsklasse aus MapManager.cs und ersetzen jede Positionsvariable durch die Vector2Int-Variable.


Dasselbe muss an mehreren Stellen im Skript DungeonGenerator.cs ausgeführt werden. Kommen wir nun zum Rest des Algorithmus.

Stufe 7 - Raum- / Hallengenerierung


Wir beginnen mit einer kleinen Änderung der zuletzt erstellten Funktion FirstRoom (). Anstatt eine andere Funktion zu erstellen, um alle anderen Elemente der Karte zu generieren und eine Menge Code zu duplizieren, transformieren wir diese Funktion einfach und verwandeln sie in eine verallgemeinerte GenerateFeature (). Ändern Sie daher den Namen von FirstRoom in GenerateFeature.

Jetzt müssen wir Parameter an diese Funktion übergeben. Zunächst müssen Sie wissen, welche Funktion es erzeugt - einen Raum oder einen Korridor. Wir können einfach eine Zeichenfolge namens type übergeben. Als nächstes muss die Funktion den Startpunkt des Elements kennen, dh von welcher Wand es kommt (weil wir immer ein neues Element aus der Wand des älteren Elements erstellen), und dafür reicht es aus, als Wall-Argument zu übergeben. Schließlich weist der erste zu erstellende Raum spezielle Merkmale auf, sodass wir eine optionale Bool-Variable benötigen, die angibt, ob das Element der erste Raum ist. Standardmäßig ist es false: bool isFirst = false. Der Funktionstitel ändert sich also wie folgt:


auf das:


Fein. Der nächste Schritt besteht darin, die Art und Weise zu ändern, in der Sie die Breite und Höhe des Elements berechnen. Während wir sie berechnen, erhalten wir einen zufälligen Wert zwischen den Min- und Max-Werten der Höhe und Breite der Räume - dies ist ideal für Räume, funktioniert jedoch nicht für Korridore. Bisher haben wir also Folgendes:


Die Korridore haben jedoch je nach Ausrichtung eine konstante Größe von 3 in Breite oder Höhe. Daher müssen wir überprüfen, was das Element ist - ein Raum oder ein Korridor - und dann die entsprechenden Berechnungen durchführen.


Damit. Wir prüfen, ob es sich bei dem Artikel um ein Zimmer handelt. Wenn ja, dann machen wir dasselbe wie zuvor
- wir erhalten eine Zufallszahl im Intervall zwischen min und max von Höhe und Breite. Aber jetzt in sonst gleich, wenn Sie etwas anderes machen müssen. Wir müssen die Ausrichtung des Korridors überprüfen. Glücklicherweise speichern wir beim Generieren einer Wand Informationen darüber, in welche Richtung sie gerichtet ist, und verwenden sie, um die Ausrichtung des Korridors zu ermitteln.


Die Variable minCorridorLength haben wir jedoch noch nicht deklariert. Sie müssen zu Variablendeklarationen zurückkehren und diese direkt über maxCorridorLength deklarieren.


Nun zurück zu unseren bedingten switch-Anweisungen. Was wir hier tun: Wir erhalten den Wert der Richtung der Wand, dh wohin die Wand schaut, von der aus der Korridor verläuft. Die Richtung kann nur vier mögliche Werte haben: Süd, Nord, West und Ost. Im Fall von Süd und Nord hat der Korridor eine Breite von 3 (zwei Wände und ein Boden in der Mitte) und eine variable Höhe (Länge). Für West und Ost ist alles umgekehrt: Die Höhe beträgt immer 3, und die Breite hat eine variable Länge. Also machen wir's.


Beeindruckend. Und hier haben wir die Größe des neuen Artikels festgelegt. Jetzt müssen Sie entscheiden, wo Sie es platzieren möchten. Wir haben den ersten Raum an einer zufälligen Stelle innerhalb der Schwellenwerte relativ zur Kartenmitte platziert.


Bei allen anderen Elementen funktioniert dies jedoch nicht. Sie sollten neben dem zufälligen Punkt an der Wand beginnen, von dem aus das Element generiert wird. Ändern wir also den Code. Zuerst müssen wir prüfen, ob das Element der erste Raum ist. Wenn dies der erste Raum ist, definieren wir die Startpunkte auf die gleiche Weise wie zuvor - als die halbe Breite und Höhe der Karte.


In andere, wenn das Element nicht der erste Raum ist, dann bekommen wir einen beliebigen Punkt an der Wand , aus dem das Element erzeugt wird. Zuerst müssen wir überprüfen, ob die Wand eine Größe von 3 hat (dies bedeutet, dass es der Endpunkt des Korridors ist), und wenn ja, wird immer der Mittelpunkt ausgewählt, dh Index 1 des Wandarrays (mit 3 Elementen hat das Array Indizes 0, 1, 2). Wenn die Größe jedoch nicht gleich 3 ist (die Wand ist nicht der Endpunkt des Korridors), nehmen wir einen zufälligen Punkt im Intervall zwischen Punkt 1 und der Länge der Wand minus 2. Dies ist erforderlich, um Passagen in der Ecke zu vermeiden. Das heißt, an einer Wand mit einer Länge von 6 schließen wir die Indizes 0 und 5 (erste und letzte) aus und wählen einen zufälligen Punkt unter den Punkten 1, 2, 3 und 4 aus.


Jetzt haben wir die Position des Punktes an der Wand, an dem ein neues Element erstellt wird. Wir können aber nicht einfach von dort aus ein Element erzeugen, da es auf diese Weise durch bereits platzierte Wände blockiert wird. Es ist auch wichtig zu beachten, dass das Element von seiner unteren linken Ecke aus erzeugt wird und dann das Inkrement nach rechts und oben ausgeführt wird. Daher müssen wir die Anfangsposition an verschiedenen Stellen festlegen, abhängig von der Richtung, in die die Wand schaut. Außerdem sind die erste Spalte x und die erste Zeile y Wände. Wenn wir ein neues Element direkt neben einem Punkt an der Wand beginnen, können wir einen Korridor erstellen, der in einer Ecke des Raums und nicht an einer geeigneten Stelle an der Wand endet.

Wenn die Wand nach Norden gerichtet ist, muss das Element an einer Position im Norden entlang der y-Achse beginnen, jedoch an einer zufälligen Anzahl von Positionen im Westen entlang der x-Achse im Bereich von 1 bis zur Breite des Raums 2. In südlicher Richtung verhält sich die x-Achse gleich, aber die Startposition auf der y-Achse ist die Position des Punkts an der Wand abzüglich der Raumhöhe. Die West- und Ostwand folgen der gleichen Logik, nur mit umgekehrten Achsen.

Bevor wir dies alles tun, müssen wir die Position des Wandpunkts in der Variablen Vector2Int speichern, damit wir ihn später bearbeiten können.


Großartig. Lass uns das tun.


Also haben wir ein Element mit der Größe und Position generiert. Der nächste Schritt besteht darin, das Element auf der Karte zu platzieren. Aber zuerst müssen wir herausfinden, ob auf der Karte wirklich Platz für dieses Element an dieser Position ist. Im Moment rufen wir einfach die Funktion CheckIfHasSpace () auf. Es wird rot hervorgehoben, da wir es noch nicht implementiert haben. Wir werden dies direkt tun, nachdem wir fertig sind, was hier in der GenerateFeature () -Funktion getan werden muss. Ignorieren Sie daher die rote Unterstreichung und fahren Sie fort.


Im nächsten Teil werden Wände erstellt. Bis wir es berühren, mit Ausnahme des Fragments in der zweiten for- Schleife .


Beim Schreiben dieses Beitrags habe ich festgestellt, dass diese if-else-Konstrukte völlig falsch sind. Zum Beispiel erhalten einige Wände in ihnen eine Länge von 1. Dies geschieht, weil, wenn die Position beispielsweise zur Nordwand hinzugefügt werden soll, wenn sie sich an der Ecke mit der Ostwand befindet, sie nicht wie gewünscht zur Ostwand hinzugefügt wird. Dies verursachte störende Fehler im Generierungsalgorithmus. Lassen Sie uns sie beseitigen.

Sie zu reparieren ist ziemlich einfach. Es reicht aus, alles andere zu löschen , damit die Position alle if- Konstrukte durchläuft und nicht beim ersten Mal stoppt, wenn sie true zurückgibt . Dann wird das letzte andere (das, das nicht anders ist, wenn ) in if geändertHiermit wird überprüft, ob die Position bereits als Wand hinzugefügt wurde. Wenn dies nicht der Fall ist, wird sie als Boden hinzugefügt.


Erstaunlich, wir sind hier fast fertig. Jetzt haben wir ein völlig neues Element, das an der richtigen Stelle erstellt wurde, aber es ist dasselbe wie unser erster Raum: Es ist vollständig von Wänden umgeben. Dies bedeutet, dass der Spieler diesen neuen Ort nicht erreichen kann. Das heißt, wir müssen einen Punkt an der Wand (der, wie wir uns erinnern, in einer Variablen vom Typ Vector2Int gespeichert ist) und den entsprechenden Punkt an der Wand eines neuen Elements in Floor konvertieren. Aber nur wenn das Element nicht der erste Raum ist.


Dieser Code überprüft, ob der neue Artikel der erste Raum ist. Wenn nicht, konvertiert es die letzte Position der Wand in den Boden und überprüft dann die Richtung, in die die Wand schaut, um zu überprüfen, welche Fliese des neuen Elements in den Boden verwandelt werden soll.

Wir haben den letzten Teil der Funktion GenerateFeature () erreicht. Es enthält bereits Zeilen, die Informationen zu dem von der Funktion erstellten Element hinzufügen.


Hier müssen wir etwas ändern. Erstens ist der Elementtyp nicht immer gleich Raum. Glücklicherweise wird die erforderliche Variable als Parameter an die Funktion übergeben, nämlich als Typzeichenfolge. Ersetzen wir hier also einfach "Raum" durch Typ.


Gut. Damit der Algorithmus, der alle Elemente des Spiels generiert, ordnungsgemäß funktioniert, müssen wir hier neue Daten hinzufügen. Ein int, das die Anzahl der erstellten Elemente und eine Liste aller erstellten Elemente zählt. Wir gehen zu der Stelle, an der wir alle Variablen deklarieren und ein int mit dem Namen countFeatures sowie eine Liste von Elementen mit dem Namen allFeatures deklarieren. Die Liste aller Elemente muss öffentlich sein, und der int-Zähler kann privat sein.


Kehren Sie nun zur Funktion GenerateFeature () zurück und fügen Sie am Ende einige Zeilen hinzu: Inkrementieren der Variablen countFeatures und Hinzufügen eines neuen Elements zur Liste allFeatures.


Unser GenerateFeature () ist also fast vollständig. Später müssen wir darauf zurückgreifen, um die leere CheckIfHasSpace-Funktion auszufüllen, aber zuerst müssen wir sie erstellen. Das machen wir jetzt.

Stufe 8 - Überprüfen Sie, ob es einen Platz gibt


Jetzt erstellen wir eine neue Funktion direkt nach Abschluss der GenerateFeature () -Funktion. Sie braucht zwei Argumente: die Position, an der das Element beginnt, und die Position, an der es endet. Sie können zwei Vector2Int-Variablen als diese verwenden. Die Funktion sollte einen Bool-Wert zurückgeben, damit er verwendet werden kann, wenn nach Speicherplatz gesucht wird.


Es ist rot unterstrichen, da es bisher nichts zurückgegeben hat. Bald werden wir es reparieren, aber im Moment werden wir nicht darauf achten. In dieser Funktion durchlaufen wir alle Positionen zwischen dem Anfang und dem Ende des Elements und prüfen, ob die aktuelle Position in MapManager.map null ist oder bereits etwas vorhanden ist. Wenn dort etwas ist, stoppen wir die Funktion und geben false zurück. Wenn nicht, fahren Sie fort. Wenn die Funktion das Ende der Schleife erreicht, ohne die gefüllten Stellen zu treffen, geben Sie true zurück.

Bevor wir die Position auf Null überprüfen, benötigen wir außerdem eine Linie, um zu überprüfen, ob sich die Position innerhalb der Karte befindet. Andernfalls kann es zu einem Array-Indexfehler und einem Spielabsturz kommen.


Fein. Nun zurück zu der Stelle, an der wir diese Funktion in die GenerateFeature () -Funktion einfügen. Wir müssen diesen Aufruf beheben, da er nicht die erforderlichen Argumente übergibt.

Hier möchten wir eine if-Anweisung einfügen , um zu überprüfen, ob genügend Platz für das Element vorhanden ist. Wenn das Ergebnis falsch ist, beenden wir die Funktion, ohne ein neues Element in MapManager.map einzufügen.


Wir müssen die erforderlichen Argumente übergeben, dh zwei Vector2Int-Variablen. Beim ersten ist alles einfach, dies ist die Position mit den x- und y-Koordinaten des Startpunkts des Elements.


Der zweite ist schwieriger, aber nicht viel. Dies ist der Startpunkt plus Höhe für y und Breite für x, wobei 1 von beiden abgezogen wird (da der Start bereits berücksichtigt wurde).


Fahren wir nun mit dem nächsten Schritt fort: Erstellen eines Algorithmus zum Aufrufen der Funktion GenerateFeature ().

Stufe 9 - Generierte Elemente aufrufen


Zurück zur GenerateDungeon () -Funktion, die im vorherigen Teil des Artikels erstellt wurde. Jetzt sollte es so aussehen:


Der Aufruf von FirstRoom () ist rot unterstrichen, da wir den Namen dieser Funktion geändert haben. Nennen wir einfach die erste Raumgeneration.


Wir haben die erforderlichen Argumente übergeben: "Raum" als Typ, da der erste Raum immer Raum, neue Wand () sein wird, weil der erste Raum nicht aus einem anderen erstellt wird, also übergeben wir einfach null, und das ist ganz normal. Anstelle von new Wall () können Sie null ersetzen . Dies ist eine Frage der persönlichen Präferenz. Das letzte Argument bestimmt, ob das neue Element der erste Raum ist. In unserem Fall übergeben wir also true .

Nun kommen wir zum Hauptpunkt. Wir verwenden eine for-Schleife, die 500 Mal ausgeführt wird - ja, wir werden versuchen, Elemente 500 Mal hinzuzufügen. Wenn jedoch die Anzahl der erstellten Elemente (Variable countFeatures) der maximal angegebenen Anzahl von Elementen (Variable maxFeatures) entspricht, unterbrechen wir diesen Zyklus.


Der erste Schritt in dieser Schleife besteht darin, das Element zu deklarieren, aus dem das neue Element erstellt wird. Wenn wir nur ein Element erstellt haben (den ersten Raum), ist es das ursprüngliche. Andernfalls wählen wir zufällig eines der bereits erstellten Elemente aus.


Nun wählen wir aus, an welcher Wand dieses Elements das neue Element erstellt werden soll.


Bitte beachten Sie, dass wir diese ChoseWall () -Funktion noch nicht haben. Lass es uns schnell schreiben. Gehen Sie zum Ende der Funktion und erstellen Sie sie. Es sollte eine Wand zurückgeben und ein Element als Argument verwenden, damit die Funktion die Wand dieses Elements auswählen kann.


Ich habe es zwischen den Funktionen CheckIfHasSpace () und DrawMap () erstellt. Beachten Sie, dass Sie bei der Arbeit in Visual Studio, das mit Unity installiert ist, die Felder - / + links verwenden können, um Teile des Codes zu reduzieren / zu erweitern und die Arbeit zu vereinfachen.

In dieser Funktion finden wir die Wand, aus der das Element noch nicht erstellt wurde. Manchmal erhalten wir Elemente mit einer oder mehreren Wänden, an denen bereits andere Elemente angebracht sind. Daher müssen wir immer wieder überprüfen, ob eine der zufälligen Wände frei ist. Dazu verwenden wir eine zehnmal wiederholte for-Schleife. Wenn nach diesen zehnmal keine freie Wand gefunden wird, gibt die Funktion null zurück.


Kehren Sie nun zur Funktion GenerateDungeon () zurück und übergeben Sie das ursprüngliche Element als Parameter an die Funktion ChoseWall ().


Die Zeile if (wall == null) continue;bedeutet, dass das ursprüngliche Element kein neues Element generieren kann, wenn die Wandsuchfunktion false zurückgegeben hat. Daher setzt die Funktion den Zyklus fort, dh sie konnte kein neues Element erstellen und fährt mit der nächsten Iteration des Zyklus fort.

Jetzt müssen wir den Typ für das nächste Element auswählen. Wenn das Quellelement ein Raum ist, muss das nächste ein Korridor sein (wir möchten nicht, dass der Raum direkt zu einem anderen Raum ohne einen Korridor zwischen ihnen führt). Wenn dies jedoch ein Korridor ist, müssen wir die Wahrscheinlichkeit schaffen, dass ein weiterer Korridor oder Raum als nächstes kommt.


Fein. Jetzt müssen wir nur noch die GenerateFeature () -Funktion aufrufen, die Wand übergeben und als Parameter eingeben.


Wechseln Sie schließlich zum Unity-Inspektor, wählen Sie das GameManager-Objekt aus und ändern Sie die Werte wie folgt:


Wenn Sie jetzt auf die Wiedergabetaste klicken, sehen Sie bereits die Ergebnisse!


Wie gesagt, dies ist nicht der beste Dungeon. Wir haben viele Sackgassen. Aber es ist voll funktionsfähig und garantiert, dass Sie keinen Raum haben, der mit keinem anderen verbunden ist.

Ich hoffe, dass es Ihnen gefallen hat! Im nächsten Beitrag erstellen wir einen Spieler, der sich durch den Dungeon bewegt, und verwandeln dann die Karte von ASCII in Sprite.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Post3 : MonoBehaviour {
    public int mapWidth;
    public int mapHeight;

    public int widthMinRoom;
    public int widthMaxRoom;
    public int heightMinRoom;
    public int heightMaxRoom;

    public int minCorridorLength;
    public int maxCorridorLength;
    public int maxFeatures;
    int countFeatures;

    public bool isASCII;

    public List<Feature> allFeatures;

    public void InitializeDungeon() {
        MapManager.map = new Tile[mapWidth, mapHeight];
    }

    public void GenerateDungeon() {
        GenerateFeature("Room", new Wall(), true);

        for (int i = 0; i < 500; i++) {
            Feature originFeature;

            if (allFeatures.Count == 1) {
                originFeature = allFeatures[0];
            }
            else {
                originFeature = allFeatures[Random.Range(0, allFeatures.Count - 1)];
            }

            Wall wall = ChoseWall(originFeature);
            if (wall == null) continue;

            string type;

            if (originFeature.type == "Room") {
                type = "Corridor";
            }
            else {
                if (Random.Range(0, 100) < 90) {
                    type = "Room";
                }
                else {
                    type = "Corridor";
                }
            }

            GenerateFeature(type, wall);

            if (countFeatures >= maxFeatures) break;
        }

        DrawMap(isASCII);
    }

    void GenerateFeature(string type, Wall wall, bool isFirst = false) {
        Feature room = new Feature();
        room.positions = new List<Vector2Int>();

        int roomWidth = 0;
        int roomHeight = 0;

        if (type == "Room") {
            roomWidth = Random.Range(widthMinRoom, widthMaxRoom);
            roomHeight = Random.Range(heightMinRoom, heightMaxRoom);
        }
        else {
            switch (wall.direction) {
                case "South":
                    roomWidth = 3;
                    roomHeight = Random.Range(minCorridorLength, maxCorridorLength);
                    break;
                case "North":
                    roomWidth = 3;
                    roomHeight = Random.Range(minCorridorLength, maxCorridorLength);
                    break;
                case "West":
                    roomWidth = Random.Range(minCorridorLength, maxCorridorLength);
                    roomHeight = 3;
                    break;
                case "East":
                    roomWidth = Random.Range(minCorridorLength, maxCorridorLength);
                    roomHeight = 3;
                    break;

            }
        }

        int xStartingPoint;
        int yStartingPoint;

        if (isFirst) {
            xStartingPoint = mapWidth / 2;
            yStartingPoint = mapHeight / 2;
        }
        else {
            int id;
            if (wall.positions.Count == 3) id = 1;
            else id = Random.Range(1, wall.positions.Count - 2);

            xStartingPoint = wall.positions[id].x;
            yStartingPoint = wall.positions[id].y;
        }

        Vector2Int lastWallPosition = new Vector2Int(xStartingPoint, yStartingPoint);

        if (isFirst) {
            xStartingPoint -= Random.Range(1, roomWidth);
            yStartingPoint -= Random.Range(1, roomHeight);
        }
        else {
            switch (wall.direction) {
                case "South":
                    if (type == "Room") xStartingPoint -= Random.Range(1, roomWidth - 2);
                    else xStartingPoint--;
                    yStartingPoint -= Random.Range(1, roomHeight - 2);
                    break;
                case "North":
                    if (type == "Room") xStartingPoint -= Random.Range(1, roomWidth - 2);
                    else xStartingPoint--;
                    yStartingPoint ++;
                    break;
                case "West":
                    xStartingPoint -= roomWidth;
                    if (type == "Room") yStartingPoint -= Random.Range(1, roomHeight - 2);
                    else yStartingPoint--;
                    break;
                case "East":
                    xStartingPoint++;
                    if (type == "Room") yStartingPoint -= Random.Range(1, roomHeight - 2);
                    else yStartingPoint--;
                    break;
            }
        }

         if (!CheckIfHasSpace(new Vector2Int(xStartingPoint, yStartingPoint), new Vector2Int(xStartingPoint + roomWidth - 1, yStartingPoint + roomHeight - 1))) {
            return;
        }

        room.walls = new Wall[4];

        for (int i = 0; i < room.walls.Length; i++) {
            room.walls[i] = new Wall();
            room.walls[i].positions = new List<Vector2Int>();
            room.walls[i].length = 0;

            switch (i) {
                case 0:
                    room.walls[i].direction = "South";
                    break;
                case 1:
                    room.walls[i].direction = "North";
                    break;
                case 2:
                    room.walls[i].direction = "West";
                    break;
                case 3:
                    room.walls[i].direction = "East";
                    break;
            }
        }

        for (int y = 0; y < roomHeight; y++) {
            for (int x = 0; x < roomWidth; x++) {
                Vector2Int position = new Vector2Int();
                position.x = xStartingPoint + x;
                position.y = yStartingPoint + y;

                room.positions.Add(position);

                MapManager.map[position.x, position.y] = new Tile();
                MapManager.map[position.x, position.y].xPosition = position.x;
                MapManager.map[position.x, position.y].yPosition = position.y;

                if (y == 0) {
                    room.walls[0].positions.Add(position);
                    room.walls[0].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (y == (roomHeight - 1)) {
                    room.walls[1].positions.Add(position);
                    room.walls[1].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (x == 0) {
                    room.walls[2].positions.Add(position);
                    room.walls[2].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (x == (roomWidth - 1)) {
                    room.walls[3].positions.Add(position);
                    room.walls[3].length++;
                    MapManager.map[position.x, position.y].type = "Wall";
                }
                if (MapManager.map[position.x, position.y].type != "Wall") {
                    MapManager.map[position.x, position.y].type = "Floor";
                }
            }
        }

        if (!isFirst) {
            MapManager.map[lastWallPosition.x, lastWallPosition.y].type = "Floor";
            switch (wall.direction) {
                case "South":
                    MapManager.map[lastWallPosition.x, lastWallPosition.y - 1].type = "Floor";
                    break;
                case "North":
                    MapManager.map[lastWallPosition.x, lastWallPosition.y + 1].type = "Floor";
                    break;
                case "West":
                    MapManager.map[lastWallPosition.x - 1, lastWallPosition.y].type = "Floor";
                    break;
                case "East":
                    MapManager.map[lastWallPosition.x + 1, lastWallPosition.y].type = "Floor";
                    break;
            }
        }

        room.width = roomWidth;
        room.height = roomHeight;
        room.type = type;
        allFeatures.Add(room);
        countFeatures++;
    }

    bool CheckIfHasSpace(Vector2Int start, Vector2Int end) {
        for (int y = start.y; y <= end.y; y++) {
            for (int x = start.x; x <= end.x; x++) {
                if (x < 0 || y < 0 || x >= mapWidth || y >= mapHeight) return false;
                if (MapManager.map != null) return false;
            }
        }

        return true;
    }

    Wall ChoseWall(Feature feature) {
        for (int i = 0; i < 10; i++) {
            int id = Random.Range(0, 100) / 25;
            if (!feature.walls[id].hasFeature) {
                return feature.walls[id];
            }
        }
        return null;
    }

    void DrawMap(bool isASCII) {
        if (isASCII) {
            Text screen = GameObject.Find("ASCIITest").GetComponent<Text>();

            string asciiMap = "";

            for (int y = (mapHeight - 1); y >= 0; y--) {
                for (int x = 0; x < mapWidth; x++) {
                    if (MapManager.map[x, y] != null) {
                        switch (MapManager.map[x, y].type) {
                            case "Wall":
                                asciiMap += "#";
                                break;
                            case "Floor":
                                asciiMap += ".";
                                break;
                        }
                    }
                    else {
                        asciiMap += " ";
                    }

                    if (x == (mapWidth - 1)) {
                        asciiMap += "\n";
                    }
                }
            }

            screen.text = asciiMap;
        }
    }
}

All Articles