Где я иду не так с моим алгоритмом лабиринта? - PullRequest
0 голосов
/ 10 апреля 2020

Я пытаюсь создать лабиринт из комнат и использую это руководство

This is what I am trying to achieve Это то, чего я пытаюсь достичь. Вместо этого я получаю это:

What I am achieving

Это то, что я сделал до сих пор (без ненужных частей)

    using UnityEngine;
using Random = UnityEngine.Random;
using UnityEngine.Tilemaps;

using System.Collections.Generic;
using System.Linq;

public class MazeAndRoomGenerator : MonoBehaviour
{
    TileType[,] levelWall;
    int[,] regions;
    int currentRegion = -1;
    enum TileType { Floor, Wall }


    // CONSTANTS
    readonly static int[] north = { 0, 1 };
    readonly static int[] south = { 0, -1 };
    readonly static int[] east = { 1, 0 };
    readonly static int[] west = { -1, 0 };
    readonly static int[] northEast = { 1, 1 };
    readonly static int[] northWest = { -1, 1 };
    readonly static int[] southEast = { 1, -1 };
    readonly static int[] southWest = { -1, -1 };
    readonly static int[][] northCells = { north, northEast, northWest };
    readonly static int[][] southCells = { south, southEast, southWest };
    readonly static int[][] eastCells = { east, northEast, southEast };
    readonly static int[][] westCells = { west, northWest, southWest };



    public void GenerateMaze()
    {
        // Loop through all cells in the level and grow the maze in all parts that aren't assigned yet
        for (int y = 0; y < mapSize; y += 2)
        {
            for (int x = 0; x < mapSize; x += 2)
            {
                if(levelWall[x,y] == TileType.Wall)
                {
                    GrowMaze(x, y);
                }
            }
        }
    }





    public void Carve(int x, int y)
    {
        levelWall[x,y] = TileType.Floor;
        regions[x, y] = currentRegion;
    }
    public bool CanCarve(int[] pos, int[] dir)
    {
        // Returns false if the cell is already taken or out of map bounds
        int x = pos[0] + dir[0] * 2;
        int y = pos[1] + dir[1] * 2;
        if (!InBounds(x, y)) { return false; }


        int[][] checkCells = null;
        if      (dir == north) { checkCells = northCells; }
        else if (dir == south) { checkCells = southCells; }
        else if (dir == east)  { checkCells = eastCells; }
        else if (dir == west)  { checkCells = westCells; }
        else { Debug.LogError("Incorrect direction inputted"); }

        foreach (int[] checkCell in checkCells)
        {
            int[] cell = { pos[0] + checkCell[0], pos[1] + checkCell[1] };
            if (CanCarve(cell))
            {
                return false;
            }
        }

        // All of the surrounding walls are available so return true
        return true;
    }
    public bool CanCarve(int[] pos)
    {
        // Returns false if the cell is already taken or out of map bounds

        int x = pos[0];
        int y = pos[1];

        // Checking if map is out of bounds
        if (!InBounds(x, y)) 
        { 
            return false; 
        }


        // return True if the cell is a wall (1)
        // false if the cell is a floor (0)
        return (levelWall[x, y] == TileType.Wall);
    }
    public bool InBounds(int x, int y)
    {
        // Checking if map is out of bounds
        if (!(0 < x) || !(x < mapSize) ||
            !(0 < y) || !(y < mapSize))
        {
            return false;
        }
        else return true;
    }
    public void GrowMaze(int startX, int startY)
    {
        /*
         * RULES: 
         *  If any of the neighbour cells to start point (CanCarve == false) are floor then stop.
         *  Take a random available direction and start carving.
         *  For each cell that is carved first check if the cell in front of it (travelling in the same direction)
         *  and the cells to the left and right of the cell is carvable. 
         *  If isn't then remove that direction from available directions and pick new direction from original cell.
         *  Repeat until no available directions left
         */


        int[][] directions = { north, south, east, west };
        int[][] neighbourCells = { north, south, east, west, northEast, northWest, southEast, southWest };

        int[] start = { startX, startY };
        List<int[]> cells = new List<int[]>();
        int[] lastDirection = null;

        // Check if starting point is valid
        foreach (int[] direction in neighbourCells)
        {
            int[] checkCell = { start[0] + direction[0], start[1] + direction[1] };
            if (!CanCarve(checkCell))
            {
                // Throw out start cell and don't start maze from there
                return;
            }
        }

        // Start a new region for the new maze region
        StartRegion();
        Carve(start[0], start[1]);
        cells.Add(start);


        // While there are available cells to travel to run script
        while (cells.Count > 0 && cells.Count < 10000)
        {
            int[] cell = cells[cells.Count - 1];

            List<int[]> unmadeCells = new List<int[]>();

            foreach (int[] direction in directions)
            {
                int[] checkCell = { cell[0] + direction[0], cell[1] + direction[1] };
                if (CanCarve(checkCell, direction))
                {
                    unmadeCells.Add(direction);
                }
            }

            // If there are available cells to travel to run script
            if (unmadeCells.Count > 0)
            {
                // Prefer to continue in the last direction travelling if available
                // Random chance for it to choose a different direction
                int[] direction;

                if (unmadeCells.Contains(lastDirection) 
                    && (Random.value > (windingChance/100)) )
                {
                    direction = lastDirection;
                }
                else
                {
                    direction = unmadeCells[Random.Range(0, unmadeCells.Count)];
                }

                int[] newCell;
                newCell = new int[] { cell[0] + direction[0], cell[1] + direction[1] };
                Carve(newCell[0], newCell[1]);
                // Adds new cell onto stack and script will repeat with this cell until it has no possible directions to travel
                cells.Add(newCell);

                lastDirection = direction;
            }
            else
            {
                cells.RemoveAt(cells.Count - 1);
                lastDirection = null;
            }
        }
    }


}

У меня есть идея, что это как-то связано с тем, что массив cells продолжает увеличиваться и поэтому застрял в al oop, поэтому я добавил ограничение на количество ячеек, в то время как l oop для отладки.

Мои правила для лабиринта таковы:

  • Если любая из соседних ячеек для начальной точки (CanCarve == false) имеет нижний этаж, то остановитесь.
  • Выберите произвольно доступное направление и начните вырезать.
  • Для каждой вырезанной ячейки сначала проверьте, находится ли ячейка перед ней (движется в том же направлении) и ячейки слева и право на клетку резное. Если это не так, удалите это направление из доступных направлений и выберите новое направление из исходной ячейки.
  • Повторяйте, пока не осталось доступных направлений

Я был бы очень признателен за любую помощь. Я рвал на себе волосы:)

Ответы [ 2 ]

0 голосов
/ 12 апреля 2020

Причина, по которой генерируется клетчатый узор, заключается в том, что ячейка pos не проверяется в функции CanCarve.

0 голосов
/ 10 апреля 2020

Вы увеличиваете x и y на 2 каждое изменение, поэтому конечно вы получите клетчатый узор.

Как правило, вы должны ограничить петли до ArrayYouAreWorkingWith.Lenght , Используйте MapSize при инициализации массива - таким образом, циклы - это будущее. Лично мне очень не нравятся двухмерные массивы, так как трудно определить границы одного измерения. Вместо этого я использую неровные массивы, но basi c все еще работает. Но это больше предпочтение, чем настоящее правило.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...