新手向:如何在JavaScript Tilemap中实现玩家与墙体碰撞检测?
Tilemap玩家与墙体方块碰撞检测(新手简易实现)
问题背景
我一直在寻找如何检测Tilemap中玩家与指定方块的碰撞,但找到的都是进阶教程。作为编程新手,我希望用最简单的方式实现,理解核心原理:当玩家走到值为1的方块(墙体)时,无法移动到该位置。
以下是我的初始代码:
HTML代码
<canvas id="canvas"></canvas>
JavaScript代码
// Initi ctx = null; var ctx = document.getElementById("canvas").getContext("2d"); // Map var gameMap = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; var tileW = 40, tileH = 40; var mapW = 10, mapH = 10; window.onload = function() { requestAnimationFrame(drawGame); ctx.font = "bold 10pt sans-serif"; }; // Player var x = 100; var y = 100; var radius = 10; var upPressed = false; var downPressed = false; var leftPressed = false; var rightPressed = false; var speed = 1; function drawPlayer() { ctx.fillStyle = "green"; ctx.beginPath(); ctx.arc(x, y, radius, 0, Math.PI * 2) ctx.fill(); } // Inputs function inputs() { if (upPressed) { y = y - speed; } if (downPressed) { y = y + speed; } if (leftPressed) { x = x - speed; } if (rightPressed) { x = x + speed; } } document.body.addEventListener("keydown", keyDown) document.body.addEventListener("keyup", keyUp) function keyDown(event) { if (event.keyCode == 38) { upPressed = true; } if (event.keyCode == 40) { downPressed = true; } if (event.keyCode == 37) { leftPressed = true; } if (event.keyCode == 39) { rightPressed = true; } if (event.keyCode == 65) { speedCodePressed = true; speed = 20; } if (event.keyCode == 32) { shootPressed = true; } } function keyUp(event) { if (event.keyCode == 38) { upPressed = false; } if (event.keyCode == 40) { downPressed = false; } if (event.keyCode == 37) { leftPressed = false; } if (event.keyCode == 39) { rightPressed = false; } if (event.keyCode == 32) { shootPressed = false; } } // game map draw function function drawMap() { if (ctx == null) { return; } for (var y = 0; y < mapH; ++y) { for (var x = 0; x < mapW; ++x) { switch (gameMap[((y * mapW) + x)]) { case 0: ctx.fillStyle = "#685b48"; break; default: ctx.fillStyle = "#5aa457"; } ctx.fillRect(x * tileW, y * tileH, tileW, tileH); } } } // clear screen function clearScreen() { ctx.fillStyle = "black"; ctx.fillRect(0, 0, canvas.width, canvas.height); } // game loop function drawGame() { requestAnimationFrame(drawGame); clearScreen(); drawMap(); drawPlayer(); inputs(); }
简易实现方案
核心逻辑
先预判玩家移动后的位置,检查该位置对应的方块是否为墙体(值为1),如果是,就不执行移动;如果不是,才更新玩家位置。
步骤1:添加方块检测工具函数
在代码中添加一个函数,用于根据屏幕坐标获取对应地图方块的值:
// 根据屏幕坐标获取对应地图方块的值 function getTileAt(screenX, screenY) { // 将屏幕坐标转换为地图格子的行列索引 const tileX = Math.floor(screenX / tileW); const tileY = Math.floor(screenY / tileH); // 超出地图范围的区域视为墙体,防止玩家移出画布 if (tileX < 0 || tileX >= mapW || tileY < 0 || tileY >= mapH) { return 1; } // 计算一维数组中的索引并返回方块值 return gameMap[tileY * mapW + tileX]; }
步骤2:修改输入处理函数,加入碰撞检测
替换原来的inputs()函数,加入预判和检测逻辑:
function inputs() { // 先保存当前位置,计算预判的新位置 let newX = x; let newY = y; if (upPressed) { newY = y - speed; } if (downPressed) { newY = y + speed; } if (leftPressed) { newX = x - speed; } if (rightPressed) { newX = x + speed; } // 检测新位置对应的方块是否为墙体 const targetTile = getTileAt(newX, newY); // 只有不是墙体时,才更新玩家位置 if (targetTile !== 1) { x = newX; y = newY; } }
步骤3:(可选)优化碰撞检测精度
如果玩家半径较大,上面的中心检测可能会出现边缘穿墙的情况,可以改成检测玩家四个边缘的位置:
// 检测玩家位置是否合法(不碰到墙体) function isPositionValid(newX, newY) { // 获取玩家四个边缘的坐标 const topEdge = newY - radius; const bottomEdge = newY + radius; const leftEdge = newX - radius; const rightEdge = newX + radius; // 只要任意一个边缘碰到墙体,位置就不合法 if (getTileAt(leftEdge, newY) === 1 || getTileAt(rightEdge, newY) === 1 || getTileAt(newX, topEdge) === 1 || getTileAt(newX, bottomEdge) === 1) { return false; } return true; } // 修改inputs函数使用新的检测逻辑 function inputs() { let newX = x; let newY = y; if (upPressed) { newY = y - speed; } if (downPressed) { newY = y + speed; } if (leftPressed) { newX = x - speed; } if (rightPressed) { newX = x + speed; } if (isPositionValid(newX, newY)) { x = newX; y = newY; } }
原理说明
- 坐标转换:屏幕坐标除以方块大小,取整得到地图的行列索引,再通过
tileY * mapW + tileX计算一维数组中的位置(因为你的地图是用一维数组存储的)。 - 预判移动:先计算移动后的位置再检测,避免直接移动后回滚的复杂逻辑,更适合新手理解。
- 边界处理:把地图外的区域视为墙体,防止玩家移出画布范围。
内容的提问来源于stack exchange,提问作者Karloux
相关产品推荐
相关产品推荐

