【问题标题】:Where am I going wrong with my maze algorithm?我的迷宫算法哪里出错了?
【发布时间】:2020-04-10 18:23:46
【问题描述】:

我正在尝试生成一个带有房间的迷宫,我正在使用这个guide

这就是我想要达到的目标。相反,我得到了这个:

这是我到目前为止所做的(去掉了不必要的部分)

    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 数组不断增加有关,因此卡在一个循环中,这就是为什么我在 while 循环中添加了对单元格数量的限制以进行调试。

我的迷宫规则是:

  • 如果到起点的任何相邻单元格 (CanCarve == false) 是 地板然后停止。
  • 随机选择可用方向并开始雕刻。
  • 对于每个被雕刻的单元格,首先检查其前面的单元格(沿相同方向移动)以及单元格左侧和右侧的单元格是否可雕刻。如果不是,则从可用方向中删除该方向并从原始单元格中选择新方向。
  • 重复直到没有可用的路线为止

我非常感谢任何帮助。我一直在为此扯头发:)

【问题讨论】:

  • 好问题。呃...我很确定这与我更改原始算法之前的工作方式有关。不幸的是,刚刚修复它并不能解决我的问题:(
  • 我看了源码,看不到“_connectRegions();”和“_removeDeadEnds();”代码中的方法,你错过了吗?
  • 还没有达到源代码的那部分。这些功能还不会影响我正在做的事情。
  • 填充(Tiles.wall)?是否缺少一些代码,看不到初始化,所有这些地板方块都在这里有问题吗?
  • 这实际上不在我的代码中。我意识到您正在谈论我链接的源代码。它将关卡数组中的每个元素初始化为我假设的 Tiles.wall。那不是我的代码。

标签: c# unity3d procedural-generation


【解决方案1】:

您每次迭代都会将xy 增加2,所以当然你会得到一个方格图案。

通常您应该将 for 循环限制为 ArrayYouAreWorkingWith.Lenght。初始化数组时使用 MapSize - 这样循环是未来的证明。个人而言,我真的不喜欢使用二维数组,因为很难弄清楚一维的界限。我改用锯齿状数组,但基本仍然有效。但这更像是一种偏好,而不是真正的规则。

【讨论】:

  • 感谢您的提示 :) 我会这样做的。不幸的是,每次迭代将 x 和 y 增加 2 不是问题。起始单元仍在被雕刻出来(如方格图案所示),但我正在尝试解决为什么迷宫没有从起始单元继续。
  • @LiamBoreback 原始示例中的任何内容都不需要您像这样批量设置数组值。除了初始化所有单元格之外,我不知道你为什么还要像这样遍历整个数组。
  • 你还在谈论GenerateMaze函数吗?如果是这样,您能否详细说明批量数组值的含义?我在每个可用的单元格上调用我的迷宫算法。
【解决方案2】:

之所以会生成棋盘格图案,是因为CanCarve函数中没有检查pos单元格。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-25
    • 2015-06-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多