圆与旋转矩形的碰撞检测(下篇)

本文翻译自《Circle and Rotated Rectangle Collision Detection》,敬请勘误。


圆与旋转矩形的碰撞检测

本文我将解释如何实现圆和旋转矩形的碰撞检测。碰撞检测用于确定对象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));}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值