XXHolic / blog

I wonder how~, I wonder why~

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Collision Detection :Polygon

XXHolic opened this issue · comments

目录

引子

Collision Detection :Line 中主要介绍了直线相关的碰撞检测,接着来看看更加复杂一些的多边形的情况。

以下示例未做兼容性检查,建议在最新的 Chrome 浏览器中查看。

Polygon/Point

这是示例页面

多边形与点的碰撞检测,需要每一条边与点进行检测,才能确定是否产生了碰撞:

const points = []; // 多边形顶点坐标
const pointsLen = points.length;
let collision = false; // 初始化碰撞状态

for(let index = 0; index < pointsLen; index ++) {
  // checkCondition 边与检测点的判断
  if (checkCondition()) {
    collision = !collision;
  }
}

假设一条边的一个端点的坐标为 [cx,cy] ,另一端点坐标为 [nx,ny],检测点的坐标为 [px,py] ,需要检测的条件为:

if ( ((cy > py) != (ny > py)) && (px < (nx-cx) * (py-cy) / (ny-cy) + cx) ) {
  collision = !collision;
}

这里的判断条件做了两个检测,首先是检查检测点的 Y 坐标是否在两个端点的 Y 坐标范围内:

(cy >= py && ny < py) || (cy < py && ny >= py)
// 等同于
(cy > py) != (ny > py)

61-polygon-point

然后检查监测点的 X 坐标,依赖的方法是 Jordan Curve Theorem :

px < (nx-cx) * (py-cy) / (ny-cy) + cx)

每次检测后,符合条件都需要将 collision 的布尔值取反。直到检测所有的边后,最后的 collision 值才是最终结果。

下面是完整检测逻辑:

/*
 * points 多边形顶点坐标,形式为 [[x1,y1],[x2,y2]]
 * (px,py) 检测点坐标
 */
function checkPolygonPoint({points,px,py}) {
  let collision = false;
  const pointsLen = points.length;

  for (let index = 0; index < pointsLen; index++) {
    const currentPoint = points[index];
    const next = index === pointsLen-1 ? 0:index+1;
    const nextPoint = points[next];
    const [cx,cy] = currentPoint;
    const [nx,ny] = nextPoint;
    const judgeX = px < (nx-cx)*(py-cy) / (ny-cy)+cx;
    const judgeY = (cy >= py && ny < py) || (cy < py && ny >= py);
    if (judgeX && judgeY) {
      collision = !collision;
    }
  }

  return collision;
}

Polygon/Circle

这是示例页面

多边形与圆的碰撞检测,可以分解为多边形的边与圆的碰撞检测,只要有一条边产生了碰撞,就可以进行判定。这个时候可以使用之前介绍的关于 Line/Circle 检测的方法。

下面是完整检测逻辑:

/*
 * points 多边形顶点坐标,形式为 [[x1,y1],[x2,y2]]
 * (cx,cy) 圆心坐标
 * radius 圆半径
 */
function checkPolygonCircle({points,cx,cy,radius}) {
  const pointsLen = points.length;

  for (let index = 0; index < pointsLen; index++) {
    const currentPoint = points[index];
    const next = index === pointsLen-1 ? 0:index+1;
    const nextPoint = points[next];
    const [x1,y1] = currentPoint;
    const [x2,y2] = nextPoint;
    const collision = checkLineCircle({x1,y1,x2,y2,cx,cy,radius});
    if (collision) {
      return true;
    }
  }

  return false;
}

Polygon/Rectangle

这是示例页面

多边形与矩形的碰撞检测,可以分解为多边形的边与矩形的碰撞检测,只要有一条边产生了碰撞,就可以进行判定。这个时候可以使用之前介绍的关于 Line/Rectangle 检测的方法。

下面是完整检测逻辑:

/*
 * points 多边形顶点坐标,形式为 [[x1,y1],[x2,y2]]
 * (rx,ry) 矩形左上角顶点坐标
 * rw 矩形宽度
 * rh 矩形高度
 */
function checkPolygonRectangle({points,rx,ry,rw,rh}) {
  const pointsLen = points.length;

  for (let index = 0; index < pointsLen; index++) {
    const currentPoint = points[index];
    const next = index === pointsLen-1 ? 0:index+1;
    const nextPoint = points[next];
    const [x1,y1] = currentPoint;
    const [x2,y2] = nextPoint;
    const collision = checkLineRectangle({x1,y1,x2,y2,rx,ry,rw,rh});
    if (collision) {
      return true;
    }
  }

  return false;

}

Polygon/Line

这是示例页面

多边形与直线的碰撞检测,可以分解为多边形的边与直线的碰撞检测,只要有一条边产生了碰撞,就可以进行判定。这个时候可以使用之前介绍的关于 Line/Line 检测的方法。

下面是完整检测逻辑:

/*
 * points 多边形顶点坐标,形式为 [[x1,y1],[x2,y2]]
 * (x1,y1) 直线线端点坐标
 * (x2,y2) 直线另一个端点坐标
 */
function checkPolygonLine({points,x1,y1,x2,y2}) {
  const pointsLen = points.length;

  for (let index = 0; index < pointsLen; index++) {
    const currentPoint = points[index];
    const next = index === pointsLen-1 ? 0:index+1;
    const nextPoint = points[next];
    const [x3,y3] = currentPoint;
    const [x4,y4] = nextPoint;

    const collision = checkLineLine({x1,y1,x2,y2,x3,y3,x4,y4});
    if (collision) {
      return true;
    }
  }

  return false;

}

Polygon/Polygon

这是示例页面,刷新页面,会生成随机的多边形。

多边形与多边形的碰撞检测,思路是检测一个多边形任意边是否与另外一个多边形的任意边产生碰撞。这个时候可以使用前面介绍的关于 Polygon/Line 检测的方法。

下面是完整检测逻辑:

/*
 * points1 多边形1顶点坐标,形式为 [[x1,y1],[x2,y2]]
 * points2 多边形2顶点坐标,形式为 [[x1,y1],[x2,y2]]
 */
function checkPolygonPolygon({points1,points2}) {
  const pointsLen = points1.length;

  for (let index = 0; index < pointsLen; index++) {
    const currentPoint = points1[index];
    const next = index === pointsLen-1 ? 0:index+1;
    const nextPoint = points1[next];
    const [x1,y1] = currentPoint;
    const [x2,y2] = nextPoint;
    const collision = checkPolygonLine({points:points2,x1,y1,x2,y2});
    if (collision) {
      return true;
    }
  }

  return false;

}

参考资料

🗑️

善待自己第一步:面对现实。

61-poster