Unity实现随机房间生成时崩溃及房间嵌套问题咨询
Unity 简单房间放置功能异常修复方案
问题现象
- 开发功能:Simple Room Placement(简单房间放置)
- 初始故障:点击Play按钮运行后Unity直接崩溃,当前使用地图参数为宽172、高80
- 初始版本
GenerateRoom实现代码:
void GenerateRoom() { int paddingX = 14; int paddingY = 14; roomX1 = Random.Range(0, width - paddingX); roomY1 = Random.Range(0, height - paddingY); roomX2 = Random.Range(roomX1 + 6, roomX1 + paddingX); roomY2 = Random.Range(roomY1 + 6, roomY1 + paddingY); for (int x = roomX1; x < roomX2; x++) { for (int y = roomY1; y < roomY2; y ++) { if (map[x, y] == 0) { GenerateRoom(); } else { map[x, y] = 0; } } } }
- 初步排查调整:移除双层循环内对
GenerateRoom()的递归调用,仅保留map[x,y]不为0时赋值为0的逻辑,修改后循环片段:
for (int x = roomX1; x < roomX2; x++) { for (int y = roomY1; y < roomY2; y ++) { if (map[x, y] != 0) { map[x,y] = 0 } } }
- 调整后效果:Unity崩溃问题消失,确认崩溃原因为无限制递归触发栈溢出,但出现新问题:生成的房间存在重叠、嵌套情况,无法实现无重叠房间生成。
根因分析
原实现存在两个核心逻辑错误:
- 递归逻辑无终止条件:遍历到已标记为房间的格子(值为0)就立刻触发新的
GenerateRoom调用,递归深度完全不可控,地图尺寸越大越容易触发栈溢出导致编辑器崩溃- 无放置合法性校验:移除递归后直接写入地图的逻辑,完全没有检查待生成区域是否已被其他房间占用,必然出现重叠、嵌套问题
无重叠房间生成实现方案
核心思路是前置校验替代递归回溯,从根源上规避栈溢出和重叠问题,实现步骤如下:
- 抽离可配置参数:定义单房间最小/最大尺寸、房间间距、目标房间总数、单房间最大重试次数,避免随机逻辑失控
- 改用外层循环控制生成流程:完全移除遍历格子过程中的递归调用,通过循环逐个生成房间,单个房间多次随机坐标校验不通过时直接终止生成,避免死循环
- 放置前做全区域校验:随机出房间边界后,先检查房间覆盖范围+预留边距内的所有格子是否存在已占用内容,校验通过才允许写入地图
- 存储已生成房间数据:用列表记录所有已生成房间的边界坐标,方便后续生成走廊、设置玩家出生点等逻辑调用
参考实现代码:
public int width = 172; public int height = 80; public int roomMinSize = 6; public int roomMaxSize = 14; public int roomGap = 1; // 房间之间的预留间距,设为0则房间可贴边生成 public int targetRoomCount = 20; public int maxRetryPerRoom = 50; // 单个房间最多重试随机坐标次数 private int[,] map; private List<(int x1, int y1, int x2, int y2)> placedRooms = new(); void Start() { // 初始化地图:1代表未占用/墙体,0代表房间地面 map = new int[width, height]; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { map[x, y] = 1; } } GenerateNonOverlapRooms(); } void GenerateNonOverlapRooms() { int generatedCount = 0; while (generatedCount < targetRoomCount) { bool placed = false; int retry = 0; while (retry < maxRetryPerRoom && !placed) { // 随机当前房间的尺寸和坐标 int roomW = Random.Range(roomMinSize, roomMaxSize + 1); int roomH = Random.Range(roomMinSize, roomMaxSize + 1); int x1 = Random.Range(0, width - roomW - roomGap); int y1 = Random.Range(0, height - roomH - roomGap); int x2 = x1 + roomW; int y2 = y1 + roomH; // 校验待放置区域是否合法 bool canPlace = true; for (int x = x1 - roomGap; x < x2 + roomGap; x++) { for (int y = y1 - roomGap; y < y2 + roomGap; y++) { // 超出地图边界直接判定不可放置 if (x < 0 || x >= width || y < 0 || y >= height) { canPlace = false; break; } // 区域内已有已占用格子则不可放置 if (map[x, y] == 0) { canPlace = false; break; } } if (!canPlace) break; } // 校验通过则写入地图 if (canPlace) { for (int x = x1; x < x2; x++) { for (int y = y1; y < y2; y++) { map[x, y] = 0; } } placedRooms.Add((x1, y1, x2, y2)); placed = true; generatedCount++; } retry++; } // 剩余空间不足以放置新房间时直接终止生成 if (!placed) break; } }
- 上述实现完全规避递归带来的栈溢出风险,前置校验逻辑可以100%避免房间重叠、嵌套问题
- 可根据实际需求调整参数:比如增大
roomGap留出走廊空间,调整roomMinSize/roomMaxSize控制房间大小区间 - 后续需要生成房间连通路径时,直接遍历
placedRooms列表取房间中心点连接即可,不需要额外遍历全地图找房间位置
内容的提问来源于stack exchange,提问作者gregghy
相关产品推荐
相关产品推荐

