分离轴定理 - AABB和圆

时间:2013-04-12 17:33:19

标签: c++ math collision-detection

我已经成功实现了其他碰撞:AABB与AABB,Circle vs Circle,Poly与Poly等,但引入圆圈会带来奇怪的问题,即使理论上实现这些其他碰撞后应该是直接的。根据我的理解,AABB vs Circle实现应该像这样工作:

  
      
  1. 针对碰撞的X轴进行测试
  2.   
  3. 针对碰撞的Y轴进行测试
  4.   
  5. 测试从圆心的轴到AABB最近的顶点
  6.   

我怀疑当我在第3步进行投影时可能会出现问题,但是我使用相同的投影技术进行聚合,这是我在这里用于AABB以及最小和最大投影的圆圈应该是圆圈的中心然后分别加上半径 - 至少我理解它。

我也添加了一些参考图片(黄色=无碰撞,红色=碰撞)
http://imgur.com/a/C2fDO

bool AABBCircleCollision(Point center1, int halfWidth1, int halfHeight1, Point center2, int radius2)
{   

    /***** TEST THE X-AXIS *****/
    double min1, max1, min2, max2;

    min1 = (center1.x - halfWidth1);
    max1 = (center1.x + halfWidth1);
    min2 = (center2.x - radius2);
    max2 = (center2.x + radius2);

    if(max1 <= min2 || max2 <= min1) {
        return false;
    }

    // At this point it is intersecting on the X-axis, now find out if it is on the left or right
    bool onRight;
    if(min2 - max1 > min1 - max2)
        onRight = true;
    else
        onRight = false;

    /***** TEST THE Y-AXIS *****/
    min1 = (center1.y - halfHeight1);
    max1 = (center1.y +  halfHeight1);
    min2 = (center2.y - radius2);
    max2 = (center2.y + radius2);

    if(max1 <= min2 || max2 <= min1) {
        return false;
    }

    // Like for the X-axis, find if the circle is above or below the AABB
    bool onBottom;
    if(min2 - max1 > min1 - max2)
        onBottom = true;
    else
        onBottom = false;

    /***** START TESTING THE AXIS BETWEEN CIRCLE'S CENTER AND CLOSEST VERTEX *****/
    // Get the point of the AABB closest to the circles center
    double vertX, vertY;
    if(onRight)
        vertX = center1.x + halfWidth1;
     else 
        vertX = center1.x - halfWidth1;

    if(onBottom)
        vertY = center1.y + halfHeight1;
    else
        vertY = center1.y - halfHeight1;

    // Get the axis vector between the closest point and the circles center
    double axisX = center2.x - vertX;
    double axisY = center2.y - vertY;

    // Normalise it
    double magV = sqrt(axisX * axisX + axisY * axisY);
    axisX /= magV;
    axisY /= magV;

    // Project the AABB onto the axis
    std::vector<Point> verts; // Here i'm creating the verts on the fly to see if this thing works. USE A BETTER WAY TO HANDLE THIS
    verts.push_back(Point(center1.x - halfWidth1, center1.y - halfHeight1));
    verts.push_back(Point(center1.x + halfWidth1, center1.y - halfHeight1));
    verts.push_back(Point(center1.x + halfWidth1, center1.y + halfHeight1));
    verts.push_back(Point(center1.x - halfWidth1, center1.y + halfHeight1));

    min1 = std::numeric_limits<double>::max(), max1 = -min1; // Reset min and max values

    for(int j=0; j < verts.size(); j++) {
        double proj = (axisX * verts[j].x + axisY * verts[j].y);
        min1 = std::min(proj, min1);
        max1 = std::max(proj, max1);
    }

    // Project the circle onto the axis
    min2 = axisX * (center2.x - radius2) + axisY * (center2.y - radius2);
    max2 = axisX * (center2.x + radius2) + axisY * (center2.y + radius2);

    printf("%f <= %f || %f <= %f\n", max1, min2, max2, min1);
    if(max1 <= min2 || max2 <= min1) {
        return false;
    }

    return true;
}

0 个答案:

没有答案