【ゲーム制作】DirectXでAABBの当たり判定をする方法|初心者向けにわかりやすく解説

サムネイル画像 ゲーム制作

今回は、DirectXでの「AABBの当たり判定をする方法」について解説していきます。

「AABBって何?」
「矩形の当たり判定ってどう書けばいいの?」
「見た目は当たっているのに、判定だけズレる…」

こんな疑問はありませんか?

ゲーム制作では、弾と敵、プレイヤーと壁、アイテム取得など、当たり判定はかなり重要です。特に2Dゲームや、3Dでも簡易的な判定では、AABBは軽くて扱いやすい基本手法です。

この記事を読み終えると、あなたはAABBの意味・判定式・実装の流れ・ズレやすいポイントを理解できると思いますので、ぜひ最後まで読んでいただけると嬉しいです。

AABBとは?

AABB とは、Axis Aligned Bounding Box の略です。

日本語だと、座標軸に平行な矩形の当たり判定というイメージです。簡単に言うと、回転していない四角形同士が重なっているかを調べる方法です。

たとえば、プレイヤー・敵・弾の見た目が多少複雑でも、まずは四角形で包んで判定することで、軽くて実装しやすい当たり判定が作れます。

なぜAABBが必要?

AABBがよく使われる理由は、実装がシンプルで高速だからです。

  • 2Dゲームで扱いやすい
  • 弾や敵の数が多くても比較的軽い
  • まず動く当たり判定を作りやすい

特にDirectXの自主制作では、最初から複雑な当たり判定を作るよりも、まずAABBでしっかり動かす方がかなり大事です。

DirectXで作るかUnityで作るか迷っている方は、DirectXとUnityの違いを解説した記事も参考になります。

AABB判定の基本

AABBでは、矩形が次のどれかに当てはまると当たっていないと判断できます。

  • AがBより左にある
  • AがBより右にある
  • AがBより上にある
  • AがBより下にある

逆に言うと、これらのどれにも当てはまらなければ、重なっているのでヒットです。

▼main.cpp

#include <iostream>

struct AABB {
    float x;
    float y;
    float width;
    float height;
};

bool IsHit(const AABB& a, const AABB& b) {
    if (a.x + a.width < b.x) return false;
    if (b.x + b.width < a.x) return false;
    if (a.y + a.height < b.y) return false;
    if (b.y + b.height < a.y) return false;

    return true;
}

int main() {
    AABB player = {100.0f, 100.0f, 64.0f, 64.0f};
    AABB enemy  = {140.0f, 120.0f, 64.0f, 64.0f};

    if (IsHit(player, enemy)) {
        std::cout << "当たっています\n";
    } else {
        std::cout << "当たっていません\n";
    }

    return 0;
}

実行結果

当たっています

このコードでは、xy を左上座標、widthheight を大きさとして扱っています。

実践での使い方

ゲームでは、毎フレーム座標が変わるので、更新後の位置で当たり判定を行います。

たとえば、弾と敵の判定ならこんな形になります。

▼main.cpp

#include <iostream>

struct AABB {
    float x;
    float y;
    float width;
    float height;
};

bool IsHit(const AABB& a, const AABB& b) {
    return !(a.x + a.width < b.x ||
             b.x + b.width < a.x ||
             a.y + a.height < b.y ||
             b.y + b.height < a.y);
}

int main() {
    AABB bullet = {300.0f, 200.0f, 16.0f, 16.0f};
    AABB enemy  = {290.0f, 190.0f, 64.0f, 64.0f};

    if (IsHit(bullet, enemy)) {
        std::cout << "弾が敵にヒットしました\n";
    } else {
        std::cout << "まだ当たっていません\n";
    }

    return 0;
}

実行結果

弾が敵にヒットしました

実際のDirectXゲームでは、この判定結果を使って、敵のHPを減らしたり、弾を消したり、SEを鳴らしたりします。

【重要】私が実際にAABBで困った体験談

私も自主制作の2Dゲームで、AABBの当たり判定が見た目とズレてかなり困ったことがあります。

原因は、描画は画像の中心座標を基準にしていたのに、当たり判定は左上座標として計算していたことでした。そのせいで、見た目では当たっているのに判定だけ少し右下にズレる状態になっていました。

最初は描画処理がおかしいのかと思っていたのですが、矩形の数値をログで出してみると、座標の基準がそもそも揃っていませんでした。以降は、描画の基準点と当たり判定の基準点を必ず統一するようにしています。

こういうズレは目で見ただけだと原因が分かりづらいので、ログ出力やブレークポイントで確認するのがかなり有効です。デバッグ方法については、C++でのデバッグのやり方の記事もあわせて読むのがおすすめです。

AABB使用時のよくある失敗例と対処法

  • 左上座標と中心座標を混ぜてしまう
    描画と判定で基準点が違うと、当たり判定がズレます。必ずどちらかに統一しましょう。
  • width と height の意味を間違える
    半分のサイズなのか、全体のサイズなのかを曖昧にするとズレやすいです。変数名をはっきりさせるのが大切です。
  • 境界が触れた時の扱いを決めていない
    <<= の違いで、「ぴったり接した時に当たりとするか」が変わります。ゲーム仕様に合わせて選びましょう。

クラスで管理すると見やすくなる

オブジェクト数が増えてくると、AABBをクラスや構造体でまとめて管理した方が見やすくなります。

▼HitBox.h

#pragma once

struct AABB {
    float x;
    float y;
    float width;
    float height;
};

▼Collision.h

#pragma once
#include "HitBox.h"

bool IsHit(const AABB& a, const AABB& b);

▼Collision.cpp

#include "Collision.h"

bool IsHit(const AABB& a, const AABB& b) {
    return !(a.x + a.width < b.x ||
             b.x + b.width < a.x ||
             a.y + a.height < b.y ||
             b.y + b.height < a.y);
}

このように分けておくと、プレイヤー・敵・弾など、いろいろな場所から同じ判定関数を使い回せます。

クラスや構造体でまとめる考え方は、クラスの記事もあわせて読むとより理解しやすいです。

注意点

  • AABBは回転していない矩形に強いですが、斜めに回転するオブジェクトには向きません
  • 見た目どおりの厳密な判定をしたい場合は、円判定やOBB、ピクセル判定が必要になることもあります
  • まずはAABBで作って、必要になったら精度を上げる流れがおすすめです

まとめ

  • AABBは、座標軸に平行な矩形同士の当たり判定
  • 軽くて実装しやすく、2Dゲームで特に使いやすい
  • 「左・右・上・下に完全に離れていないか」を調べれば判定できる
  • 描画の基準点と判定の基準点を揃えることがかなり重要
  • まずはAABBで作って、必要なら後で精度を上げるのが実践的

AABBは、ゲーム制作でかなり登場頻度の高い基本技術です。

最初は地味に見えるかもしれませんが、ここをしっかり理解しておくと、弾・敵・壁・アイテムなどいろいろな判定に応用しやすくなります。

ここまで読んでくださり、ありがとうございました。

関連記事