JS/HTML5游戏开发:普里姆算法生成随机迷宫详解
本文详解JavaScript游戏中常用的随机迷宫生成算法——普里姆算法。通过二维数组构建逻辑地图,利用随机访问与打通墙壁机制生成连通迷宫,并结合寻路算法实现角色自动移动。包含完整代码示例及在线测试工具,适用于RPG等游戏开发场景。
游戏路径搜索与迷宫生成概述
本文介绍JS/HTML5游戏开发中常用的路径搜索算法,重点讲解随机迷宫生成技术。内容旨在为开发者提供参考与实现思路。
路径搜索算法在游戏开发中应用广泛,尤其在RPG(角色扮演)和SLG(策略)类游戏中不可或缺。玩家通过鼠标点击指定目的地,系统自动计算并控制角色或NPC移动至该点,这一过程即由寻路算法实现。简言之,其核心需求是:在地图中,当主角位于A点且玩家点击B点时,系统需自动规划出一条从A到B的路径并执行移动,如图6-21所示。

在碰撞检测算法章节中曾提及,现代游戏地图通常采用网格化结构。尽管视觉层不显示网格,但逻辑层会维护一个与地图尺寸一致的二维数组。该数组中,可通过区域标记为0,障碍物或不可通行区域标记为1或其他数值。如左侧游戏地图对应右侧二维数组所示,逻辑层专门用于定义障碍。基于此逻辑层,自动寻路问题转化为:在二维数组中,寻找一条从值为0的节点到目标节点的路径。

随机迷宫生成算法原理
在深入自动寻路算法之前,先介绍另一种游戏常用算法:随机地图(迷宫)生成算法。该算法常与寻路算法结合使用,以提供动态地图环境。
随机迷宫算法
基于前述地图理论,地图的障碍逻辑层由二维数组存储,其中0代表通路,1代表障碍。我们的目标是随机生成该二维数组。最直观的方法是遍历数组并在每个位置随机生成0或1,但这种方法生成的图形视觉效果较差,且无法保证图中任意两点之间连通。
如图6-21所示,假设有一个6×6的迷宫。初始状态下,所有通路被视为完全封闭:白色格子代表可通行区域,黑色格子代表墙壁(不可通行)。

步骤一:随机选择一个白色格子作为当前访问节点,并将其加入已访问列表。
步骤二:循环执行以下操作,直到所有格子均被访问。
- 获取当前访问格子四周(上、下、左、右)的相邻格子。在这些相邻格子中,随机选择一个尚未在已访问列表中的格子。若找到该格子,则打通当前格子与目标格子之间的墙壁(将其值置为0),并将该目标格子设为新的当前访问格子,同时加入已访问列表。
- 若当前格子的四周格子均已在已访问列表中,则从已访问列表中随机选取一个格子作为新的当前访问格子。
通过上述迷宫生成算法,可以生成具有自然随机性的迷宫结构。
JavaScript代码实现与测试
以下代码实现了上述算法,生成一个R行N列大小的迷宫。需注意,R表示初始空白格子的行数,由于需包含墙壁数据,最终生成的二维数组实际行数为2R+1,列数为2N+1。
<!DOCTYPE html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<meta charset="UTF-8">
<title>01_随机迷宫算法</title>
<style>
#stage {
border: 1px solid lightgray;
}
.rebuild{
width:160px;
height:40px;
line-height: 40px;
text-align: center;
background-color:#000000;
color:#fff;
font-size: 24px;
margin-bottom: 20px;
cursor: pointer;
}
</style>
</head>
<body>
<div class="rebuild">点击更新</div>
<canvas id="stage"></canvas>
</body>
<script>
window.onload = function () {
var stage = document.querySelector('#stage'),
ctx = stage.getContext('2d');
stage.width = 600;
stage.height = 600;
//取区域随机数x>=min && x<max
function randInt(min,max)
{
max=max||0;
min=min||0;
var step=Math.abs(max-min);
var st = (arguments.length<2)?0:min;//参数只有一个的时候,st = 0;
var result ;
result = st+(Math.ceil(Math.random()*step))-1;
return result;
}
//普里姆算法生成连通图的二维数组
// row 行 column 列
function primMaze(r, c) {
//初始化数组
function init(r, c) {
var a = new Array(2 * r + 1);
//全部置1
for (let i = 0, len = a.length; i < len; i++) {
var cols = 2 * c + 1;
a[i] = new Array(cols);
for(let j=0,len1=a[i].length;j<len1;j++)
{
a[i][j]=1;
}
}
//中间格子为0
for (let i = 0; i < r; i++)
for (let j = 0; j < c; j++) {
a[2 * i + 1][2 * j + 1] = 0;
}
return a;
}
//处理数组,产生最终的数组
function process(arr) {
//acc存放已访问队列,noacc存放没有访问队列
var acc = [], noacc = [];
var r = arr.length >> 1, c = arr[0].length >> 1;
var count = r * c;
for (var i = 0; i < count; i++) {
noacc[i] = 0;
}
//定义空单元上下左右偏移
var offs = [-c, c, -1, 1], offR = [-1, 1, 0, 0], offC = [0, 0, -1, 1];
//随机从noacc取出一个位置
var pos = randInt(count);
noacc[pos] = 1;
acc.push(pos);
while (acc.length < count) {
var ls = -1, offPos = -1;
offPos = -1;
//找出pos位置在二维数组中的坐标
var pr = pos / c | 0, pc = pos % c, co = 0, o = 0;
//随机取上下左右四个单元
while (++co < 5) {
o = randInt(0, 5);
ls = offs[o] + pos;
var tpr = pr + offR[o];
var tpc = pc + offC[o];
if (tpr >= 0 && tpc >= 0 && tpr <= r - 1 && tpc <= c - 1 && noacc[ls] == 0) {
offPos = o;
break;
}
}
if (offPos < 0) {
pos = acc[randInt(acc.length)];
}
else {
pr = 2 * pr + 1;
pc = 2 * pc + 1;
//相邻空单元中间的位置置0
arr[pr + offR[offPos]][pc + offC[offPos]] = 0;
pos = ls;
noacc[pos] = 1;
acc.push(pos);
}
}
}
var a = init(r, c);
process(a);
return a;
//返回一个二维数组,行的数据为2r+1个,列的数据为2c+1个
}
//栅格线条
function drawGrid(context, color, stepx, stepy) {
context.strokeStyle = color;
context.lineWidth = 0.5;
for (var i = stepx + 0.5; i < context.canvas.width; i += stepx) {
context.beginPath();
context.moveTo(i, 0);
context.lineTo(i, context.canvas.height);
context.stroke();
}
for (var i = stepy + 0.5; i < context.canvas.height; i += stepy) {
context.beginPath();
context.moveTo(0, i);
context.lineTo(context.canvas.width, i);
context.stroke();
}
}
function createRect(x, y, r, c) {
ctx.beginPath();
ctx.fillStyle = c;
ctx.rect(x, y, r, r);
ctx.fill();
}
function update() {
ctx.clearRect(0, 0, 600, 600);
drawGrid(ctx, 'lightgray', 40, 40);
var mapArr = primMaze(7,7);
console.log(mapArr);
//根据地图二维数组创建色块
for (var i = 0, len = mapArr.length; i < len; i++) {
for (var j = 0, len1 = mapArr[i].length; j < len1; j++) {
if (mapArr[i][j]) {
createRect(i * 40, j * 40, 40, "black");
}
}
}
}
update();
document.querySelector('.rebuild').addEventListener('click', update);
};
</script>
</html>
可使用在线HTML/CSS/JavaScript代码运行工具(http://tools.jb51.net/code/HtmlJsRun)测试上述代码的运行效果,结果如图a4所示。

完整代码托管于GitHub:https://github.com/krapnikkk/JS-gameMathematics
如需了解更多JavaScript相关内容,可参考本站专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》。
希望本文所述内容对JavaScript程序设计有所帮助。
JS动态构建复杂嵌套JSON对象的方法
2026-11-05 15:41:52
JS负数取余为什么是负数?详解余数与模运算区别
2026-11-05 13:34:41
JS 解压出现乱码或报错怎么解决?
2026-11-05 12:32:38
JS 中 startsWith、indexOf 与正则判断字符串前缀的对比
2026-11-04 22:19:43
JS解析Excel后怎么将数据转换为JSON格式
2026-11-04 20:58:28
