AABB 代表的是轴对齐碰撞盒 (Axis-aligned Bounding Box)
AABB碰撞盒是指与场景基础坐标轴(2D中的是x和y轴)对齐的长方形的碰撞外形。与坐标轴对齐意味着这个长方形没有经过旋转并且它的边线和场景中基础坐标轴平行(例如,左右边线和y轴平行)。这些碰撞盒总是和场景的坐标轴平行,这使得所有的计算都变得更简单。
一般在2D环境下,常用的碰撞盒有一下几种种。
- 1.BOX(方形) 碰撞
- 2.Sphere(圆形) 碰撞
- 3.BOX&Sphere(方形与圆形) 碰撞
特殊形状的碰撞盒不在本篇文章的讨论范围。
问题:Unity已经有了自带的2D 和 3D 碰撞体以及一套完整且成熟的物理系统,我们为什么不能直接使用Unity自带的,反而还要去编写这个系统。
答:问的好。当你看到这篇文章的时候,说明你的心中已经有了准确的答案。不然也不会进到这篇文章中。
首先肯定是Unity的物理系统,无法满足我们的特殊需求。
那特殊需求都有哪些呢?
1.我们需要编写一套基于定点数的碰撞检测系统。
2.我们不想用Unity的碰撞检测,想自己编写一套属于自己的碰撞系统。
3.纯纯个人兴趣,来学习如何碰撞系统的检测。
相信肯定是第一种占的人数比较多。
既然有需求,那就满足你们。
首先说下2D场景下 BOX&BOX碰撞的原理:
由于AABB碰撞是基于坐标轴对齐的,所以我们检测起来就非常简单。只需要检测两个碰撞盒是否有重合即可,也就是X轴和Y轴上是否有重合或相交。
判断原理:我们只需要检测第一个物体的最右的点是否大于第二个物体的最左侧的点,并且第二个物体的最右侧的点是否大于第一个物体的最左侧的点。Y轴检测原理也是如此
public class FixIntBoxCollider2D : FixIntCollider2D
{
public float x { get { return transform.position.x; } }
public float y { get { return transform.position.y; } }
public Vector2 size = new Vector2(1, 1);
public Vector2 conter;
protected float mBoxWidth { get { return size.x / 2; } }
protected float mBoxHeigth { get { return size.y / 2; } }
public override bool DetectionBoxCollider(FixIntBoxCollider2D target)
{
//第一个物体的最右侧是否大于第二个物体的最左侧 并且 第二个物体的最右侧是否大于第一个物体的最左侧;
bool isCollider_x = x + mBoxWidth >= target.x - target.mBoxWidth
&& target.x + target.mBoxWidth >= x - mBoxWidth;
bool isCollider_y = y + size.y >= target.y - target.mBoxHeigth
&& target.y + target.mBoxHeigth >= y - mBoxHeigth;
return isCollider_x && isCollider_y;
}
}
BOX与Shpere碰撞检测相较于 BOX&BOX 以及 Shpere&Shpere碰撞检测就稍微复杂一点。 不过也不是特别复杂,因为博主已经给大家画好了碰撞检测流程图,以及碰撞检测计算公式。
其实我们都只到,当球体与BOX平行X轴或Y轴时,我们只需要计算二者之间的半径即可检测出是否发生重叠和相交。
BOX与Shpere碰撞检测主要复杂在当一个球形碰撞盒处于BOX碰撞盒边角时的处理。
这个时候我们已经不能单纯的通过二者之间的距离和半径去判断是否发生碰撞了。因为BOX从中心点到边角的距离是大于BOX的半径的。所以单纯这样检测是不准的。
而准确的做法是获取Shpere球体距离BOX最近的处于BOX碰撞盒上的一个点。然后在由球体半径减去该点得出向量长度,最后计算该向量是否<球体半径,若小于则产生碰撞,否则说明两个碰撞体不重叠。
关于BOX距离球体最近的一个点获取原理:
1.P.y获取原理: (P点如下图位置)
首先获取 球体A的Y点,称作为A.y。
然后获取BOX碰撞体D的Y点坐标。称作为D.y。
用 A.y-D.y 就得到了一个值Y_Dis。当然这个值可能是大于Box的高度的。也就是超出了Box碰撞盒的区域。所以我们需要通过Clamp(Y_Dis,-D.height/2,D.height/2)强制限制该值,让该值处于Box碰撞盒的范围之内。然后用D.y+Y_Dis就得到了P.y。
通过这种方式我们就得到了P.y。然后就是获取P.x
2.P.x获取原理:
首先获取 球体A的X点坐标,称作为A.x。
然后获取BOX碰撞体D的X点坐标。称作为D.x。
用 A.x-D.x 就得到了一个值X_Dis。当然这个值也是可能是大于Box的宽度的。也会超出Box碰撞盒的区域。所以我们需要通过Clamp(X_Dis,-D.Width/2,D.Width/2)强制限制该值,让该值处于Box碰撞盒的范围之内。然后用D.x+X_Dis就得到了P.x。
通过这种方式我们就得到了P.x。
通过上面两步我们就得到了距离 Shpere球体A 最近的一个处于BOX碰撞盒上的一个点P。
最后用球体的位置减去P点的位置得出一个向量。这个向量如果小于 Shpere球体A 的半径,则说明发生了碰撞。
碰撞原理和计算公式如下图:
public class FixIntBoxCollider2D : FixIntCollider2D
{
public float x { get { return transform.position.x; } }
public float y { get { return transform.position.y; } }
public Vector2 size = new Vector2(1, 1);
public Vector2 conter;
protected float mBoxWidth { get { return size.x / 2; } }
protected float mBoxHeigth { get { return size.y / 2; } }
public override bool DetectionSphereollider(FixIntSphereCollider2D target)
{
float xDis = target.x - x;
float p_x = Mathf.Clamp(xDis, -mBoxWidth, mBoxWidth);
float yDis = target.y - y;
float p_y = Mathf.Clamp(yDis, -mBoxHeigth, mBoxHeigth);
Vector2 closePoint = new Vector2(x+p_x,y+p_y);
return Vector2.Distance(target.transform.position,closePoint)<=target.radius;
}
}
球体与球体的碰撞就在简单不过了,只需要判断半径即可。
原理就是判断A与B之间的向量是否 <= A.radius+B.raduis。小于则说明产生碰撞,大于则不碰撞。
public class FixIntSphereCollider2D : FixIntCollider2D
{
public float radius;
public float x { get { return transform.position.x; } }
public float y { get { return transform.position.y; } }
public override bool DetectionSphereollider(FixIntSphereCollider2D target)
{
// 判断A与B之间的向量是否 <= A.radius+B.raduis。小于则说明产生碰撞,大于则不碰撞。
bool isCollider = Vector2.Distance(transform.position,target.transform.position) <= (radius + target.radius) ;
return isCollider;
}
public override bool DetectionBoxCollider(FixIntBoxCollider2D target)
{
throw new System.NotImplementedException();
}
}
文章来自于铸梦老师,铸梦之路系列课程。
想了解更多框架、帧同步技术、UGUI优化相关技术可在企鹅kt搜索 铸梦xy。