JS/HTML5游戏开发:基于网格的碰撞检测算法详解
本文详解JavaScript游戏中常用的网格碰撞检测算法。通过构建二维数组表示逻辑地图,利用0和1标识通行与障碍,实现高效的人物与NPC或障碍物碰撞判断。文章提供完整代码示例,分析该算法在RPG、SLG等类型游戏中的应用,并探讨其优缺点及精度优化建议。
网格碰撞检测算法概述
本文详细介绍一种在JS/HTML5游戏开发中广泛使用的碰撞检测技术——地图格子算法。内容包含原理分析、代码实现及实际应用案例,供开发者参考学习。
算法适用场景与地图构建
该算法常见于RPG(如早期的《最终幻想》、《勇者斗恶龙》、《仙剑奇侠传》)、SLG(如《炎龙骑士团》、《超级机器人大战》)以及PUZ(如《俄罗斯方块》、《宝石迷阵》)类游戏。在这类游戏中,地图通常由基础地图块拼接而成。开发时,首先对构成地图的基本元素进行编号,随后通过组合这些编号块来构建任意尺寸的地图。
逻辑层设计与碰撞判断原理
早期RPG或SLG游戏的地图明显由小格子构成。这种设计优势在于节省内存,仅需少量基础元素即可组合出大规模地图,具有简单灵活的特点,但视觉美观度可能受限。为了便于处理碰撞逻辑(如人物与NPC交互或避开不可通行障碍),游戏内部通常维护一张不可见的逻辑层地图。该逻辑层与视觉地图大小一致,同样划分为网格。通常使用一个二维数组来描述此层:数组值为1代表不可通行,0代表可通行。例如,当控制人物移动时,只需查询对应网格数组值,即可快速判断是否发生碰撞。
代码实现与测试
var mapArr = [
1, 0, 0, 1,
0, 0, 0, 1,
0, 1, 0, 0,
1, 0, 0, 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>地图格子算法</title>
<style>
#stage {
border: 1px solid lightgray;
}
</style>
</head>
<body>
<canvas id="stage"></canvas>
</body>
<script>
window.onload = function () {
var stage = document.querySelector('#stage'),
ctx = stage.getContext('2d');
stage.width = 400;
stage.height = 400;
var mapArr = [
1, 0, 0, 1,
0, 0, 0, 1,
0, 1, 0, 0,
1, 0, 0, 1
],rectIdx = 5;
//栅格线条
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();
}
document.onkeydown = function (event) {
var e = event || window.event || arguments.callee.caller.arguments[0];
//根据地图数组碰撞将测
switch (e.keyCode){
case 37:
console.log("Left");
if (rectIdx - 1 >= 0 && (rectIdx - 1) % 4 !== 3 && !mapArr[rectIdx - 1]) {
rectIdx -= 1;
}
break;
case 38:
console.log("Top");
if (rectIdx - 4 >= 0 && !mapArr[rectIdx - 4]) {
rectIdx -= 4;
}
break;
case 39:
console.log("Right");
if ((rectIdx + 1) % 4 !== 0 && !mapArr[rectIdx + 1]) {
rectIdx += 1;
}
break;
case 40:
console.log("Bottom");
if (rectIdx + 4 < mapArr.length && !mapArr[rectIdx + 4]) {
rectIdx += 4;
}
break;
default:
return false;
}
};
function update() {
ctx.clearRect(0, 0, 400, 400);
drawGrid(ctx, 'lightgray', 100, 100);
var rect = {
x: rectIdx % 4 * 100,
y: rectIdx % 4 === 0 ? rectIdx / 4 * 100 : Math.floor(rectIdx / 4) * 100,
r: 100,
c: "blue"
};
createRect(rect.x, rect.y, rect.r, rect.c);
//根据地图数组创建色块
for (var i = 0, len = mapArr.length; i < len; i++) {
if (mapArr[i]) {
createRect(i % 4 * 100, i % 4 === 0 ? i / 4 * 100 : Math.floor(i / 4) * 100, 100, "red");
}
}
requestAnimationFrame(update);
}
update();
};
</script>
</html>
利用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试运行上述代码,可得到如下运行结果:

完整代码仓库地址:https://github.com/krapnikkk/JS-gameMathematics
算法优缺点与精度优化
这种判断方式逻辑简单且执行效率高,但精度有限。若游戏对象的尺寸远小于网格尺寸,可能出现物体虽未接触但已无法继续移动的情况(即“悬空”碰撞)。因此,采用此类算法时,建议缩小网格尺寸,或确保游戏角色尺寸与网格大小相匹配,以减少视觉与逻辑上的偏差。
如果您对JavaScript算法感兴趣,还可参考本站其他专题:《JavaScript数学运算用法总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript数组操作技巧总结》、《JavaScript排序算法总结》、《JavaScript遍历算法与技巧总结》、《JavaScript查找算法技巧总结》及《JavaScript错误与调试技巧总结》。
希望本文关于JavaScript程序设计的分享能对您的开发工作有所帮助。
JavaScript怎么准确判断数据类型:typeof、instanceof与Object.prototype.toString
2026-11-05 17:09:21
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
