Skip to main content

当たり判定やろう

球とAABBの当たり判定

ゲームの当たり判定では、複雑なモデルをそのまま比較するのではなく、扱いやすい形で近似して判定することがあります。

代表的な形が 球(Sphere) と AABB です。


1.球と球の当たり判定

球は、中心と半径で表せます。

球と球の当たり判定

2つの球がちょうど接するとき、中心間距離 (d) は半径の合計と等しくなります。

$$ d=r_A+r_B $$

したがって、

$$ d\leq r_A+r_B $$

なら、2つの球は接触または重なっています。

当たり判定では平方根を求めず、

$$ d^2\leq(r_A+r_B)^2 $$

を比較できます。

struct Sphere
{
    Vector3 center;
    float radius;
};
bool IsHit(const Sphere& a, const Sphere& b)
{
    Vector3 diff = b.center - a.center;

    float distanceSq =
        diff.x * diff.x +
        diff.y * diff.y +
        diff.z * diff.z;

    float radiusSum = a.radius + b.radius;

    return distanceSq <= radiusSum * radiusSum;
}

2.AABB

AABBは Axis-Aligned Bounding Box の略です。

まず2次元で min と max の意味を見ます。

AABBのminとmax(2D)

3次元ではZ方向が追加されます。

AABBのminとmax(3D)

AABBは、

$$ \mathrm{min}=(x_{min},y_{min},z_{min}) $$

$$ \mathrm{max}=(x_{max},y_{max},z_{max}) $$

で表せます。


3.AABBとAABBの当たり判定

2次元では、X方向とY方向の両方で区間が重なれば矩形どうしが重なっています。

AABBとAABBの当たり判定(2D)

3次元ではZ方向も調べます。

AABBとAABBの当たり判定(3D)

X、Y、Zのどれか1方向でも完全に離れていれば、2つのAABBは衝突していません。

struct AABB
{
    Vector3 min;
    Vector3 max;
};
bool IsHit(const AABB& a, const AABB& b)
{
    if (a.max.x < b.min.x || a.min.x > b.max.x)
    {
        return false;
    }

    if (a.max.y < b.min.y || a.min.y > b.max.y)
    {
        return false;
    }

    if (a.max.z < b.min.z || a.min.z > b.max.z)
    {
        return false;
    }

    return true;
}

4.球とAABBの当たり判定

球とAABBでは形が違うため、中心どうしの距離だけでは判定できません。

AABB上で、球の中心に最も近い点を求めます。

その点を最近点 (Q)、球の中心を (C) とします。

球とAABBの最近点

最近点 (Q) が分かれば、

$$ |C-Q| $$

は球の中心からAABBまでの最短距離になります。


5.clampで最近点を求める

clamp は、値を指定した区間の中へ収める処理です。

clampを1次元で考える

X座標とY座標を別々にclampすると、2次元AABB上の最近点を求められます。

clampで2次元AABB上の最近点を求める

3次元ではZ座標も同じように計算します。

$$ Q_x=\mathrm{clamp}(C_x,x_{min},x_{max}) $$

$$ Q_y=\mathrm{clamp}(C_y,y_{min},y_{max}) $$

$$ Q_z=\mathrm{clamp}(C_z,z_{min},z_{max}) $$


6.最近点のできる場所

球の中心の位置によって、最近点のできる場所は変わります。

最近点のできる場所(面・側面)

最近点のできる場所(角・内部)

面・側面・角・内部を別々の式で処理する必要はありません。

各成分を clamp すれば、同じ方法で最近点を求められます。


7.球とAABBの判定式

最近点 (Q) が求まったら、

$$ \mathbf{v}=C-Q $$

を作ります。

球とAABBの判定式

衝突条件は、

$$ |\mathbf{v}|^2\leq r^2 $$

です。

bool IsHit(const Sphere& sphere, const AABB& box)
{
    Vector3 closest;

    closest.x = std::clamp(
        sphere.center.x,
        box.min.x,
        box.max.x);

    closest.y = std::clamp(
        sphere.center.y,
        box.min.y,
        box.max.y);

    closest.z = std::clamp(
        sphere.center.z,
        box.min.z,
        box.max.z);

    Vector3 diff = sphere.center - closest;

    float distanceSq =
        diff.x * diff.x +
        diff.y * diff.y +
        diff.z * diff.z;

    return distanceSq <= sphere.radius * sphere.radius;
}

8.3種類の判定を整理する

3種類の当たり判定

判定 調べるもの 数学的な考え方
球 vs 球 中心間距離 2点間の距離
AABB vs AABB X・Y・Z方向の範囲 区間の重なり
球 vs AABB 球中心と最近点の距離 clamp+2点間の距離