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

使用JavaScript检测两个div元素碰撞的正确实现方法

太空侵略者碰撞检测实现方案

你当前碰撞逻辑失效的核心原因有两个:

  • 仅使用getBoundingClientRect()返回的元素左上角x、y坐标做比对,完全没有考虑元素自身的宽高占比,本质是在判断「两个坐标点是否完全重合」,自然无法识别矩形重叠
  • 用===做坐标值严格匹配不符合实际运行逻辑:元素通过CSS动画连续移动,10ms间隔的检测很难刚好捕获到两个坐标完全相等的瞬间

2D DOM游戏做矩形碰撞检测,直接用行业通用的AABB(轴对齐包围盒)判定即可,实现简单且性能极高,完全适配你的场景。

实现原理

你对getBoundingClientRect()的返回值理解有误,这个API不止返回左上角坐标,它会直接返回元素四个边界相对于视口的距离:

  • left:元素左边缘距视口左侧的距离
  • top:元素上边缘距视口顶部的距离
  • right:元素右边缘距视口左侧的距离(自动计算了元素宽度)
  • bottom:元素下边缘距视口顶部的距离(自动计算了元素高度)

判定两个矩形碰撞不需要写复杂的重叠计算,反向判断「不碰撞」的场景即可:两个矩形只要不满足以下任意一种情况,就处于重叠状态:

  • 子弹右边缘在敌人左边缘的左侧(子弹在敌人左边)
  • 子弹左边缘在敌人右边缘的右侧(子弹在敌人右边)
  • 子弹下边缘在敌人上边缘的上侧(子弹在敌人上方)
  • 子弹上边缘在敌人下边缘的下侧(子弹在敌人下方)

代码修改

直接把你原有的collision_system函数替换成下面的代码即可,同时优化了原代码硬编码敌人ID的问题,后续增减敌人不需要改检测逻辑,命中后会自动销毁对应敌人和子弹:

const collision_system = () => {
  setInterval(() => {
    const enemies = document.querySelectorAll(".enemies > div");
    const bullet = document.getElementById("bullet");
    // 场上无子弹时直接跳过检测,减少不必要的性能消耗
    if (!bullet) return;

    const bulletRect = bullet.getBoundingClientRect();
    
    enemies.forEach(enemy => {
      // 已经被击毁的敌人直接跳过
      if (!enemy.isConnected) return;
      const enemyRect = enemy.getBoundingClientRect();

      // 碰撞判定
      const isHit = !(
        bulletRect.right < enemyRect.left ||
        bulletRect.left > enemyRect.right ||
        bulletRect.bottom < enemyRect.top ||
        bulletRect.top > enemyRect.bottom
      );

      if (isHit) {
        enemy.remove();
        bullet.remove();
      }
    })
  }, 10);
};

可选优化点

  • 目前你给子弹设置了固定idbullet,后续如果要支持多子弹同时发射,不要用id标识子弹,统一用类名.div_bullet获取所有子弹遍历检测即可,避免同id导致的元素取值异常
  • 用setInterval做游戏循环容易出现掉帧卡顿,后续可以替换为requestAnimationFrame,时间精度更高,动画和检测的同步性更好
  • 现有CSS动画驱动敌人移动的逻辑不需要调整,getBoundingClientRect()可以实时拿到动画过程中的元素位置,不会出现检测偏差

内容的提问来源于stack exchange,提问作者LUCAS DE BIAZI FERREIRA

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 21:00:56