【CSON原创】关于旋转矩形的碰撞检测
在网上可以找到很多关于旋转矩形碰撞检测的方法,砖家也有文章对这种碰撞检测作过分析:方向包围盒(OBB)碰撞检测。本文主要是对其中的细节加上一些自己的分析,并结合网上另外一些关于旋转矩形碰撞检测的资料,和大家一起探讨实现的技术细节。
首先推荐一个参考资料,里面对旋转矩形碰撞检测的解释还是挺不错的,并且同样适用于其他多边形碰撞检测,点击下载。
demo预览:
(拖动条调整矩形旋转角度,鼠标点击可移动红色矩形,demo做得比较简陋,大概凑合看吧,意思表达清楚了就行~)
实现原理:
对于两个多边形,如果存在一个轴,使得两个多边形的在该轴上的投影不重叠,则多边形之间没有碰撞发生。
所有可能的轴为垂直于多边形每个边的轴。
实现步骤:
step1:获取矩形的两个轴。
由于矩形对边相互平行,因此平行的两个边共同拥有一条垂直于它们的轴。因此,对于每个矩形,需要用于检测的轴只有两条。我们只需要检测在另一个矩形在该轴上的投影是否和该轴重叠。为了方便,我们可以直接拿矩形相邻的两个边作为两个轴,然后把这两个轴和另一个矩形的四个边作是否有重叠的比较:
var linesArr1=getFourLines(rect1.pointsArr);//矩形1的四个边 var linesArr2=getFourLines(rect2.pointsArr);//矩形2的四个边 //矩形相邻的两个边作为两个轴,并且和另一个矩形的四个边进行投影重叠的比较 if(detectAxisCollision(linesArr2[0],linesArr1)&&detectAxisCollision(linesArr2[1],linesArr1)&&detectAxisCollision(linesArr1[0],linesArr2)&&detectAxisCollision(linesArr1[1],linesArr2)){ return true; } return false;
step2:获取该轴在该向量上的投影以及获取另一个矩形各个边在该轴上的投影。
要获得线段在轴上的投影,我们需要分解为计算线段两个顶点在轴上的投影。如何计算点在轴上的投影?这里附上公式:
//顶点在轴上的投影 var x=((p[0]*axis[0]+p[1]*axis[1])/(axis[0]*axis[0]+axis[1]*axis[1]))*axis[0]; var y=((p[0]*axis[0]+p[1]*axis[1])/(axis[0]*axis[0]+axis[1]*axis[1]))*axis[1];
p为需要被投影的点,axis为投影的目标轴向量。由于之前我们已经把矩形的相邻两个边作为矩形的轴,因此轴向量axis的计算可以为:
var axis=[L[1][0]-L[0][0],L[1][1]-L[0][1]];//轴向量 L为矩形的轴
计算出两个点的投影,就可以得到线段的投影了。
step4:检测每个边在该向量上的投影是否和轴在该向量上的投影重叠。
如何检测线段的重叠?由于这里两个线段是投影在同一个轴向量上,因此他们肯定平行,所以判别方法也比较简单了。方法这里提供一个:线段端点的x轴坐标分别和另一线段的两个端点的x轴坐标相减,得出的两个结果相乘,如果存在结果小于0,则证明线段重叠(当两个线段垂直的时候,使用端点的y轴坐标作判断):
var isLineOverlap=function(l1,l2){//判断线段是否重叠 var l1p1=l1[0],l1p2=l1[1],l2p1=l2[0],l2p2=l2[1]; if(l1p1[0]!=l2p1[0]){//非垂直X轴的两线段 if((l1p1[0]-l2p1[0])*(l1p1[0]-l2p2[0])<0||(l1p2[0]-l2p1[0])*(l1p2[0]-l2p2[0])<0||(l2p1[0]-l1p1[0])*(l2p1[0]-l1p2[0])<0||(l2p2[0]-l1p1[0])*(l2p2[0]-l1p2[0])<0){ return true; } } else{//垂直X轴 if((l1p1[1]-l2p1[1])*(l1p1[1]-l2p2[1])<0||(l1p2[1]-l2p1[1])*(l1p2[1]-l2p2[1])<0||(l2p1[1]-l1p1[1])*(l2p1[1]-l1p2[1])<0||(l2p2[1]-l1p1[1])*(l2p2[1]-l1p2[1])<0){ return true; } } return false; }
step5:如果其中一个轴在向量上的投影和另一个矩形各个边在该向量上的投影都不重叠,则没有碰撞,否则产生碰撞。
只要我们检测到有一条轴和另一个矩形的四个边的在该轴上的投影没有重叠,则可以结束判断,返回没有碰撞了:
//矩形相邻的两个边作为两个轴,并且和另一个矩形的四个边进行投影重叠的比较 if(detectAxisCollision(linesArr2[0],linesArr1)&&detectAxisCollision(linesArr2[1],linesArr1)&&detectAxisCollision(linesArr1[0],linesArr2)&&detectAxisCollision(linesArr1[1],linesArr2)){ return true; } return false;