碰撞检测
1.矩形与矩形碰撞
一般规则的物体碰撞都可以处理成矩形碰撞,实现的原理就是检测两个矩形是否重叠。
矩形1的参数是:左上角的坐标是(x1,y1),宽度是w1,高度是h1;
矩形2的参数是:左上角的坐标是(x2,y2),宽度是w2,高度是h2。
在检测时,数学上可以处理成比较中心点的坐标在x和y方向上的距离和宽度的关系。
即两个矩形中心点在x方向的距离的绝对值小于等于矩形宽度和的二分之一,同时y方向的距离的绝对值小于等于矩形高度和的二分之一。
x方向:| (x1 + w1 / 2) – (x2 +w2/2) | < |(w1 + w2) / 2|
y方向:| (y1 + h1 / 2 ) – (y2 + h2/2) | < |(h1 + h2) / 2 |
2.圆形与圆形碰撞
计算两个圆心之间的距离是否小于两个圆的半径和。
假设
圆形1的左上角坐标是(x1,y1),半径是r1,
圆形2的左上角的坐标是(x2,y2),半径是r2。
下面是数学表达式:
(x1 – x2)2 + (y1 – y2)2 < (r1 + r2)2
3.矩形与圆碰撞
我们会找到AABB上距离圆最近的一个点,如果圆到这一点的距离小于它的半径,那么就产生了碰撞。
难点在于获取AABB上的最近点P¯。下图展示了对于任意的AABB和圆我们如何计算该点:
首先我们要获取球心C¯与AABB中心B¯的矢量差D¯。接下来用AABB的半边长(half-extents)w和h¯来限制(clamp)矢量D¯。长方形的半边长是指长方形的中心到它的边的距离;简单的说就是它的尺寸除以2。这一过程返回的是一个总是位于AABB的边上的位置矢量(除非圆心在AABB内部)。
限制运算把一个值限制在给定范围内,并返回限制后的值。通常可以表示为:
float clamp(float value, float min, float max) {
return std::max(min, std::min(max, value));
}
例如,值42.0f被限制到6.0f和3.0f之间会得到6.0f;而4.20f会被限制为4.20f。
限制一个2D的矢量表示将其x和y分量都限制在给定的范围内。
这个限制后矢量P¯就是AABB上距离圆最近的点。接下来我们需要做的就是计算一个新的差矢量D′¯,它是圆心C¯和P¯的差矢量。
既然我们已经有了矢量D′,我们就可以比较它的长度和圆的半径以判断是否发生了碰撞。
GLboolean CheckCollision(BallObject &one, GameObject &two) // AABB - Circle collision
{
// 获取圆的中心
glm::vec2 center(one.Position + one.Radius);
// 计算AABB的信息(中心、半边长)
glm::vec2 aabb_half_extents(two.Size.x / 2, two.Size.y / 2);
glm::vec2 aabb_center(
two.Position.x + aabb_half_extents.x,
two.Position.y + aabb_half_extents.y
);
// 获取两个中心的差矢量
glm::vec2 difference = center - aabb_center;
glm::vec2 clamped = glm::clamp(difference, -aabb_half_extents, aabb_half_extents);
// AABB_center加上clamped这样就得到了碰撞箱上距离圆最近的点closest
glm::vec2 closest = aabb_center + clamped;
// 获得圆心center和最近点closest的矢量并判断是否 length <= radius
difference = closest - center;
return glm::length(difference) < one.Radius;
}
相关阅读
Axis-aligned bounding boxes(AABB包围盒)原文地址 在游戏中,为了简化物体之间的碰撞检测运算,通常会对物体创建一个规则的几何外形将