コンテンツにスキップ

ボールキャッチゲームでの当たり判定

上から落ちてくるボールと、それ受け止めるバーの当たり判定をします。ブロック崩しなどにも応用できます。

1.当たり判定がない場合のプログラム

以下は当たり判定のないプログラムです。ボールはバーを素通りしていきます。

float ball_x;
float ball_y;
float catcher_x;
int migi, hirdari;
void setup() {
size(600, 400);
ball_x = 300;
ball_y = 0;
catcher_x = 0;
}
void draw() {
background(255);
ball_y += 2;
if (ball_y > 400) {
ball_y = 0;
ball_x = 300;
}
fill(0, 255, 0);
ellipse(ball_x, ball_y, 50, 50);
catcher_x += (migi + hirdari);
fill(255, 0, 0);
rect(catcher_x, 300, 100, 30);
}
void keyPressed() {
if (keyCode == RIGHT) {
migi = 5;
}
if (keyCode == LEFT) {
hirdari = -5;
}
}
void keyReleased() {
if (keyCode == RIGHT) {
migi = 0;
}
if (keyCode == LEFT) {
hirdari = 0;
}
}

※上記のプログラムは、左右矢印キーでバーを スムーズに 動かせるように工夫していますので、ちょっと見慣れないコードかもしれません。詳しくは「キーで図形をスムーズに動かすための小技」のページを参照してください。

2.ボールとバーの当たり判定の考え方

当たり、つまりモノ同士がぶつかった(触れた、重なった)状態というのを、プログラムでどう書けばよいでしょうか。

Scratchプログラムでは、

img01.png

img02.png

などがあり、何かと触れた時の判定が簡単にできますが、Processingではこのような便利な関数はないので、自分でプログラムしなければなりません。

理解を早めるために、ここではまず条件を簡単にしましょう。つまり、まずはボールの中心座標のみを考え、大きさ(幅)は考慮しないとします。また、バーは上辺のみを考えることにします。

すると、ボールをキャッチした(ボールがバーに当たった)というのは、 ボールがバーより下に来た時に、ボール(の中心座標)はバー(の上辺)の幅の範囲内にある 、ということができます。つまり、

  • ボールのy座標がバーのy座標よりも下になった時に
  • ボールのx座標はバーの左端のx座標よりも大きい かつ バーの右端のx座標よりも小さい

となります。(下図の、青矢印の範囲内)

img03.png

やってみよう

上記の条件をプログラムしてみましょう。下記のif文の中の日本語のところにプログラムコードを書いてみてください。

if (ボールのy座標がバーのy座標よりも下になった) {
if ((ボールのx座標はバーの左端のx座標よりも大きい) && (ボールのx座標はバーの右端のx座標よりも小さい)) {
ball_y = 0; //バーに当たったので、ボールを上に戻す
ball_x = 300;
}

3.当たり判定がある場合のプログラム

これをプログラムで書くと、以下のようになります。

float ball_x;
float ball_y;
float catcher_x;
int migi, hirdari;
void setup() {
size(600, 400);
ball_x = 300;
ball_y = 0;
catcher_x = 0;
}
void draw() {
background(255);
ball_y += 2;
if (ball_y > 400) {
ball_y = 0;
ball_x = 300;
}
//-----当たり判定を追加--------
if (ball_y > 300) {
if ((catcher_x < ball_x ) && (ball_x < (catcher_x + 100))) {
ball_y = 0;
ball_x = 300;
}
}
//----------------------------
fill(0, 255, 0);
ellipse(ball_x, ball_y, 50, 50);
catcher_x += (migi + hirdari);
fill(255, 0, 0);
rect(catcher_x, 300, 100, 30);
}
void keyPressed() {
if (keyCode == RIGHT) {
migi = 5;
}
if (keyCode == LEFT) {
hirdari = -5;
}
}
void keyReleased() {
if (keyCode == RIGHT) {
migi = 0;
}
if (keyCode == LEFT) {
hirdari = 0;
}
}

「ボールがバーより下に来た時」は (ball_y > 300) 、「ボールはバーの幅の範囲内にある」は (ball_x > catcher_x) && (ball_x < (catcher_x + 100)) です。

ここで &&(ball_x > catcher_x)(ball_x < (catcher_x + 100)) の条件を結び付けています。そしてこのif文 if ((ball_x > catcher_x) && (ball_x < (catcher_x + 100))) が成り立つためにはこの2つの条件のどちらも満たす必要がある(かつ/AND条件)ことを示しています。

なお、上記の例ではball_yに関する条件とball_xに関する条件でそれぞれ別のif文にしましたが、これらを全て && で結合して1つのif文で書いても構いません。

やってみよう

上記のプログラムは 不完全 です。このプログラムでは、ボールの端がバーの端に当たっていても、当たりとは判定されずに通り過ぎてしまいます。これはボールの中心座標だけ考えていて、ボールの大きさ(幅)を考慮しなかったからです。

また、ボールが通り過ぎた直後にバーがボールの上に行くと、当たりと判定されてしまいます。これはバー(の上辺)よりも下に来た時だけを考えていて、バーの厚さを考慮しなかったからです。

課題集の「ボールキャッチゲームを作ってください」で、この不具合を修正したプログラムを作ってみてください。

img04.png

(図左)当たりとは判定されない例

(図右)当たりと判定されてしまう例