圆与旋转矩形的碰撞检测

来源:互联网 发布:申请淘宝介入流程 编辑:程序博客网 时间:2024/04/24 21:53

圆与旋转矩形的碰撞检测

本文我将解释如何实现圆和旋转矩形的碰撞检测。碰撞检测用于确定对象A是否碰撞了对象B。圆包含圆心位置x,y和一个半径。矩形包含左上角的x,y位置、宽度、长度和一个可以旋转的角度。我们假设矩形沿着它的中心点旋转。


我将使用一个小程序、图片和代码来展示这一点。为了更好地理解圆和不旋转矩形之间的碰撞检测,可以阅读这篇文章


程序示例

这里有一个程序示例来展示碰撞检测。如果检测到碰撞,图形就变为蓝色。绿色轮廓形状表示该用于计算的状态。橙色的线将未旋转圆的圆心与矩形上最近的一个点连接。你可以设置一个角度,点击按钮使其旋转。点击或者拖动某个地方使圆形移动。


设置一个角度实现圆形和矩形碰撞检测

想法很简单。为了计算方便,我们需要在矩形角度为0时,记下两个形状的位置。如果我们将矩形旋转一个角度,计算将会有一点复杂。将矩形保持0度角时候通过简单的加法就可以很容易地获得其他点的位置坐标。左上角为(x,y),右上角为(x+width, y),左下角为(x, y+height)。

c1.png

这里我们不旋转矩形,而是用同样的角度旋转圆。将圆心放在矩形角度为0时的位置。单点比较容易旋转。如下简图所示,蓝色区域代表我们所看到的矩形在旋转。黑色区域用于计算。我们用0度调整形状,换言之,我们将矩形设为0度,用矩形的角度来旋转圆。


所用的公式如下所示。变量cx/cy代表圆心点,originX/originY代表旋转圆的点,x’/y’为旋转点。

x’= cos(theta) * (cx – originX) – sin(theta) * (cy – originY) + originX

y’= sin(theta) * (cx – originX) + cos(theta) * (cy – originY) + originY

c2.png

找到离未旋转的圆和矩形最近的点,我们需要做的就是分别为x和y做if-else判断。


对于x,使用矩形的left x(rx)和right x(rx+width)与圆心x(cx)做比较。如果cx在rx的左边,那么rx离x最近。如果cx在rx+width的右边,那么rx+width离x最近。最后如果以上两点都不符合,那么cx本身离x最近。如下图所示,红色线表示x的位置。

c3.png


对于y,与x的规则相同。如果圆心y(cy)在top y(ry)的上面,那么ry离y点最近。如果cy在bottom y(ry+height)的下面,ry+heigh离y最近。如果以上两点都不符合,那么cy离y最近。

c4.png


最后,我们需要计算未旋转圆的圆心和我们所找到的最近点的距离。我使用勾股定理计算这个最近距离(a^2 + b^2 = c^2)。然后将这个值与圆的半径作比较,如果这个距离远远小于半径,那么这两个图形就会发生碰撞。

c5.png

代码实例

1
2
3
4
5
6
7
8
9
10
11
// Rotate circle's center point backdouble unrotatedCircleX = Math.cos(rect.angle) * (circle.x - rect.centerX) - 
Math.sin(rect.angle) * (circle.y - rect.centerY) + rect.centerX;double unrotatedCircleY  = Math.sin(rect.angle) * (circle.x - rect.centerX) + 
Math.cos(rect.angle) * (circle.y - rect.centerY) + rect.centerY; // Closest point in the rectangle to the center of circle rotated backwards(unrotated)double closestX, closestY; // Find the unrotated closest x point from center of unrotated circleif (unrotatedCircleX  < rect.x)
closestX = rect.x;else if (unrotatedCircleX  > rect.x + rect.width)
closestX = rect.x + rect.width;else
closestX = unrotatedCircleX ; // Find the unrotated closest y point from center of unrotated circleif (unrotatedCircleY < rect.y)
closestY = rect.y;else if (unrotatedCircleY > rect.y + rect.height)
closestY = rect.y + rect.height;else
closestY = unrotatedCircleY; // Determine collisionboolean collision = false; double distance = findDistance(unrotatedCircleX , unrotatedCircleY, closestX, closestY);if (distance < circle.radius)
collision = true// Collisionelse
collision = false;
1
2
3
4
5
6
7
8
9
/**
 * Pythagorean theorem
 * @param fromX
 * @param fromY
 * @param toX
 * @param toY
 */public double findDistance(double fromX, double fromY, double toX, double toY){
 double a = Math.abs(fromX - toX);
 double b = Math.abs(fromY - toY);     return Math.sqrt((a * a) + (b * b));}

0 0