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

Canvas 2D矩形碰撞检测实现:碰撞时阻挡玩家移入方块

Canvas 2D 玩家与方块碰撞阻挡实现方案

原代码核心问题

  • 遍历逻辑错误:双层forEach嵌套遍历cubeX和cubeY会把所有X坐标和Y坐标随机配对,生成大量实际不存在的虚拟方块,检测对象从根上就出错。
  • 判定逻辑错误:轴对齐碰撞的判定条件写反,比如Y轴的两个判断条件互斥,永远不可能触发碰撞分支。
  • 缺少碰撞响应逻辑:只留了方向注释,没有实际的坐标修正代码,就算检测到碰撞也不会阻挡玩家移动。

前置数据修正

不推荐分开用两个数组存储方块的X、Y坐标,很容易出现坐标配对错误,优先改成方块对象数组存储属性:

// 推荐数据结构,每个方块的坐标、宽高一一对应
const cubes = [
  {x: 100, y: 100, w: 50, h: 50},
  {x: 220, y: 160, w: 50, h: 50},
  // 其余方块配置
]

如果暂时无法修改原有cubeX/cubeY数组结构,遍历的时候必须按下标配对坐标,禁止双层嵌套遍历:

// 原有数组的正确遍历方式
for (let i = 0; i < cubeX.length; i++) {
  const currentCube = {
    x: cubeX[i],
    y: cubeY[i],
    w: cube.w, // 替换为实际存储方块宽度的变量
    h: cube.h  // 替换为实际存储方块高度的变量
  }
  // 针对当前真实存在的方块做碰撞检测
}

核心碰撞实现

1. 轴对齐矩形(AABB)碰撞判定

两个无旋转的矩形发生碰撞的充要条件是X、Y轴同时存在重叠,封装通用判定函数:

// 传入两个带x/y/w/h属性的矩形,碰撞返回true,否则返回false
function checkAABBCollide(rect1, rect2) {
  return rect1.x < rect2.x + rect2.w &&
         rect1.x + rect1.w > rect2.x &&
         rect1.y < rect2.y + rect2.h &&
         rect1.y + rect1.h > rect2.y
}

2. 碰撞后方向判定与位置修正

碰撞发生后,计算玩家和方块在X、Y轴的重叠深度,重叠量更小的轴就是碰撞的入射方向,沿这个轴把玩家推到方块边界外即可实现阻挡效果:

function fixPlayerCollision() {
  // 遍历所有真实方块
  for (let i = 0; i < cubeX.length; i++) {
    const cube = {
      x: cubeX[i],
      y: cubeY[i],
      w: cube.w,
      h: cube.h
    }
    // 没碰撞直接跳过
    if (!checkAABBCollide(player, cube)) continue

    // 计算X、Y轴的重叠深度
    const overlapX = Math.min(
      player.x + player.w - cube.x, // 玩家从左侧撞方块的重叠量
      cube.x + cube.w - player.x    // 玩家从右侧撞方块的重叠量
    )
    const overlapY = Math.min(
      player.y + player.h - cube.y, // 玩家从顶部撞方块的重叠量
      cube.y + cube.h - player.y    // 玩家从底部撞方块的重叠量
    )

    // 沿重叠量更小的轴修正玩家位置
    if (overlapX < overlapY) {
      // X轴方向碰撞
      player.x = player.x < cube.x ? cube.x - player.w : cube.x + cube.w
    } else {
      // Y轴方向碰撞
      player.y = player.y < cube.y ? cube.y - player.h : cube.y + cube.h
    }
  }
}

渲染循环执行顺序

因为逻辑跑在requestAnimationFrame()驱动的循环里,每帧执行顺序不能乱,否则会出现穿模、碰撞延迟:

  1. 读取WASD按键状态,计算本帧玩家位移,先更新player.x、player.y到未做碰撞检测的目标位置
  2. 调用fixPlayerCollision()遍历所有方块,修正玩家碰撞后的位置
  3. 清空画布,依次绘制所有方块、修正位置后的玩家

注意事项

  • 不要在更新玩家坐标之前做碰撞检测,否则会出现一帧的穿模延迟。
  • 普通WASD移动速度下上述逻辑足够稳定,如果玩家单帧移动距离超过方块厚度(高速移动场景),可以额外补充扫掠碰撞检测避免穿模。
  • 不需要给四个碰撞方向单独写冗余的判定分支,通过重叠深度判断方向的代码更简洁,也不容易出现条件写反的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 03:01:16