You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复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>

额外优化说明

  1. 新增了player.speed统一管理移动速度,方便调整
  2. drawTile函数补充了fillRect调用,修复了原代码未实际绘制地图块的问题
  3. 碰撞检测只遍历玩家周围的地图块,减少无效计算提升性能
  4. 改用checkCollision函数返回布尔值,逻辑更清晰易维护

内容的提问来源于stack exchange,提问作者Jon Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 16:08:10