ゲーム開発

ゲーム開発入門② 座標と当たり判定を理解しよう

左端・右端の計算から始め、四角形の当たり判定を4段階で読み解きます。数値例、端が触れる場合、一度だけ加点する実行例、練習の解答を掲載します。

コインの上を通ったのに拾えない。逆に、まだ離れているように見えるのに当たる。ゲームを作っていると、位置の扱いで「あれ?」となる場面が出てきます。

今回は、四角形のプレイヤーとコインを題材に、位置の表し方から当たり判定まで進みます。いきなり長い条件式を覚える必要はありません。まず、左右の端を足し算で求めるところからです。

XとYで位置を表す

2Dの位置には、横方向の X座標と縦方向の Y座標を使います。この記事では、ブラウザーのCanvasなどでよく使う「左上が (0, 0)、右と下が正」の座標を採用します。

(0, 0) ───────→ Xが増える
  │
  │
  ↓ Yが増える

数学のグラフでは上へ行くほどYが増えることが多いので、ここは少し戸惑いますよね。「ゲームだから必ず下が正」ではなく、使うエンジンや画面の座標の決まりを確認してください。

位置が (40, 20) なら、左端から右へ40、上端から下へ20です。右へ10進むならXだけを増やします。

let x = 40;
let y = 20;

x += 10;
console.log(x, y);

出力は 50 20。上下へ動いていないのでYは20のままです。

動き 計算
右へ進む x = x + 距離
左へ進む x = x - 距離
下へ進む y = y + 距離
上へ進む y = y - 距離

1回の移動距離を速さと経過時間から求める方法は、ゲームループの記事で説明しています。

位置だけでなく大きさも持つ

点には大きさがありませんが、キャラクターには幅と高さがあります。ここでは x と y を四角形の左上とし、次の4つで表します。

const player = {
  x: 40,
  y: 20,
  width: 32,
  height: 24
};

これはJavaScriptのオブジェクトです。x や width のような名前と値をまとめています。player.x と書くと、この中のX座標を使えます。

端の位置を計算すると、次のようになります。

端 計算 値
左 x 40
右 x + width 72
上 y 20
下 y + height 44

大切なのは、「右端=幅」ではないことです。幅32は大きさ、右端72は場所です。位置と大きさを混ぜると、画面の左側では動くのに右側で判定がおかしい、といった不具合につながります。

エンジンによっては位置が画像の中心を表します。その場合の左端は x - width / 2 です。この記事の式を使うなら、左上基準へそろえてから比べます。

四角形の重なりを調べる

回転していない、軸に平行な四角形の判定を AABB判定と呼びます。Axis-Aligned Bounding Boxの略で、「座標の軸に沿った囲みの箱」という意味です。

1. まず横方向だけを見る

プレイヤーの横範囲は40〜72、コインの横範囲を60〜76とします。

  • プレイヤーの左40は、コインの右76より左
  • プレイヤーの右72は、コインの左60より右

この両方が成立すると、横方向に共通する部分があります。40〜72と60〜76なら、60〜72が重なっています。

2. 縦方向も同じように見る

プレイヤーの縦範囲は20〜44、コインは30〜46とします。

  • プレイヤーの上20は、コインの下46より上
  • プレイヤーの下44は、コインの上30より下

縦も重なっています。横だけ重なっていても、上下に離れていれば衝突しません。 横と縦の両方を見る必要があるんですね。

3. 4つの条件を関数にする

function isHit(a, b) {
  return (
    a.x < b.x + b.width &&
    a.x + a.width > b.x &&
    a.y < b.y + b.height &&
    a.y + a.height > b.y
  );
}

関数は、処理を名前でまとめて再利用する仕組みです。a と b に2つの四角形を渡すと、return で結果を返します。

&& は「かつ」。4条件がすべて正しいときだけ true、どれか1つでも違えば false になります。true と false は、はい・いいえを表す真偽値です。

この方法では、幅と高さが0より大きく、同じ座標の基準を使う四角形を想定しています。

4. 端がぴったり触れたら?

プレイヤーの右端72と、コインの左端72が一致する場合を考えます。

式は 72 > 72 になるので false。今回の関数は、面積を持って重なる場合だけ当たりとし、端に触れただけでは当たりにしません。

端への接触も含めたいなら、< と > をそれぞれ <= と >= に変える選択肢があります。どちらが正しいかは、ゲームのルール次第です。式の意味を決めずに記号を変えると、壁際などの動作が不安定になるので、先に決めておきましょう。

当たった後の処理を書く

判定の式だけではゲームは変わりません。「当たったら何をするか」を別に書きます。

次は、コンソールまたはNode.jsでそのまま実行できる例です。前の関数も含めて、独立したコードにしています。

function isHit(a, b) {
  return (
    a.x < b.x + b.width &&
    a.x + a.width > b.x &&
    a.y < b.y + b.height &&
    a.y + a.height > b.y
  );
}

const player = { x: 40, y: 20, width: 32, height: 24 };
const coin = { x: 60, y: 30, width: 16, height: 16, active: true };
let score = 0;

function collectCoin() {
  if (coin.active && isHit(player, coin)) {
    score += 10;
    coin.active = false;
  }
}

collectCoin();
console.log(score, coin.active);

collectCoin();
console.log(score, coin.active);
10 false
10 false

1回目はコインが有効で重なっているので、10点を加算して active を false にします。2回目は最初の条件が成立せず、10点のままです。

非表示にするだけでは足りない

「画像を消したから大丈夫」と思いやすいのですが、表示と判定は別です。visible = false にしても、独自に書いた判定コードはその値を見なければ動き続けます。

この例では、判定に参加するかを表す active を条件で確認することで二重取得を防いでいます。描画する側でも、active が真のコインだけを描くようにします。

ゲームループは何度も回るので、一度だけ起こす処理には「もう処理済みか」という情報が必要になるんですね。

判定と見た目を分ける

画像の透明な部分まで含めた四角形で判定すると、離れて見えるのに当たることがあります。そこで、見えない判定用の形を別に置きます。こうした形はコライダー、用途によってヒットボックスなどと呼ばれます。

例えば64×64の画像に、中央の40×48だけを判定範囲として使うなら、左上を画像の位置から右へ12、下へ8ずらします。

ただし、小さくすれば必ず遊びやすいわけではありません。地面に立つ足元、攻撃が届く範囲、コインの取りやすさでは目的が違います。開発中は判定枠も描いて、見た目と重なりを確認すると原因が見つけやすくなります。

この判定でできないこと

AABBは分かりやすい方法ですが、万能ではありません。

  • 回転した形や円の輪郭を、ぴったり判定するものではない
  • 重なった後、どの方向へ押し戻すかまでは決めない
  • 1回の更新で薄い壁を飛び越えると、重なりを見逃すことがある

最後の問題をすり抜けと呼ぶことがあります。現在位置だけを見るため、前の位置では壁の左、次の位置では壁の右にいると、途中の接触が分かりません。

移動を細かく分ける、移動経路を判定する、エンジンの連続衝突判定を使うなどの対応があります。小さな学習用なら、まず速さを控えめにし、「重なり判定」と「通った経路の判定」は違うと覚えておけば十分です。

練習:右端の座標を求める

問題1:プレイヤーの x が40、width が32のとき、右端はいくつでしょうか。

問題2:今回の isHit で、コインを x = 72、y = 30 にした場合は当たりますか。コインの大きさは16×16のままとします。

模範解答

  1. 右端は 40 + 32 = 72
  2. false。横方向は端が触れるだけだからです

解説

縦方向は重なっていますが、player.x + player.width > coin.x が 72 > 72 となり成立しません。4条件すべてが必要なので、全体も false になります。

もしコインのXを71へ変えると、横に1だけ重なるため true になります。このように境目の前後を試す確認を境界値のテストと呼びます。ちょうど触れる・1だけ離れる・1だけ重なる、を調べると記号の間違いを見つけやすくなります。

次に読む記事と出典

当たり判定ができたら、タイトル・プレイ・ゲームオーバーの切り替えへ進みましょう。得点や体力を「どの画面で更新するか」まで分けると、ゲームらしい形になります。

← 記事一覧へ戻る