矩形碰撞检测
进行矩形碰撞检测可能比看起来更复杂。
这不仅仅是弄清楚两个矩形是否相交或重叠,而且我们还需要知道它们碰撞的角度以及它们移动的方向,以便正确地偏转它们,理想情况下将“速度”传递给彼此(质量/能量)等等。
我在这里介绍的方法将执行以下步骤:
- 首先进行简单的相交检测以查明它们是否发生碰撞。
- 如果相交:计算两个矩形之间的角度
- 将一组主矩形划分为圆的四个区域,其中区域 1 位于右侧,区域 2 位于底部,依此类推。
- 根据区域,检查矩形移动的方向,是否根据检测到的区域向另一个矩形偏转。
➔ 在线演示
➔ 这里有更高速度的版本
检测交叉点并计算角度
检测交点和角度的代码如下,其中r1
and r2
这里是具有属性的对象x
, y
, w
and h
.
function collides(r1, r2) {
/// classic intersection test
var hit = !(r1.x + r1.w < r2.x ||
r2.x + r2.w < r1.x ||
r1.y + r1.h < r2.y ||
r2.y + r2.h < r1.y);
/// if intersects, get angle between the two rects to determine hit zone
if (hit) {
/// calc angle
var dx = r2.x - r1.x;
var dy = r2.y - r1.y;
/// for simplicity convert radians to degree
var angle = Math.atan2(dy, dx) * 180 / Math.PI;
if (angle < 0) angle += 360;
return angle;
} else
return null;
}
该函数将返回一个angle or null
然后我们用它来确定循环中的偏转(即:在我们的例子中,角度用于确定击中区域)。这是必要的,以便它们朝正确的方向反弹。
为什么要打区域?
只需进行简单的相交测试和偏转,您就可能面临盒子偏转的风险,如右图所示,这对于 2D 场景来说是不正确的。您希望方框继续朝左侧没有影响的方向移动。
确定碰撞区域和方向
以下是我们如何确定要反转的速度矢量(提示:如果您想要更多physical正确的偏转你可以让矩形“吸收”其他矩形的一些速度,但我不会在这里介绍):
var angle = collides({x: x, y: y, w: 100, h: 100}, /// rect 1
{x: x2, y: y2, w: 100, h: 100}); /// rect 2
/// did we have an intersection?
if (angle !== null) {
/// if we're not already in a hit situation, create one
if (!hit) {
hit = true;
/// zone 1 - right
if ((angle >= 0 && angle < 45) || (angle > 315 && angle < 360)) {
/// if moving in + direction deflect rect 1 in x direction etc.
if (vx > 0) vx = -vx;
if (vx2 < 0) vx2 = -vx2;
} else if (angle >= 45 && angle < 135) { /// zone 2 - bottom
if (vy > 0) vy = -vy;
if (vy2 < 0) vy2 = -vy2;
} else if (angle >= 135 && angle < 225) { /// zone 3 - left
if (vx < 0) vx = -vx;
if (vx2 > 0) vx2 = -vx2;
} else { /// zone 4 - top
if (vy < 0) vy = -vy;
if (vy2 > 0) vy2 = -vy2;
}
}
} else
hit = false; /// reset hit when this hit is done (angle = null)
差不多就是这样了。
The hit
使用标志是为了当我们受到撞击时,我们将“情况”标记为撞击情况,这样我们就不会得到内部偏转(例如,这可能会在高速时发生)。只要我们在命中设置为真后得到一个角度,我们仍然处于相同的命中情况(无论如何理论上)。当我们收到 null 时,我们会重置并准备好迎接新的命中情况。
另外值得一提的是,这里的主要矩形(我们检查其侧面)是第一个矩形(在本例中为黑色)。
两个以上的矩形
如果您想放入两个以上的矩形,那么我建议在矩形本身方面采用与此处使用的不同的方法。我建议创建一个长方形object它在位置、大小、颜色方面是独立的,并且还嵌入了更新速度、方向和绘画的方法。例如,矩形对象可以由执行清除并调用对象的更新方法的主机对象来维护。
要检测碰撞,您可以使用这些对象迭代数组,以找出哪个矩形与当前正在测试的矩形发生碰撞。这里重要的是你“标记”(使用标志)一个已经测试过的矩形,因为碰撞中总是至少有两个,如果你测试 A,然后测试 B,你最终将在不使用用于跳过每帧碰撞“伙伴”对象测试的标志。
综上所述
Note:这里没有介绍一些特殊情况,例如精确角上的碰撞,或者矩形被困在一个边缘和另一个矩形之间(您也可以使用上面提到的命中标志进行边缘测试)。
我没有优化任何代码,但试图使其尽可能简单,以使其更容易理解。
希望这可以帮助!