Генератор подземелий

Компонент
Предназначение:
Дизайн уровней
Процедурный генератор подземелий на основе TinyKeep и этой статьи.
Код генератора подземелий
using UnityEngine;
using System.Collections.Generic;
using UnityEngine.SceneManagement;

// Room IDs start at 10
public enum TileType
{
    Nothing = 0,
    Hallway = 1,
    Wall = 2,
    Door = 3
}

public class DungeonGenerator : MonoBehaviour
{
    [SerializeField]
    [Range(10, 1000)]
    int RoomCount = 300;

    [SerializeField]
    [Range(1, 500)]
    int Radius = 50;

    [SerializeField]
    [Range(0, 2)]
    float MainRoomFrequency = 1f;

    [SerializeField]
    [Range(0, 1)]
    float RoomConnectionFrequency = 0.15f;

    int[,] Grid;
    GameObject DungeonMapTexture;

    RoomGenerator RoomGenerator;

    List<int> PrimaryRoomIDs;
    List<int> SecondaryRoomIDs;
    List<Room> Rooms;

    void Awake()
    {
        Init();
    }

    void Init()
    {
        //Visual representation of the map data
        DungeonMapTexture = transform.Find("DungeonMapTexture").gameObject;

        RoomGenerator = transform.Find("RoomGenerator").GetComponent<RoomGenerator>();
        RoomGenerator.OnRoomsGenerated += RoomGenerator_OnRoomsGenerated;

        //Generates rooms and room connections
        RoomGenerator.Generate(RoomCount, Radius, MainRoomFrequency, RoomConnectionFrequency);
    }

    void RoomGenerator_OnRoomsGenerated()
    {
        //Create a copy of all Active rooms
        Rooms = RoomGenerator.Rooms;

        //Convert rooms into a grid of integers
        CreateGrid();
        AddWalls();

        //Draw a texture representation of our dungeon data
        DungeonMapTexture.GetComponent<MeshRenderer>().material.mainTexture = CreateMapTexture();
        DungeonMapTexture.transform.localScale = new Vector2(Grid.GetLength(0), Grid.GetLength(1));

        //RoomGenerator.ClearData ();
    }

    void Update()
    {
        //Generate a new dungeon
        if (Input.GetKeyDown(KeyCode.Space))
            SceneManager.LoadScene("main");
    }

    void CreateGrid()
    {
        PrimaryRoomIDs = new List<int>();
        SecondaryRoomIDs = new List<int>();

        //Get our boundaries and list of primary and secondary room IDs
        for (int n = 0; n < Rooms.Count; n++)
        {
            if (Rooms[n].IsVisible)
            {
                if (Rooms[n].IsMainRoom)
                {
                    PrimaryRoomIDs.Add(Rooms[n].ID);
                }
                else
                {
                    SecondaryRoomIDs.Add(Rooms[n].ID);
                }
            }
        }

        //Add padding for walls
        int width = (int)(RoomGenerator.XMax - RoomGenerator.XMin) + 2;
        int height = (int)(RoomGenerator.YMax - RoomGenerator.YMin) + 2;

        Grid = new int[width, height];

        //Create initial grid with room IDs
        for (int n = 0; n < Rooms.Count; n++)
        {
            if (Rooms[n].IsVisible)
            {
                int startx = (int)Rooms[n].TopLeft.x - (int)RoomGenerator.XMin;
                int starty = (int)Rooms[n].BottomRight.y - (int)RoomGenerator.YMin;
                int endx = startx + (int)Rooms[n].transform.localScale.x;
                int endy = starty + (int)Rooms[n].transform.localScale.y;

                for (int x = startx; x < endx; x++)
                {
                    for (int y = starty; y < endy; y++)
                    {
                        Grid[x + 1, y + 1] = Rooms[n].ID;
                    }
                }
            }
        }

        //Complete missing sections of map based on LineCasts created in Room Generator
        for (int n = 0; n < Rooms.Count; n++)
        {
            if (Rooms[n].IsMainRoom)
            {
                for (int m = 0; m < Rooms[n].Connections.Count; m++)
                {

                    Vector2 p0 = Rooms[n].Connections[m].Line1.p0.Value;
                    Vector2 p1 = Rooms[n].Connections[m].Line1.p1.Value;
                    Vector2 p2 = Rooms[n].Connections[m].Line2.p0.Value;
                    Vector2 p3 = Rooms[n].Connections[m].Line2.p1.Value;

                    //flip values if line is going in opposite direction
                    if ((int)p0.x > (int)p1.x || (int)p0.y > (int)p1.y)
                    {
                        p0 = Rooms[n].Connections[m].Line1.p1.Value;
                        p1 = Rooms[n].Connections[m].Line1.p0.Value;
                    }
                    if ((int)p2.x > (int)p3.x || (int)p2.y > (int)p3.y)
                    {
                        p3 = Rooms[n].Connections[m].Line2.p0.Value;
                        p2 = Rooms[n].Connections[m].Line2.p1.Value;
                    }

                    //Adjust lines to grid coordinates
                    p0 = new Vector2(p0.x - RoomGenerator.XMin, p0.y - RoomGenerator.YMin);
                    p1 = new Vector2(p1.x - RoomGenerator.XMin, p1.y - RoomGenerator.YMin);
                    p2 = new Vector2(p2.x - RoomGenerator.XMin, p2.y - RoomGenerator.YMin);
                    p3 = new Vector2(p3.x - RoomGenerator.XMin, p3.y - RoomGenerator.YMin);

                    //Create the hallways in our grid
                    AddHallway(p0, p1);
                    AddHallway(p2, p3);
                }
            }
        }
    }

    void AddHallway(Vector2 p0, Vector2 p1)
    {
        //Vertical direction
        if ((int)p1.x == (int)p0.x)
        {
            for (int y = (int)p0.y; y < (int)p1.y; y++)
            {

                //if the tile is nothing then make it a hallway
                if (Grid[(int)p1.x, y] == (int)TileType.Nothing)
                {
                    Grid[(int)p1.x, y] = (int)TileType.Hallway;
                }

                //make hallways 3 units wide
                if ((int)p1.x < Grid.GetLength(0) - 2)
                {
                    if (Grid[(int)p1.x + 1, y] == (int)TileType.Nothing)
                    {
                        Grid[(int)p1.x + 1, y] = (int)TileType.Hallway;
                    }
                    if (Grid[(int)p1.x + 2, y] == (int)TileType.Nothing)
                    {
                        Grid[(int)p1.x + 2, y] = (int)TileType.Hallway;
                    }
                }
                else
                {
                    if (Grid[(int)p1.x - 1, y] == (int)TileType.Nothing)
                    {
                        Grid[(int)p1.x - 1, y] = (int)TileType.Hallway;
                    }
                    if (Grid[(int)p1.x - 2, y] == (int)TileType.Nothing)
                    {
                        Grid[(int)p1.x - 2, y] = (int)TileType.Hallway;
                    }
                }
            }
        }

        //Horizontal direction
        else if ((int)p1.y == (int)p0.y)
        {
            for (int x = (int)p0.x; x < (int)p1.x; x++)
            {

                //if the tile is nothing then make it a hallway
                if (Grid[x, (int)p1.y] == (int)TileType.Nothing)
                {
                    Grid[x, (int)p1.y] = (int)TileType.Hallway;
                }
                //make hallways 3 units wide
                if ((int)p1.y < Grid.GetLength(1) - 2)
                {
                    if (Grid[x, (int)p1.y + 1] == (int)TileType.Nothing)
                    {
                        Grid[x, (int)p1.y + 1] = (int)TileType.Hallway;
                    }
                    if (Grid[x, (int)p1.y + 2] == (int)TileType.Nothing)
                    {
                        Grid[x, (int)p1.y + 2] = (int)TileType.Hallway;
                    }

                }
                else
                {
                    if (Grid[x, (int)p1.y - 1] == (int)TileType.Nothing)
                    {
                        Grid[x, (int)p1.y - 1] = (int)TileType.Hallway;
                    }
                    if (Grid[x, (int)p1.y - 2] == (int)TileType.Nothing)
                    {
                        Grid[x, (int)p1.y - 2] = (int)TileType.Hallway;
                    }
                }
            }
        }
    }

    void AddWalls()
    {
        //Create a copy of current grid
        int[,] gridCopy = new int[Grid.GetLength(0), Grid.GetLength(1)];
        for (int x = 0; x < Grid.GetLength(0); x++)
        {
            for (int y = 0; y < Grid.GetLength(1); y++)
            {
                gridCopy[x, y] = Grid[x, y];
            }
        }

        //Process
        for (int x = 0; x < Grid.GetLength(0); x++)
        {
            for (int y = 0; y < Grid.GetLength(1); y++)
            {
                int val = gridCopy[x, y];

                //walls for primary rooms
                if (PrimaryRoomIDs.Contains(val))
                {
                    if (x > 0 && gridCopy[x - 1, y] != val && gridCopy[x - 1, y] != (int)TileType.Wall)
                    {
                        Grid[x - 1, y] = (int)TileType.Wall;
                    }
                    else if (x < gridCopy.GetLength(0) - 1 && gridCopy[x + 1, y] != val && gridCopy[x + 1, y] != (int)TileType.Wall)
                    {
                        Grid[x + 1, y] = (int)TileType.Wall;
                    }

                    if (y > 0 && gridCopy[x, y - 1] != val && gridCopy[x, y - 1] != (int)TileType.Wall)
                    {
                        Grid[x, y - 1] = (int)TileType.Wall;
                    }
                    else if (y < Grid.GetLength(1) - 1 && gridCopy[x, y + 1] != val && gridCopy[x, y + 1] != (int)TileType.Wall)
                    {
                        Grid[x, y + 1] = (int)TileType.Wall;
                    }
                }

                //Outside borders
                if (val == 0)
                {
                    if (x > 0 && gridCopy[x - 1, y] != (int)TileType.Nothing && gridCopy[x - 1, y] != (int)TileType.Wall)
                    {
                        Grid[x, y] = (int)TileType.Wall;
                    }
                    else if (x < Grid.GetLength(0) - 1 && gridCopy[x + 1, y] != (int)TileType.Nothing && gridCopy[x + 1, y] != (int)TileType.Wall)
                    {
                        Grid[x, y] = (int)TileType.Wall;
                    }

                    if (y > 0 && gridCopy[x, y - 1] != (int)TileType.Nothing && gridCopy[x, y - 1] != (int)TileType.Wall)
                    {
                        Grid[x, y] = (int)TileType.Wall;
                    }
                    else if (y < Grid.GetLength(1) - 1 && gridCopy[x, y + 1] != (int)TileType.Nothing && gridCopy[x, y + 1] != (int)TileType.Wall)
                    {
                        Grid[x, y] = (int)TileType.Wall;
                    }
                }
            }
        }

        //update grid copy
        for (int x = 0; x < Grid.GetLength(0); x++)
        {
            for (int y = 0; y < Grid.GetLength(1); y++)
            {
                gridCopy[x, y] = Grid[x, y];
            }
        }

        //Add doors based on connecting lines
        for (int n = 0; n < Rooms.Count; n++)
        {
            if (Rooms[n].IsMainRoom)
            {

                for (int m = 0; m < Rooms[n].Connections.Count; m++)
                {

                    //Line 1
                    Vector2 p0 = Rooms[n].Connections[m].Line1.p0.Value;
                    Vector2 p1 = Rooms[n].Connections[m].Line1.p1.Value;

                    if ((int)p0.x > (int)p1.x || (int)p0.y > (int)p1.y)
                    {
                        p1 = Rooms[n].Connections[m].Line1.p0.Value;
                        p0 = Rooms[n].Connections[m].Line1.p1.Value;
                    }
                    p0 = new Vector2(p0.x - RoomGenerator.XMin, p0.y - RoomGenerator.YMin);
                    p1 = new Vector2(p1.x - RoomGenerator.XMin, p1.y - RoomGenerator.YMin);

                    AddDoor(p0, p1);

                    //Line 2
                    p0 = Rooms[n].Connections[m].Line2.p0.Value;
                    p1 = Rooms[n].Connections[m].Line2.p1.Value;

                    if ((int)p0.x > (int)p1.x || (int)p0.y > (int)p1.y)
                    {
                        p1 = Rooms[n].Connections[m].Line2.p0.Value;
                        p0 = Rooms[n].Connections[m].Line2.p1.Value;
                    }
                    p0 = new Vector2(p0.x - RoomGenerator.XMin, p0.y - RoomGenerator.YMin);
                    p1 = new Vector2(p1.x - RoomGenerator.XMin, p1.y - RoomGenerator.YMin);

                    AddDoor(p0, p1);
                }
            }
        }
    }

    void AddDoor(Vector2 p0, Vector2 p1)
    {

        //Vertical direction
        if ((int)p1.x == (int)p0.x)
        {
            for (int y = (int)p0.y; y < (int)p1.y; y++)
            {

                if ((int)p1.x < Grid.GetLength(0) - 2)
                {
                    if ((Grid[(int)p1.x, y] == (int)TileType.Wall) && (Grid[(int)p1.x + 1, y] == (int)TileType.Wall) && (Grid[(int)p1.x + 2, y] == (int)TileType.Wall))
                    {
                        Grid[(int)p1.x, y] = (int)TileType.Door;
                        Grid[(int)p1.x + 1, y] = (int)TileType.Door;
                        Grid[(int)p1.x + 2, y] = (int)TileType.Door;
                    }
                }
                else
                {
                    if ((Grid[(int)p1.x, y] == (int)TileType.Wall) && (Grid[(int)p1.x - 1, y] == (int)TileType.Wall) && (Grid[(int)p1.x - 2, y] == (int)TileType.Wall))
                    {
                        Grid[(int)p1.x, y] = (int)TileType.Door;
                        Grid[(int)p1.x - 1, y] = (int)TileType.Door;
                        Grid[(int)p1.x - 2, y] = (int)TileType.Door;
                    }
                }
            }
        }

        //Horizontal direction
        else if ((int)p1.y == (int)p0.y)
        {
            for (int x = (int)p0.x; x < (int)p1.x; x++)
            {

                if ((int)p1.y < Grid.GetLength(1) - 2)
                {

                    if ((Grid[x, (int)p1.y] == (int)TileType.Wall) && (Grid[x, (int)p1.y + 1] == (int)TileType.Wall) && (Grid[x, (int)p1.y + 2] == (int)TileType.Wall))
                    {
                        Grid[x, (int)p1.y] = (int)TileType.Door;
                        Grid[x, (int)p1.y + 1] = (int)TileType.Door;
                        Grid[x, (int)p1.y + 2] = (int)TileType.Door;
                    }

                }
                else
                {
                    if ((Grid[x, (int)p1.y] == (int)TileType.Wall) && (Grid[x, (int)p1.y - 1] == (int)TileType.Wall) && (Grid[x, (int)p1.y - 2] == (int)TileType.Wall))
                    {
                        Grid[x, (int)p1.y] = (int)TileType.Door;
                        Grid[x, (int)p1.y - 1] = (int)TileType.Door;
                        Grid[x, (int)p1.y - 2] = (int)TileType.Door;
                    }
                }
            }
        }
    }

    Texture2D CreateMapTexture()
    {
        int width = Grid.GetLength(0);
        int height = Grid.GetLength(1);

        var texture = new Texture2D(width, height);
        var pixels = new Color[width * height];

        for (var x = 0; x < width; x++)
        {
            for (var y = 0; y < height; y++)
            {
                Color color = Color.white;

                if (PrimaryRoomIDs.Contains(Grid[x, y]))
                {
                    color = new Color(255 / 255f, 150 / 255f, 50 / 255f);
                }
                else if (SecondaryRoomIDs.Contains(Grid[x, y]))
                {
                    color = new Color(255 / 255f, 215 / 255f, 70 / 255f);
                }
                else if (Grid[x, y] == 1)
                {
                    color = Color.red;
                }
                else if (Grid[x, y] == 2)
                {
                    color = Color.black;
                }
                else if (Grid[x, y] == 3)
                {
                    color = Color.magenta;
                }

                pixels[x + y * width] = color;
            }
        }

        texture.SetPixels(pixels);
        texture.wrapMode = TextureWrapMode.Clamp;
        texture.filterMode = FilterMode.Point;
        texture.Apply();
        return texture;
    }
}
Расчеты минимального остовного дерева были выполнены с помощью триангуляции Делоне.
`
LOADING AD...