0.简介
前一篇对碰撞检测的准备工作做了阅读,这回我们看第一步,分离轴定理检测矩形是否相交。
1.分离轴定理
这里面简单对分离轴定理做一个说明,这个里面有详细说明分离轴定理详细说明
大概就是比如两个凸多边形,这两个多边形分别向每条边的垂直方向做投影,如果有一个方向上的投影不相交,则两个多边形不相交,反之则相交。
具体就看上面的链接吧。
2.代码阅读
int Collide(Contact* contacts, Body* bodyA, Body* bodyB)
{
...
//C是一个从B坐标系变换到A坐标系的一个矩阵
Mat22 C = RotAT * RotB;
//经过Abs处理,Abs实际就是将矩阵中每个元素都进行求绝对值
//最后absC中全部都是大于等于0的数字
Mat22 absC = Abs(C);
//这里将absC矩阵转置得到逆矩阵
Mat22 absCT = absC.Transpose();
//1.分离轴计算碰撞
// Box A faces
Vec2 faceA = Abs(dA) - hA - absC * hB;
if (faceA.x > 0.0f || faceA.y > 0.0f)
return 0;
// Box B faces
Vec2 faceB = Abs(dB) - absCT * hA - hB;
if (faceB.x > 0.0f || faceB.y > 0.0f)
return 0;
...
}
上面这段代码,其中,这里就是计算出一个矩阵C,求其绝对值和其逆。
//C是一个从B坐标系变换到A坐标系的一个矩阵
Mat22 C = RotAT * RotB;
//经过Abs处理,Abs实际就是将矩阵中每个元素都进行求绝对值
//最后absC中全部都是大于等于0的数字
Mat22 absC = Abs(C);
//这里将absC矩阵转置得到逆矩阵
Mat22 absCT = absC.Transpose();
如何判断是否相交?
上图是只做A物体分离轴投影的结果,当然物体B也应该做一次,这里没有画出,还是画一下吧。
这回是四个轴的投影相交情况,不难看出四个轴方向只有三个方向没有相交,实际上,只要有一个不相交两个形状就不相交。我们需要得到的就是这四个轴投影的信息,或者说这四个轴投影的重叠情况。上面的四个情况都是在世界坐标下的效果,实际计算的时候,我们需要将其转换到每个矩形的局部坐标系下算。
我们拿四个情况中的以A物体x轴方向为例(上四个图中的第二个小图)。
上图中的a和b点是矩形上的点,分别投影在直线L上,a与b的选择是A最右边的点a和B最左边的点b,这样能两个点是两个矩形上x方向最接近的点,其在L上的投影点与各自的中心点
连线相交与不相交就是两个矩形在x轴方向上的相交结果(根据相似原理),上图中两条红色实线并没有相交。
上面的图是在A物体局部坐标系中表示的,那么的坐标就是
,实际上寻找b点比较容易,因为就是矩形A的最右上角的点,但是寻找a点则不容易,矩形B什么姿态是不固定的,因为在A的局部坐标下,所以A的姿态是确定的,始终都是与坐标轴对齐的,但是矩形B中怎么能确定出a点呢,矩形B的x方向最左边的点,你可能会说,把矩形B的四个点遍历一遍就OK了,这样好吗,这不好。因为四个点都要与变换矩阵相乘一次,并且还要再找到哪个点符合要求,而实际上,只需要一个点与变换矩阵相乘就够了,而这一个点我们也有办法直接求出,不是遍历获得。我们将问题转换到如何找到点c,因为由对称性可知,线段
与线段
的长度是一样的,求出点c也可以解决问题,而点c就是矩形B中x坐标最大的点。同理y轴方向也需要找到y最大的那个点,或者说是找到y最大的那个点的y坐标值。
一个二维旋转矩阵,这个矩阵乘以向量(点)后就会得到空间变换后的向量(点)。矩形有四个点坐标其从一到四象限的符号为(+,+),(+,-),(-,-),(-,+)。当矩阵乘以一个点的时候,例如
,变换后的x的结果应该为
。
上图是sin与cos的函数图像,图中标出了sin(x),cos(x),在不同区域的正负组合,结合公式来看,当确定一个
之后,那么sin与cos的符号已经确定,例如当
的时候,sin与cos符号为(+,+),对于矩形的四个顶点坐标,当坐标符号为(+,-)的时候,
的值最大,此时符号为(+,-)的点是第四象限的点,同理y坐标计算也是如此,当旋转角度变化的时候,sin与cos的符号也在变化,使得
结果最大的矩形顶点也随之变化,但总是四个顶点中的一个,而且哪个顶点使得结果最大取决于sin与cos的符号,这样一来,如果仅仅是获得最大值的话,那么就将矩阵
的每个元素求绝对值,然后去矩形顶点坐标都为正的那个顶点,这样
得出来的结果
中的
与
的值都是变换中四个顶点坐标中最大的x,y。由于得到了这个最大的x,y的结果,假设矩形A的最大x为
,矩形B的最大x为
,那么只需要计算
即可判断x轴方向的投影是否相交。
// Box A faces
//dA是两个矩形终点连线的向量,做了Abs运算后,dA表示的只是数值意义。
//实际上,下面的代码是Abs(dA) - (hA + absC * hB)
//hA就是矩形A右上角的点,absC * hB就是求出B点在A矩形的局部坐标系下的四个顶点中
//x,y方向上的最大值,因为dA,hA,hB都是从(0,0)点出发的向量,所以向量中的数值仅仅
//表示数值意义
//Abs(dA) - (hA + absC * hB)对于这个式子,如果只看x轴
//实际上就是dA的x坐标表示两个矩形的中心连线在x轴投影长度为s,
//hA是矩形A的x方向在中心连线的最大投影长度a,absC*hB就是矩形B的x方向在中心连线的最大投影长度b
//如果s-a-b大于0,这说明投影不相交。
Vec2 faceA = Abs(dA) - hA - absC * hB;
if (faceA.x > 0.0f || faceA.y > 0.0f)
return 0;
// Box B faces
Vec2 faceB = Abs(dB) - absCT * hA - hB;
if (faceB.x > 0.0f || faceB.y > 0.0f)
return 0;