程序洞穴生成四(Procedural Cave Generation)

视频原址:https://www.youtube.com/watch?v=xYOG8kH2tF8&list=PLFt_AvWsXl0eZgMK_DT5_biRkWXftAOf9&index=5

本次视频的内容是优化地图。先那之前生成的效果来看。

这张生成的随机的地图中可以从圈出来的位置看到这些细小的点占用的地方非常的小使得整个生成的地图有点不自然。或者会形成一些小的封闭的地形。所以本次就去实现将这些细小的位置移除或者填充成墙。

要找到这些区域那么我们就得先知道哪些地图信息是在这些细小的区域上的。这里就需要用到流场算法(Flow Field, 不知道这么说对不对,如有不对希望不要责怪。),下面用视频的图来解释一下。

中间的一片灰白色块是相同的类型的区域地图块,那么如何将这些所有的灰白色的地图块拿出来放在一个列表中呢?定义灰白色的tileType = 0, 黑色tileType= 1首先我们需要一个队列(Queue)queue来存所有正在被检查的方块和已经检查完确定是灰白色区域中的列表(List)tileList。我们拿到第一个点,并将该点放进队列queue中,然后记录该点的tileType,并且将mapFlag(定义的一个变量用来判定当前的点是否已经在队列中被拿来检查过是否是区域中的点, 0为未判定过)设置为1,然后用一个while循环来遍历,队列,只要队列中还有值,那么就一个一个出列来判定。拿到出队列的点的信息先将点存进tileList中,因为只有满足条件的才会进入队列,然后拿到该点周围的四个点(图中红色的区块),判定如果是mapFlag = 0(未判定过)并且tileType与记录的相同,那么就将点放进队列queue中并且将mapFlag设置为1,此时队列的数量大于0,所以循环继续,知道队列为0 的时候,此时tileList中就是存着所有的灰白色区域中所有的地图信息。

由于是操作的地图的信息,所以本次修改的MapGenerator脚本。修改的内容:

由于原本的地图的信息是存在一个二维数组中,为了能一个一个遍历地图并获取到地图信息,就定义了一个结构体用来存放地图的信息的xy坐标。可以通过tilex和tiley从二维数组的地图信息中拿到地图数据。

    /// <summary>
    /// 坐标结构体
    /// </summary>
    struct Coord
    {
        public int tileX;
        public int tileY;

        public Coord(int x, int y)
        {
            tileX = x;
            tileY = y;
        }
    }

其他的代码就放下面。

    /// <summary>
    /// 处理优化地图信息
    /// </summary>
    void ProcessMap()
    {
        //传入类型1(墙)获取到所有的墙的一块一块的区域的信息
        List<List<Coord>> wallRegions = GetRegions(1);

        int wallThresholdSize = 50;          //阈值
        foreach (List<Coord> wallRegion in wallRegions)
        {
            //设定一个阈值,如果这个区域块内的地图信息的数量小于该阈值就将这块地方移除设置成地面
            if (wallRegion.Count < wallThresholdSize)
            {
                foreach (Coord tile in wallRegion)
                {
                    map[tile.tileX, tile.tileY] = 0;
                }
            }
        }

        //传入类型0(Room)获取到所有的地面的一块一块的区域的信息
        List<List<Coord>> roomRegions = GetRegions(1);

        int roomThresholdSize = 50;
        foreach (List<Coord> roomRigion in roomRegions)
        {
            //设定一个阈值,如果这个区域块内的地图信息的数量小于该阈值就将这块地方移除设置成墙
            if (roomRigion.Count < roomThresholdSize)
            {
                foreach (Coord tile in roomRigion)
                {
                    map[tile.tileX, tile.tileY] = 1;
                }
            }
        }
    }

    /// <summary>
    /// 获取所有的区域块的列表
    /// </summary>
    /// <param name="tileType"></param>
    /// <returns></returns>
    List<List<Coord>> GetRegions(int tileType)
    {
        List<List<Coord>> regions = new List<List<Coord>>();

        int[,] mapFlags = new int[width, height];
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                if (mapFlags[x, y] == 0 && map[x, y] == tileType)
                {
                    List<Coord> newRegion = GetRigionTiles(x, y);

                    regions.Add(newRegion);
                    foreach (Coord tile in newRegion)
                    {
                        mapFlags[tile.tileX, tile.tileY] = 1;
                    }
                }
            }
        }

        return regions;
    }

    /// <summary>
    /// 获取到区域中的所有的方块
    /// </summary>
    /// <param name="startX"></param>
    /// <param name="startY"></param>
    /// <returns></returns>
    List<Coord> GetRigionTiles(int startX, int startY)
    {
        List<Coord> tiles = new List<Coord>();
        int[,] mapFlags = new int[width, height];       //对应地图数据,记录该信息是否已经被计算过
        int tileType = map[startX, startY];             //获取传进来的当前的方块的数据类型    1 - wall 0 - room

        Queue<Coord> queue = new Queue<Coord>();
        queue.Enqueue(new Coord(startX, startY));       //将第一个样本区域放进队列中
        mapFlags[startX, startY] = 1;

        //循环
        while (queue.Count > 0)
        {
            Coord tile = queue.Dequeue();
            tiles.Add(tile);

            for (int x = tile.tileX - 1; x <= tile.tileX + 1; x++)
            {
                for (int y = tile.tileY - 1; y <= tile.tileY + 1; y++)
                {
                    //获取四个方向的方块,排除对角的临近的方块
                    if (IsInMapRange(x, y) && (y == tile.tileY || x == tile.tileX))
                    {
                        //如果flag为1没有判定过并且类型符合,就如队列
                        if (mapFlags[x, y] == 0 && map[x, y] == tileType)
                        {
                            mapFlags[x, y] = 1;
                            queue.Enqueue(new Coord(x, y));
                        }
                    }
                }
            }
        }


        return tiles;
    }
    
    /// <summary>
    /// 传进来的坐标是否在地图内
    /// </summary>
    /// <param name="x"></param>
    /// <param name="y"></param>
    /// <returns></returns>
    bool IsInMapRange(int x, int y)
    {
        return x >= 0 && x < width && y >= 0 && y < height;
    }

最后只需要设定好阈值并在在SmoothMap之后,创建地图边界前调用ProcessMap函数就可以了。

下面给一张上面地图的对比图,比较小的区块都已经被移除了。

 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值