最近有一些会员反映他们很喜欢那一篇介绍地牢生成的文章《房间和迷宫:一个地牢生成算法》,但是在一些细节的开发方面,这篇文章并没有说得太详细。比如迷宫算法,那么今天就给大家介绍一种相对简单的迷宫生成算法。
回溯法生成迷宫
关于回溯法(Back Tracking)的介绍可以看一下简要的介绍。
其实这是一种很简单的生成迷宫的算法,大概分为以下几个步骤:
- 选择地图的某一点,标记为已访问过,并将这个点加入到(
push
)回溯点列表堆栈中; - 查看这个点四周有哪里可以前进(也就是没有标记为访问过的点);
- 前进到目标点,将这个点也标记为已访问过,并加入到回溯点列表堆栈中;
- 重复第 2 步,直到找不到合适的点(周围的点都被访问过了,说明到了死胡同);
- 从回溯点堆栈中移除最后一个(
pop
),然后继续重复第 2 步(相当于往回走一步); - 当回溯点堆栈为空的时候,说明迷宫已经生成完毕了。
伪代码如下:
backtracking = new Array(); start = point(x, y) // 寻找下一个路径点 function find(point) { // 寻找目标点 next_point = findNext(point); if (next_point) { // 如果找到路就加入回溯堆栈,并且寻找下一个 backtracking.push(next_point); find(next_point); } else { // 如果没找到路就移除掉堆栈中的最后一点 backtracking.pop(); // 检查是否完成 if (backtracking.count > 0) { // 没有完成,就从堆栈的最后一个位置重新开始 find(backtracking.last_point) } else { // 找到就完成 print('END'); } } }
经过这样一个递归,我们应该很快就能生成一个完美迷宫,从迷宫中的任何一点都能够到达另外一点。
让它适合 Tile Based 地图
其实迷宫算法并不难,但是我们不少会员被卡在这里,一个主要原因可能是因为大家使用的是 Tile Based 的地图数据,而目前大部分教程描述的都是可以生成如下这种地图:
这个地图其实也是 Tile Based,只不过在生成地图数据以后,在每一个 Tile 上加上了“墙”,而我们有些时候需要“墙”本身就是作为一个 Tile 的形态存在。
其实这也很简单,我们根据前面讲述的知识,采用如下的代码来实现它:
function query(start) { var x = start.x, y = start.y; var dirs = ''; if ((y - 2 >= 0) && (map.mapData[y - 2][x] == 0)) dirs += 'N'; if ((x - 2 >= 0) && (map.mapData[y][x - 2] == 0)) dirs += 'W'; if ((y + 2 < map.rows) && (map.mapData[y + 2][x] == 0)) dirs += 'S'; if ((x + 2 < map.cols) && (map.mapData[y][x + 2] == 0)) dirs += 'E'; if (dirs == '') { moves.pop(); if (moves.length == 0) draw(); else query(moves[moves.length - 1]); } else { var dir = dirs.substr(Math.floor(Math.random() * dirs.length), 1); switch (dir) { case 'N' : map.mapData[y - 1][x] = 1; y = y - 2; break; case 'W' : map.mapData[y][x - 1] = 1; x = x - 2; break; case 'S' : map.mapData[y + 1][x] = 1; y = y + 2; break; case 'E' : map.mapData[y][x + 1] = 1; x = x + 2; break; } map.mapData[y][x] = 1; moves.push({ y:y, x:x }); query({ y:y, x:x }); } }
上面是一段可用的 JavaScript 代码,注意它在寻找下一个目标点的时候是采用了 +2
-2
的形式,也就是说,在选择临近点的时候是跳过一个 Tile 的,而当找到合适的目标点的时候,还要多一个步骤将这两点之间的 Tile 也标记为联通路径,这样,就很简单的实现了将 Tile 作为墙的需求。
大家可以尝试一下:
生成过程
这里也特地准备了一个带有动画演示生成过程的示例,可以大概看到生成迷宫的过程。浅灰色的代表回溯的过程。(这篇教程准备相对仓促,所以可能偶尔会出现 BUG,请多担待)
讨论和交流
我们已经开通了专门针对 Roguelike 学习的小组,大家有兴趣的不妨来这里访问:
Roguelike 开发小组
indienova 小组 去小组
相关教程的问题也可以在小组中交流。
很有用,感谢 indienova 大神的无私奉献!!!
很想知道The Beginer's Guide结局的迷宫是不是也是动态生成的
感谢楼主,我用C#实现了并用到unity中了,等实现《房间和迷宫:一个地牢生成算法》这篇文章中的算法后一并发出来给大家参考~
@Terean:加油啊~
很棒,受教了
谢谢楼主,我用C实现了