游戏碰撞检测

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;
}   

猜你喜欢

转载自blog.csdn.net/weixin_41301576/article/details/119207354