【问题标题】:Collision detection: Separating Axis Theorem - Circle versus Polygon碰撞检测:分离轴定理 - 圆形与多边形
【发布时间】:2016-06-10 19:50:59
【问题描述】:

我一直在尝试基于Randy Gaul's C++ Impulse Engine 实现圆形和多边形之间的碰撞检测,非常接近代码,但算法永远不会返回 true。

这是JSFiddle。 (为方便起见,正文使用 HTML5 Canvas API 呈现)

一个sn-p的代码(只是碰撞检测):

const circPoly = (a, b) => {
  let data = {},
    center = a.pos;
  data.contacts = [];
  center = b.mat.clone().trans().mult(center.clone().sub(b.pos));
  let sep = -Number.MAX_VALUE,
    faceNorm = 0;
  for (let i = 0; i < b.verts2.length; ++i) {
    let sep2 = b.norms[i].dot(center.clone().sub(b.verts2[i]));
    if (sep2 > a.radius) return data;
    if (sep2 > sep) { sep = sep2; faceNorm = i; }
  }
  let v1 = b.verts2[faceNorm],
    v2 = b.verts2[faceNorm + 1 < b.verts2.length ? faceNorm + 1 : 0];
  if (sep < 0.0001) {
    data.depth = a.radius;
    data.norm = b.mat.clone().mult(b.norms[faceNorm]).neg();
    data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
    return data;
  }
  let dot1 = center.clone().sub(v1).dot(v2.clone().sub(v1)),
    dot2 = center.clone().sub(v2).dot(v1.clone().sub(v2));
  data.depth = a.radius - sep;
  if (dot1 <= 0) {
    if (center.dist2(v1) > a.radius * a.radius) return data;
    let norm = v1.clone().sub(center);
    norm = b.mat.clone().mult(norm);
    norm.norm();
    data.norm = norm;
    v1 = b.mat.clone().mult(v1.clone().add(b.pos));
    data.contacts[0] = v1;
  } else if (dot2 <= 0) {
    if (center.dist2(v2) > a.radius * a.radius) return data;
    let norm = v2.clone().sub(center);
    norm = b.mat.clone().mult(norm);
    norm.norm();
    data.norm = norm;
    v2 = b.mat.clone().mult(v2.clone().add(b.pos));
    data.contacts[0] = v2;
  } else {
    let norm = b.norms[faceNorm];
    if (center.clone().sub(v1).dot(norm) > a.radius) return data;
    norm = b.mat.clone().mult(norm);
    data.norm = norm.clone().neg();
    data.contacts[0] = data.norm.clone().vmult(a.pos.clone().sadd(a.radius));
  }
  return data;
};

请注意,b.verts2 指的是多边形在现实世界坐标中的顶点。

我知道 Vector 类没有问题,但由于我对转换矩阵没有太多经验,因此该类可能是这些错误的根源,尽管它的代码非常多也完全源自 Impulse Engine,所以它应该可以工作。如前所述,该算法总是返回 false,即使确实发生了冲突。我在这里做错了什么?我尝试取出早期返回,但这只会返回奇怪的结果,例如带有负坐标的接触点,这显然不太正确。

编辑:修改了我的矢量类的垂直函数,使其与脉冲引擎的工作方式相同(两种方式都是正确的,但我认为一种是顺时针的,另一种是逆时针的——我还修改了我的顶点以反映逆时针方向)。不幸的是,它仍然没有通过测试。

https://jsfiddle.net/khanfused/tv359kgL/4/

【问题讨论】:

    标签: javascript html canvas collision-detection separating-axis-theorem


    【解决方案1】:

    嗯,有很多问题,我真的不明白你要做什么,因为它看起来过于复杂。例如为什么矩阵有trans???为什么使用 Y 向上屏幕作为变换的坐标系??? (修辞)

    在第一个循环中。

    • 首先是您正在测试法线向量的距离 每个顶点的,应该是测试顶点位置。
    • 您还可以使用vec.dot 函数查找距离 返回距离的平方。但是你测试半径,你 应该测试if(sep2 &lt; radius * radius)
    • 而且你的比较方式应该是错误的 测试是否小于半径平方(不大于)
    • 然后,当您检测到半径内的顶点时,您会返回数据 对象但忘记将在圆圈内找到的顶点放在 data.contacts 数组。
    • 我不知道保持索引的最大意图是什么 遥远的 vect 是,但函数的其余部分对 我???? :( 我试图理解它。

    你需要做的就是

    检查多边形上是否有任何顶点比半径更近,如果是,那么你有一个截距(或完全在里面)

    那你需要检查每条线段的距离

    如果您不需要截距,则可以对每个线段使用以下方法(如果您需要截距,则可以使用以下方法)只使用一个或另一个。

    // circle is a point {x:?,y:?}
    // radius = is the you know what
    // p1,p2 are the start and end points of a line
            checkLineCircle = function(circle,radius,p1,p2){
                var v1 = {};
                var v2 = {};
                var v3 = {};
                var u;
                // get dist to end of line
                v2.x = circle.x - p1.x;
                v2.y = circle.y - p1.y;
                // check if end points are inside the circle
                if( Math.min(
                        Math.hypot(p2.x - circle.x, p2.y - circle.y),
                        Math.hypot(v2.x, v2.y)
                    ) <= radius){
                    return true;
                }
                // get the line as a vector
                v1.x = p2.x - p1.x;
                v1.y = p2.y - p1.y;
                // get the unit distance of the closest point on the line
                u = (v2.x * v1.x + v2.y * v1.y)/(v1.y * v1.y + v1.x * v1.x);
                // is this on the line segment
                if(u >= 0 && u <= 1){
                    v3.x = v1.x * u;  // get the point on the line segment
                    v3.y = v1.y * u;
                    // get the distance to that point and return true or false depending on the 
                    // it being inside the circle
                    return (Math.hypot(v3.y - v2.y, v3.x - v2.x) <= radius);
                }
                return false; // no intercept
          }
    

    对每条线都这样做。为了节省时间,将圆心变换为局部多边形,而不是变换多边形上的每个点。

    如果您需要截取点,请使用以下函数

    // p1,p2 are the start and end points of a line
     // returns an array empty if no points found or one or two points depending on the number of intercepts found
     // If two points found the first point in the array is the point closest to the line start (p1)
     function circleLineIntercept(circle,radius,p1,p2){
            var v1 = {};
            var v2 = {};
            var ret = [];
            var u1,u2,b,c,d;
            // line as vector
            v1.x = p2.x - p1.x;
            v1.y = p2.y - p1.y;
            // vector to circle center
            v2.x = p1.x - circle.x;
            v2.y = p1.y - circle.y;
            // dot of line and circle
            b = (v1.x * v2.x + v1.y * v2.y) * -2;
            // length of line squared * 2
            c = 2 * (v1.x * v1.x + v1.y * v1.y);
            // some math to solve the two triangles made by the intercept points, the circle center and the perpendicular line to the line.
            d = Math.sqrt(b * b - 2 * c * (v2.x * v2.x + v2.y * v2.y - radius * radius));
            // will give a NaN if no solution
            if(isNaN(d)){ // no intercept
                return ret;
            }
            // get the unit distance of each intercept to the line
            u1 = (b - d) / c;
            u2 = (b + d) / c;
    
            // check the intercept is on the line segment
            if(u1 <= 1 && u1 >= 0){  
                ret.push({x:line.p1.x + v1.x * u1, y : line.p1.y + v1.y * u1 });
            }
            // check the intercept is on the line segment
            if(u2 <= 1 && u2 >= 0){  
                ret.push({x:line.p1.x + v1.x * u2, y : line.p1.y + v1.y * u2});
            }
            return ret;
        }
    

    我会让你来做多边形迭代。

    【讨论】:

    • 其实这个函数没有问题——我想通了,我的 Vector 和 Matrix 类有问题。不过还是谢谢。
    猜你喜欢
    • 2012-03-15
    • 1970-01-01
    • 2015-07-24
    • 2014-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多