You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多
文档控制台
免费开始使用

圆形与矩形的Swept AABB碰撞检测误判问题求助

问题分析与解决方案

你的问题出在两个核心地方:矩形膨胀方式错误,以及Swept AABB的时间计算未考虑运动方向,导致边角误判。

错误原因

  1. 矩形膨胀逻辑错误
    你提到“将矩形的x、y方向范围扩大2倍圆形半径”,但正确的圆-矩形碰撞转换应该是将原矩形的每个边界向外偏移1倍圆半径,而非整体扩大2倍。如果只是单纯增加矩形宽高而不调整起始坐标,会导致膨胀后的矩形位置偏移,把离原矩形边角超过半径的区域也纳入碰撞检测范围,从而产生误判。

  2. 运动方向未处理导致时间计算颠倒
    原代码中直接计算进入/退出时间,但没有根据速度方向交换时间顺序。当点向矩形内部反向移动时,原本的进入时间会变成退出时间,反之亦然,导致时间区间计算错误,误判路径与矩形相交。

修正后的实现

第一步:正确膨胀矩形

假设圆半径为r,原矩形参数为rectx, recty, rectw, recth,膨胀后的矩形参数应调整为:

const r = 你的圆半径;
const expandedRectX = rectx - r;   // 左边界左移r
const expandedRectY = recty - r;   // 上边界上移r
const expandedRectW = rectw + 2*r; // 宽度增加2r(左右各扩r)
const expandedRectH = recth + 2*r; // 高度增加2r(上下各扩r)

第二步:修正Swept AABB函数

function sweptAABB(pointX, pointY, velX, velY, deltaTime, rectX, rectY, rectW, rectH) {
  // 计算当前帧内点的总位移
  const moveX = velX * deltaTime;
  const moveY = velY * deltaTime;

  // 矩形的边界
  const rectLeft = rectX;
  const rectRight = rectX + rectW;
  const rectTop = rectY;
  const rectBottom = rectY + rectH;

  // 计算x方向的进入/退出时间
  let tEnterX, tExitX;
  if (moveX === 0) {
    // x方向无移动,判断是否已在矩形内
    tEnterX = (pointX >= rectLeft && pointX <= rectRight) ? -Infinity : Infinity;
    tExitX = (pointX >= rectLeft && pointX <= rectRight) ? Infinity : -Infinity;
  } else {
    tEnterX = (rectLeft - pointX) / moveX;
    tExitX = (rectRight - pointX) / moveX;
    // 速度为负时,进入/退出时间颠倒,交换两者
    if (moveX < 0) [tEnterX, tExitX] = [tExitX, tEnterX];
  }

  // 计算y方向的进入/退出时间
  let tEnterY, tExitY;
  if (moveY === 0) {
    // y方向无移动,判断是否已在矩形内
    tEnterY = (pointY >= rectTop && pointY <= rectBottom) ? -Infinity : Infinity;
    tExitY = (pointY >= rectTop && pointY <= rectBottom) ? Infinity : -Infinity;
  } else {
    tEnterY = (rectTop - pointY) / moveY;
    tExitY = (rectBottom - pointY) / moveY;
    // 速度为负时,交换进入/退出时间
    if (moveY < 0) [tEnterY, tExitY] = [tExitY, tEnterY];
  }

  // 整体进入时间是两个方向的最晚进入时间,退出时间是两个方向的最早退出时间
  const enterTime = Math.max(tEnterX, tEnterY);
  const exitTime = Math.min(tExitX, tExitY);

  // 碰撞判定:路径与矩形有交集,且交集在当前帧时间范围内(0到1)
  return enterTime <= exitTime && exitTime >= 0 && enterTime <= 1;
}

原理说明

Swept AABB的核心是计算点(圆心)在当前帧内的运动路径与膨胀矩形的时间交集:

  • enterTime:点完全进入矩形的最早时间(0=帧开始,1=帧结束)
  • exitTime:点完全离开矩形的最晚时间
  • enterTime <= exitTime时,说明路径与矩形有交集;同时确保交集落在当前帧范围内(exitTime >=0enterTime <=1),即可判定碰撞。

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

火山引擎 最新活动