如何修复JavaScript游戏中仅玩家左上角触发碰撞的问题?
修复Canvas游戏中玩家仅左上角触发碰撞的问题
你的核心问题是碰撞检测只用到了玩家的左上角坐标,没有覆盖玩家整个32x32的矩形范围,导致只有角落能触发碰撞。以下是具体修复方案:
关键修复点
- 放弃用
FX/FY作为临时移动坐标,直接基于玩家当前位置计算移动后的目标位置 - 碰撞检测时,使用玩家的完整矩形边界(左、右、上、下)与地图块进行碰撞判断
- 优化碰撞逻辑:只允许在目标位置不与障碍块(tile=3等)碰撞时,才更新玩家位置
修改后的完整代码
JavaScript部分
var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); var source = new Image(548, 64); source.src = "dungeon.jpg"; let rightPressed = false; let leftPressed = false; let upPressed = false; let downPressed = false; var map = { cols: 9, rows: 8, tsize: 64, tiles: [ 3, 2, 3, 2, 3, 2, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 2, 1, 1, 3, 3, 3, 3, 2, 2, 2, 1, 3, 3, 3, 3, 3, 3, 2, 2, 1, 3, 3, 3, 3, ], getTile: function(col, row) { return this.tiles[row * map.cols + col]; }, }; var player = { x: 200, y: 200, size: 32, speed: 2 }; console.log("drawing tilemap"); console.clear(); ctx.font = "30px Arial"; ctx.fillText("nvm its not working", 10, 50); draw_map(); draw_player(); function draw_map() { for (var column = 0; column < map.cols; column++) { for (var row = 0; row < map.rows; row++) { const tile = map.getTile(column, row); const x = column * map.tsize; const y = row * map.tsize; drawTile(tile, x, y); } } } function drawTile(tile, X, Y) { if (tile === 1) { ctx.fillStyle = "#34ed4d"; } else if (tile === 2) { ctx.fillStyle = "#fff291"; } else if (tile === 3) { ctx.fillStyle = "#9c9ea1"; } ctx.fillRect(X, Y, map.tsize, map.tsize); } function draw_player() { ctx.fillStyle = "#fff200"; ctx.fillRect(player.x, player.y, player.size, player.size); } function keyDownHandler(e) { if (e.key === "Right" || e.key === "ArrowRight") { rightPressed = true; } else if (e.key === "Left" || e.key === "ArrowLeft") { leftPressed = true; } if (e.key === "Up" || e.key === "ArrowUp") { upPressed = true; } else if (e.key === "Down" || e.key === "ArrowDown") { downPressed = true; } } function keyUpHandler(e) { if (e.key === "Right" || e.key === "ArrowRight") { rightPressed = false; } else if (e.key === "Left" || e.key === "ArrowLeft") { leftPressed = false; } if (e.key === "Up" || e.key === "ArrowUp") { upPressed = false; } else if (e.key === "Down" || e.key === "ArrowDown") { downPressed = false; } } function loop() { ctx.clearRect(0, 0, canvas.width, canvas.height); // 计算移动后的目标位置 let targetX = player.x; let targetY = player.y; if (rightPressed) { targetX += player.speed; } else if (leftPressed) { targetX -= player.speed; } if (upPressed) { targetY -= player.speed; } else if (downPressed) { targetY += player.speed; } // 检查目标位置是否碰撞 if (!checkCollision(targetX, targetY)) { player.x = targetX; player.y = targetY; } draw_map(); draw_player(); } function checkCollision(targetX, targetY) { // 玩家的四个边界 const playerLeft = targetX; const playerRight = targetX + player.size; const playerTop = targetY; const playerBottom = targetY + player.size; // 遍历玩家周围的地图块(减少不必要的检测) const startCol = Math.floor(playerLeft / map.tsize); const endCol = Math.ceil(playerRight / map.tsize) - 1; const startRow = Math.floor(playerTop / map.tsize); const endRow = Math.ceil(playerBottom / map.tsize) - 1; for (let col = startCol; col <= endCol; col++) { for (let row = startRow; row <= endRow; row++) { // 跳过超出地图范围的块 if (col < 0 || col >= map.cols || row < 0 || row >= map.rows) continue; const tile = map.getTile(col, row); // 只检测障碍块 if (tile !== 3 && tile !== 4 && tile !== 5 && tile !== 6) continue; const tileLeft = col * map.tsize; const tileRight = tileLeft + map.tsize; const tileTop = row * map.tsize; const tileBottom = tileTop + map.tsize; // 矩形碰撞检测:两个矩形有重叠则碰撞 if (playerRight > tileLeft && playerLeft < tileRight && playerBottom > tileTop && playerTop < tileBottom) { console.log("no sir you cant go there"); return true; } } } return false; } document.addEventListener("keydown", keyDownHandler, false); document.addEventListener("keyup", keyUpHandler, false); setInterval(loop, 10);
HTML部分
<canvas id="myCanvas" width="576" height="512" style="border:1px solid #c3c3c3;"> Your browser does not support the canvas element. </canvas>
额外优化说明
- 新增了
player.speed统一管理移动速度,方便调整 drawTile函数补充了fillRect调用,修复了原代码未实际绘制地图块的问题- 碰撞检测只遍历玩家周围的地图块,减少无效计算提升性能
- 改用
checkCollision函数返回布尔值,逻辑更清晰易维护
内容的提问来源于stack exchange,提问作者Jon Ali
相关产品推荐
相关产品推荐

