Unity 随机房间地图生成

  • 2020 年 3 月 31 日
  • 筆記

无论是在迷宫还是类似于地牢的游戏地图中,利用程序来生成每次都不一样的地图是一件叫人兴奋不已的事。

这时我们需要解决两个非常重要的随机事件:

1.在一定范围内随机出各不相同但又不能互相重叠的房间

2.优美生成连接这些房间的通道

 

基本的UML思路图:

 

 

这次我们先讨论如何快速生成符合各种随机要求的房间。

一般来说,一个房间的高度是一个相对固定的值,可以根据面板上的参数进行必要的调整,而真正参与随机的应该是房间的长,宽和位置。

 

建立房间的数据结构,根据需求可以随时补充和添加:

 1 using System.Collections.Generic;   2 using UnityEngine;   3   4 public class RoomData   5 {   6     public int Id;   7     //房间的Transform等属性   8     public RoomTran RoomTran;   9     //该房间的战斗类型  10     public RoomBattleType BattleType;  11     //该房间与哪些其余房间互相连接  12     public List<RoomData> CrossRooms;  13     //房间内的怪物列表  14     public List<GameObject> Monsters;  15     //是否是端点房间  16     public bool bEndRoom;  17     //是否是主路径房间  18     public bool bMainCrossRoom;  19 }  20  21 public class RoomTran  22 {  23     public int Length;  24     public int Width;  25     //长宽中心点  26     public Vector2Int CenterPos;  27     //高度位置  28     public float PosY;  29 }  30  31 public enum RoomBattleType  32 {  33     Rest,  34     NormalBattle,  35     BossBattle  36 }

 

RoonBuilder属性和控制参数:

 1     //建筑单位方块   2     public GameObject BuildUnit;   3   4     //房间高度值   5     public int FixedUnitHeight;   6     //生成的房间层数   7     public int LayerCount;   8     //长宽随机范围   9     public Vector2Int GenRange;  10  11     //随机类型  12     public RoomRandType RandType;  13     //随机的房间形状类型  14     public RoomShapeType Shape;  15  16     //房间大小的随机列表,用于枚举随机  17     public List<Vector2Int> RoomRandSizes = new List<Vector2Int>();  18  19     //随机的房间最大面积  20     public int MaxRoomArea;  21     //最大随机数量(随机试验次数)  22     public int MaxRoomCount;  23  24     //最小边长度  25     private int MinRoomEdge;  26     //最大长宽比  27     public int MaxLengthWidthScale = 2;  28  29     //标准方向  30     Vector3Int Dx = new Vector3Int(1, 0, 0);  31     Vector3Int Dy = new Vector3Int(0, 1, 0);  32     Vector3Int Dz = new Vector3Int(0, 0, 1);  33  34     //建筑单位标签  35     const string S_TAG = "Unit";  36  37     private MapSystem MapManager;

 

单房间轮廓生成:

 1     /// <summary>   2     /// 生成单一房间的轮廓   3     /// </summary>   4     /// <param name="centerPos">房间中点位置</param>   5     /// <param name="length"></param>   6     /// <param name="width"></param>   7     /// <param name="parent">父物体</param>   8     void GenOneRoom(Vector3 centerPos, int length, int width, Transform parent = null)   9     {  10         var to = new Vector3(length - 1, FixedUnitHeight - 1, width - 1) * .5f;  11  12         //顶点  13         var ned = centerPos - to;  14         var fod = centerPos + to;  15  16         var v3 = new Vector3(ned.x, fod.y, ned.z);  17         var v4 = new Vector3(ned.x, fod.y, fod.z);  18         var v5 = new Vector3(ned.x, ned.y, fod.z);  19  20         var v6 = new Vector3(fod.x, ned.y, ned.z);  21         var v7 = new Vector3(fod.x, ned.y, fod.z);  22         var v8 = new Vector3(fod.x, fod.y, ned.z);  23  24         //顶点位置(8个)  25         InsSetPos(ned, parent);  26         InsSetPos(fod, parent);  27         InsSetPos(v3, parent);  28         InsSetPos(v4, parent);  29         InsSetPos(v5, parent);  30         InsSetPos(v6, parent);  31         InsSetPos(v7, parent);  32         InsSetPos(v8, parent);  33  34         //12条棱(4*3)  35         //  36         InsOneEdge(length, ned, Dx, parent);  37         InsOneEdge(length, v3, Dx, parent);  38         InsOneEdge(length, v4, Dx, parent);  39         InsOneEdge(length, v5, Dx, parent);  40         //  41         InsOneEdge(FixedUnitHeight, ned, Dy, parent);  42         InsOneEdge(FixedUnitHeight, v5, Dy, parent);  43         InsOneEdge(FixedUnitHeight, v6, Dy, parent);  44         InsOneEdge(FixedUnitHeight, v7, Dy, parent);  45         //  46         InsOneEdge(width, ned, Dz, parent);  47         InsOneEdge(width, v3, Dz, parent);  48         InsOneEdge(width, v6, Dz, parent);  49         InsOneEdge(width, v8, Dz, parent);  50     }  51  52     //生成一条边上的建筑单位但不包含顶点位置  53     void InsOneEdge(int edge, Vector3 v, Vector3 dir, Transform parent = null)  54     {  55         //忽略首尾单位  56         for (int i = 1; i < edge - 1; i++)  57         {  58             InsSetPos(v + i * dir, parent);  59         }  60     }  61  62     void InsSetPos(Vector3 pos, Transform parent = null)  63     {  64         var ins = Instantiate(BuildUnit);  65         ins.transform.position = pos;  66         ins.transform.parent = parent;  67     }

这里唯一值得注意的地方是房间顶点位置的单位不要重复生成。(因为想偷懒的话真的很容易重复Orz)。

 

随机RoonTran结构:

 1     RoomTran RanRoomTran(Vector3 centerPos)   2     {   3         var rt = new RoomTran();   4   5         switch (RandType)   6         {   7             case RoomRandType.AllRand:   8                 int temp;   9                 var oe = MaxRoomArea / MinRoomEdge;  10                 switch (Shape)  11                 {  12                     case RoomShapeType.LengthMain:  13                         rt.Length = Random.Range(MinRoomEdge + 1, oe + 1);  14                         temp = MaxRoomArea / rt.Length;  15                         if (temp >= rt.Length)  16                             rt.Width = Random.Range(MinRoomEdge, rt.Length);  17                         else  18                             rt.Width = Random.Range(MinRoomEdge, temp + 1);  19                         break;  20                     case RoomShapeType.WidthMain:  21                         rt.Width = Random.Range(MinRoomEdge + 1, oe + 1);  22                         temp = MaxRoomArea / rt.Width;  23                         if (temp >= rt.Width)  24                             rt.Length = Random.Range(MinRoomEdge, rt.Width);  25                         else  26                             rt.Length = Random.Range(MinRoomEdge, temp + 1);  27                         break;  28                     case RoomShapeType.Coustom:  29                         rt.Length = Random.Range(MinRoomEdge, oe + 1);  30                         temp = MaxRoomArea / rt.Length;  31                         rt.Width = Random.Range(MinRoomEdge, temp + 1);  32                         break;  33                 }  34                 break;  35             case RoomRandType.EnumRand:  36                 var rc = RoomRandSizes.Count;  37                 if (rc == 0)  38                 {  39                     //未填写时设定随机默认值  40                     rt.Length = 3;  41                     rt.Width = 3;  42                 }  43                 else  44                 {  45                     var ridx = Random.Range(0,rc);  46                     var t = RoomRandSizes[ridx];  47                     if (t.x < 3 || t.y < 3)  48                     {  49                         //填写错误时设定随机默认值  50                         rt.Length = 3;  51                         rt.Width = 3;  52                     }  53                     else  54                     {  55                         switch (Shape)  56                         {  57                             case RoomShapeType.LengthMain:  58                                 rt.Length = t.x > t.y ? t.x : t.y;  59                                 rt.Width = t.x < t.y ? t.x : t.y;  60                                 break;  61                             case RoomShapeType.WidthMain:  62                                 rt.Width = t.x > t.y ? t.x : t.y;  63                                 rt.Length = t.x < t.y ? t.x : t.y;  64                                 break;  65                             case RoomShapeType.Coustom:  66                                 rt.Length = Random.value < .5f ? t.x : t.y;  67                                 rt.Width = t.y == rt.Length ? t.x : t.y;  68                                 break;  69                         }  70                     }  71                 }  72                 break;  73         }  74  75         rt.CenterPos = new Vector2Int(Random.Range((int)(centerPos.x - GenRange.x * .5f), (int)(centerPos.x + GenRange.x * .5f)),  76             Random.Range((int)(centerPos.z - GenRange.y * .5f), (int)(centerPos.z + GenRange.y * .5f)));  77  78         rt.PosY = centerPos.y;  79  80         var roomCenter = new Vector3(rt.CenterPos.x, rt.PosY, rt.CenterPos.y);  81  82         //射线检测重叠  83         if (RayRoomCheck(roomCenter, rt.Length, rt.Width))  84         {  85             return null;  86         }  87         return rt;  88     }

用的是射线检测重叠,生成了重叠的房间就会被视作是一次失败的随机试验,之前尝试过直接用物理系统推开失败了,可能是使用有误,如果有知道原因的欢迎与笔者分享,共同进步,有更好的避免矩形重叠的算法当然更好

(无奈笔者没能想出来):

 1     //生成房间前射线检测下范围内有无其他房间   2     bool RayRoomCheck(Vector3 cp, int length, int width)   3     {   4         bool result = false;   5         //长宽至少留一格间隙,高度与地板格对齐   6         var to = new Vector3(length + 1, FixedUnitHeight - 1, width + 1) * .5f;   7         var ned = cp - to;   8   9         var vx2 = ned + new Vector3(0, 0, width + 1) * .5f;  10         var vx3 = ned + new Vector3(0, 0, width + 1);  11  12         var vx4 = ned + new Vector3(length + 1, 0, width * .5f + .5f);  13         var vx5 = ned + new Vector3(length + 1, 0, width + 1);  14  15         var vz2 = ned + new Vector3(length + 1, 0, 0) * .5f;  16         var vz3 = ned + new Vector3(length + 1, 0, 0);  17  18         var vz4 = ned + new Vector3(length * .5f + .5f, 0, width + 1);  19         var vz5 = ned + new Vector3(length + 1, 0, width + 1);  20  21         result =  22         //4组射线,每组3条  23         RayCast(ned, Dx, length + 1, S_TAG) ||  24         RayCast(vx2, Dx, length + 1, S_TAG) ||  25         RayCast(vx3, Dx, length + 1, S_TAG) ||  26  27         RayCast(vx4, Dx * -1, length + 1, S_TAG) ||  28         RayCast(vx5, Dx * -1, length + 1, S_TAG) ||  29         RayCast(vz3, Dx * -1, length + 1, S_TAG) ||  30  31         RayCast(ned, Dz, width + 1, S_TAG) ||  32         RayCast(vz2, Dz, width + 1, S_TAG) ||  33         RayCast(vz3, Dz, width + 1, S_TAG) ||  34  35         RayCast(vz4, Dz * -1, width + 1, S_TAG) ||  36         RayCast(vz5, Dz * -1, width + 1, S_TAG) ||  37         RayCast(vx3, Dz * -1, width + 1, S_TAG);  38  39         return result;  40     }

这里将射线的起点和终点都延长了一格,是为了避免两个生成的房间贴得太紧,这样至少每个房间与其它房间间隔一个单位格或以上。

 

完整的房间结构生成脚本:

  1 using System.Collections;    2 using System.Collections.Generic;    3 using UnityEngine;    4 using UnityEngine.Events;    5    6 public enum RoomRandType    7 {    8     //全随机    9     AllRand,   10     //枚举大小随机   11     EnumRand   12 }   13   14 public enum RoomShapeType   15 {   16     //宽>=长   17     WidthMain,   18     //长>=宽   19     LengthMain,   20     //自定义,无形状要求   21     Coustom   22 }   23 //x-length z-width y-height   24   25 public class RoomBuilder : MonoBehaviour   26 {   27     //建筑单位方块   28     public GameObject BuildUnit;   29   30     //房间高度值   31     public int FixedUnitHeight;   32     //生成的房间层数   33     public int LayerCount;   34     //长宽随机范围   35     public Vector2Int GenRange;   36   37     //随机类型   38     public RoomRandType RandType;   39     //随机的房间形状类型   40     public RoomShapeType Shape;   41   42     //房间大小的随机列表,用于枚举随机   43     public List<Vector2Int> RoomRandSizes = new List<Vector2Int>();   44   45     //随机的房间最大面积   46     public int MaxRoomArea;   47     //最大随机数量(随机试验次数)   48     public int MaxRoomCount;   49   50     //最小边长度   51     private int MinRoomEdge;   52     //最大长宽比   53     public int MaxLengthWidthScale = 2;   54   55     //标准方向   56     Vector3Int Dx = new Vector3Int(1, 0, 0);   57     Vector3Int Dy = new Vector3Int(0, 1, 0);   58     Vector3Int Dz = new Vector3Int(0, 0, 1);   59   60     //建筑单位标签   61     const string S_TAG = "Unit";   62   63     private MapSystem MapManager;   64   65     void Awake()   66     {   67         MapManager = GetComponent<MapSystem>();   68     }   69   70     public IEnumerator GenRooms(Vector3Int centerPos,UnityAction complete)   71     {   72         var temp = (int)Mathf.Sqrt(MaxRoomArea * 1.0f / MaxLengthWidthScale);   73         MinRoomEdge = temp > 3 ? temp : 3;   74   75         //每层至少1   76         for (int i = 1; i <= LayerCount; i++)   77         {   78             SetGenOneRoom(centerPos, i);   79             yield return new WaitForSeconds(.1f);   80         }   81   82         //超过的随机布置   83         var oc = MaxRoomCount - LayerCount;   84         if (oc > 0)   85         {   86             for (int i = 1; i <= oc; i++)   87             {   88                 var r = Random.Range(1, LayerCount + 1);   89                 SetGenOneRoom(centerPos, r);   90                 yield return new WaitForSeconds(.1f);   91             }   92         }   93   94         //所有房间生成完成后发送一个委托信号,以便后续创建房间数据和计算必要连接   95         complete();   96     }   97   98     void SetGenOneRoom(Vector3Int cp, int r)   99     {  100         var layerCenter = cp - new Vector3(0, (LayerCount - 2 * r + 1) * .5f * FixedUnitHeight, 0);  101  102         var rt = RanRoomTran(layerCenter);  103         if (rt != null)  104         {  105             var roomCenter = new Vector3(rt.CenterPos.x, rt.PosY, rt.CenterPos.y);  106  107             GameObject temp = new GameObject(r.ToString());  108             temp.transform.position = roomCenter;  109             temp.tag = S_TAG;  110  111             //给生成的房间添加碰撞盒子并设置大小  112             GenOneRoom(roomCenter, rt.Length, rt.Width, temp.transform);  113             var bc = temp.AddComponent<BoxCollider>();  114             bc.size = new Vector3(rt.Length, FixedUnitHeight, rt.Width);  115  116             //目前用物理方式似乎难以推开重叠的房间,可能是哪里使用方法有误,改为用用射线检测解决...  117             //var rb = temp.AddComponent<Rigidbody>();  118             //rb.useGravity = false;  119             //rb.drag = Mathf.Infinity;  120             //rb.constraints = RigidbodyConstraints.FreezePositionY;  121             //rb.freezeRotation = true;  122  123             //将房间数据存入临时列表  124             MapManager.GenRooms.Add(rt);  125             MapManager.UnCrossRooms.Add(rt);  126         }  127     }  128  129     RoomTran RanRoomTran(Vector3 centerPos)  130     {  131         var rt = new RoomTran();  132  133         switch (RandType)  134         {  135             case RoomRandType.AllRand:  136                 int temp;  137                 var oe = MaxRoomArea / MinRoomEdge;  138                 switch (Shape)  139                 {  140                     case RoomShapeType.LengthMain:  141                         rt.Length = Random.Range(MinRoomEdge + 1, oe + 1);  142                         temp = MaxRoomArea / rt.Length;  143                         if (temp >= rt.Length)  144                             rt.Width = Random.Range(MinRoomEdge, rt.Length);  145                         else  146                             rt.Width = Random.Range(MinRoomEdge, temp + 1);  147                         break;  148                     case RoomShapeType.WidthMain:  149                         rt.Width = Random.Range(MinRoomEdge + 1, oe + 1);  150                         temp = MaxRoomArea / rt.Width;  151                         if (temp >= rt.Width)  152                             rt.Length = Random.Range(MinRoomEdge, rt.Width);  153                         else  154                             rt.Length = Random.Range(MinRoomEdge, temp + 1);  155                         break;  156                     case RoomShapeType.Coustom:  157                         rt.Length = Random.Range(MinRoomEdge, oe + 1);  158                         temp = MaxRoomArea / rt.Length;  159                         rt.Width = Random.Range(MinRoomEdge, temp + 1);  160                         break;  161                 }  162                 break;  163             case RoomRandType.EnumRand:  164                 var rc = RoomRandSizes.Count;  165                 if (rc == 0)  166                 {  167                     //未填写时设定随机默认值  168                     rt.Length = 3;  169                     rt.Width = 3;  170                 }  171                 else  172                 {  173                     var ridx = Random.Range(0,rc);  174                     var t = RoomRandSizes[ridx];  175                     if (t.x < 3 || t.y < 3)  176                     {  177                         //填写错误时设定随机默认值  178                         rt.Length = 3;  179                         rt.Width = 3;  180                     }  181                     else  182                     {  183                         switch (Shape)  184                         {  185                             case RoomShapeType.LengthMain:  186                                 rt.Length = t.x > t.y ? t.x : t.y;  187                                 rt.Width = t.x < t.y ? t.x : t.y;  188                                 break;  189                             case RoomShapeType.WidthMain:  190                                 rt.Width = t.x > t.y ? t.x : t.y;  191                                 rt.Length = t.x < t.y ? t.x : t.y;  192                                 break;  193                             case RoomShapeType.Coustom:  194                                 rt.Length = Random.value < .5f ? t.x : t.y;  195                                 rt.Width = t.y == rt.Length ? t.x : t.y;  196                                 break;  197                         }  198                     }  199                 }  200                 break;  201         }  202  203         rt.CenterPos = new Vector2Int(Random.Range((int)(centerPos.x - GenRange.x * .5f), (int)(centerPos.x + GenRange.x * .5f)),  204             Random.Range((int)(centerPos.z - GenRange.y * .5f), (int)(centerPos.z + GenRange.y * .5f)));  205  206         rt.PosY = centerPos.y;  207  208         var roomCenter = new Vector3(rt.CenterPos.x, rt.PosY, rt.CenterPos.y);  209  210         //射线检测重叠  211         if (RayRoomCheck(roomCenter, rt.Length, rt.Width))  212         {  213             return null;  214         }  215         return rt;  216     }  217  218     //生成房间前射线检测下范围内有无其他房间  219     bool RayRoomCheck(Vector3 cp, int length, int width)  220     {  221         bool result = false;  222         //长宽至少留一格间隙,高度与地板格对齐  223         var to = new Vector3(length + 1, FixedUnitHeight - 1, width + 1) * .5f;  224         var ned = cp - to;  225  226         var vx2 = ned + new Vector3(0, 0, width + 1) * .5f;  227         var vx3 = ned + new Vector3(0, 0, width + 1);  228  229         var vx4 = ned + new Vector3(length + 1, 0, width * .5f + .5f);  230         var vx5 = ned + new Vector3(length + 1, 0, width + 1);  231  232         var vz2 = ned + new Vector3(length + 1, 0, 0) * .5f;  233         var vz3 = ned + new Vector3(length + 1, 0, 0);  234  235         var vz4 = ned + new Vector3(length * .5f + .5f, 0, width + 1);  236         var vz5 = ned + new Vector3(length + 1, 0, width + 1);  237  238         result =  239         //4组射线,每组3条  240         RayCast(ned, Dx, length + 1, S_TAG) ||  241         RayCast(vx2, Dx, length + 1, S_TAG) ||  242         RayCast(vx3, Dx, length + 1, S_TAG) ||  243  244         RayCast(vx4, Dx * -1, length + 1, S_TAG) ||  245         RayCast(vx5, Dx * -1, length + 1, S_TAG) ||  246         RayCast(vz3, Dx * -1, length + 1, S_TAG) ||  247  248         RayCast(ned, Dz, width + 1, S_TAG) ||  249         RayCast(vz2, Dz, width + 1, S_TAG) ||  250         RayCast(vz3, Dz, width + 1, S_TAG) ||  251  252         RayCast(vz4, Dz * -1, width + 1, S_TAG) ||  253         RayCast(vz5, Dz * -1, width + 1, S_TAG) ||  254         RayCast(vx3, Dz * -1, width + 1, S_TAG);  255  256         return result;  257     }  258  259     bool RayCast(Vector3 ori, Vector3 dir, float mD, string tag)  260     {  261         Ray ray = new Ray(ori, dir);  262         RaycastHit info;  263         if (Physics.Raycast(ray, out info, mD))  264         {  265             if (info.transform.tag == tag)  266                 return true;  267         }  268         return false;  269     }  270  271     /// <summary>  272     /// 生成单一房间的轮廓  273     /// </summary>  274     /// <param name="centerPos">房间中点位置</param>  275     /// <param name="length"></param>  276     /// <param name="width"></param>  277     /// <param name="parent">父物体</param>  278     void GenOneRoom(Vector3 centerPos, int length, int width, Transform parent = null)  279     {  280         var to = new Vector3(length - 1, FixedUnitHeight - 1, width - 1) * .5f;  281  282         //顶点  283         var ned = centerPos - to;  284         var fod = centerPos + to;  285  286         var v3 = new Vector3(ned.x, fod.y, ned.z);  287         var v4 = new Vector3(ned.x, fod.y, fod.z);  288         var v5 = new Vector3(ned.x, ned.y, fod.z);  289  290         var v6 = new Vector3(fod.x, ned.y, ned.z);  291         var v7 = new Vector3(fod.x, ned.y, fod.z);  292         var v8 = new Vector3(fod.x, fod.y, ned.z);  293  294         //顶点位置(8个)  295         InsSetPos(ned, parent);  296         InsSetPos(fod, parent);  297         InsSetPos(v3, parent);  298         InsSetPos(v4, parent);  299         InsSetPos(v5, parent);  300         InsSetPos(v6, parent);  301         InsSetPos(v7, parent);  302         InsSetPos(v8, parent);  303  304         //12条棱(4*3)  305         //  306         InsOneEdge(length, ned, Dx, parent);  307         InsOneEdge(length, v3, Dx, parent);  308         InsOneEdge(length, v4, Dx, parent);  309         InsOneEdge(length, v5, Dx, parent);  310         //  311         InsOneEdge(FixedUnitHeight, ned, Dy, parent);  312         InsOneEdge(FixedUnitHeight, v5, Dy, parent);  313         InsOneEdge(FixedUnitHeight, v6, Dy, parent);  314         InsOneEdge(FixedUnitHeight, v7, Dy, parent);  315         //  316         InsOneEdge(width, ned, Dz, parent);  317         InsOneEdge(width, v3, Dz, parent);  318         InsOneEdge(width, v6, Dz, parent);  319         InsOneEdge(width, v8, Dz, parent);  320     }  321  322     //生成一条边上的建筑单位但不包含顶点位置  323     void InsOneEdge(int edge, Vector3 v, Vector3 dir, Transform parent = null)  324     {  325         //忽略首尾单位  326         for (int i = 1; i < edge - 1; i++)  327         {  328             InsSetPos(v + i * dir, parent);  329         }  330     }  331  332     void InsSetPos(Vector3 pos, Transform parent = null)  333     {  334         var ins = Instantiate(BuildUnit);  335         ins.transform.position = pos;  336         ins.transform.parent = parent;  337     }  338 }

 

在MapSystem中可以在房间结构生成完后创建一个默认的数据结构:

 1     public void RandRoomDatas()   2     {   3         if (RoomBuilder == null||MapData ==null)   4             return;   5   6         RoomBuilder.StartCoroutine(RoomBuilder.GenRooms(MapData.MapCenter,()=>   7         {   8             CreatRoomData();   9             RandRoomCrosses();  10         }));  11     }  12  13     void CreatRoomData()  14     {  15         for (int i = 1; i < GenRooms.Count + 1; i++)  16         {  17             var rd = new RoomData();  18             rd.Id = i;  19             rd.RoomTran = GenRooms[i - 1];  20             rd.BattleType = RoomBattleType.NormalBattle;  21             if (rd.Id == 1)  22                 rd.BattleType = RoomBattleType.Rest;  23             rd.CrossRooms = new List<RoomData>();  24             rd.Monsters = new List<GameObject>();  25             rd.bEndRoom = false;  26             rd.bMainCrossRoom = false;  27  28             MapData.RoomDataDic.Add(rd.Id, rd);  29         }  30     }

 

效果图:(单层-枚举列表随机)

 

 单层(全随机-长条形房间随机):

 

 多层(层数5)(自定义-全随机):

 

 参考资料:

https://indienova.com/indie-game-development/rooms-and-mazes-a-procedural-dungeon-generator/?tdsourcetag=s_pctim_aiomsg

https://mp.weixin.qq.com/s/3yM-mAAXq_fX5tcy82s0uQ