コンテンツにスキップ

4. プレイヤーと下の土管の衝突判定をする

Processingでフラッピーバード風ゲームをつくっていきます。

前回はプレイヤーと土管のプログラムをつくりました。

そのまえに

前回までのプログラムコード

float x = 100;
float y = 200;
float speed = 0;
float dx = 400;
float dy = 100;
void setup() {
size(600, 400);
}
void draw() {
background(0);
// まとめた土管のプログラムを呼び出す
dokan();
// まとめたプレイヤーのプログラムを呼び出す
player();
}
void mousePressed() {
speed = -10;
}
void dokan(){
// 土管のdraw
dx = dx - 5;
if (dx + 50 < 0) {
dx = 600;
dy = random(200, 300);
}
rect(dx, dy, 50, 400 - dy);
}
void player(){
// プレイヤーのdraw
speed = speed + 1;
y = y + speed;
rect(x, y, 50, 50);
}

このプログラムが手元にある前提で話をしていきます。

4-1 衝突判定の関数を用意する

衝突判定の関数

衝突したかどうかを判定する関数を用意しました。

下のコードを最後の行に追加しましょう。

int isHit(float px, float py, float pw, float ph, float ex, float ey, float ew, float eh) {
if (px < ex + ew && px + pw > ex) {
if (py < ey + eh && py + ph > ey) {
return 1;
}
}
return 0;
}

衝突判定の使い方

衝突判定を使うためには、なんと8個の情報が必要です。

  1. プレイヤーのx
  2. プレイヤーのy
  3. プレイヤーの横幅
  4. プレイヤーのたて幅
  5. 土管のx
  6. 土管のy
  7. 土管の横幅
  8. 土管のたて幅

これらをisHit関数に伝えます。

isHit(プレイヤーのx, プレイヤーのy, プレイヤーの横幅, プレイヤーのたて幅, 土管のx, 土管のy, 土管の横幅, 土管のたて幅)

これらを実際のプログラムコードにあわせると、

isHit(x, y, 50, 50, dx, dy, 50, 400 - dy);

となります。

isHitは、衝突していれば「1」を、衝突してなければ「0」を作ります。

なので、

int hit = isHit(x, y, 50, 50, dx, dy, 50, 400 - dy)
if(hit == 1){
// 衝突していた場合のプログラム
}

上のようにif条件分岐と組み合わせて、 isHitが作ってくれた数字「1か0」を、変数「hit」に持たせて、 変数「hit」が「1」かどうかを「==(イコール二つ)」で条件にしています。

このようにして、衝突していた場合のプログラムを書くことができます。

実際にplayer関数の中にプログラムしてみましょう。

drawの中ではありませんよ!

drawの中で呼び出している、player関数の中で、プレイヤーに関するプログラムを書きましょう。

void player() {
speed = speed + 1;
y = y + speed;
// プレイヤーと土管が衝突しているか、isHit関数で調べる
int hit = isHit(x, y, 50, 50, dx, dy, 50, 400 - dy);
// もし、プレイヤーと土管が衝突しているなら
if(hit == 1){
// 衝突時のプログラムを書く
}
rect(x, y, 50, 50);
}

衝突判定のアルゴリズム

衝突判定をするプログラムを先に記載しましたが、どういうアルゴリズムかを説明しておきます。

四角形Aと四角形Bの衝突判定をしたい時、以下の4つの条件がすべて満たされた時に衝突していると判定できます。

  1. Aの左端が、Bの右端より、左にある。
  2. Aの右端が、Bの左端より、右にある。
  3. Aの上端が、Bの下端より、上にある。
  4. Aの下端が、Bの上端より、下にある。

これら4つの条件がすべて一致したときにだけ「1」を返すプログラムが、先ほど作ったisHit関数です。

int isHit(float px, float py, float pw, float ph, float ex, float ey, float ew, float eh) {
if (px < ex + ew && px + pw > ex) {
if (py < ey + eh && py + ph > ey) {
return 1;
}
}
return 0;
}

論理演算子と呼ばれる「&&」で条件を結ぶことで、複数の条件が一致した時のみの条件分岐が作れます。 (今回は、横の判定と縦の判定をifを二つ使って実現しています。&&が2個、ifが2個で、計4個の条件が同時に一致したとき、をプログラムしています)

衝突判定のアルゴリズム、アルゴリズムをプログラムにするのは難しいので、今回は無理して覚える必要はありません。 いろいろな作品を作っていくうちに自然に覚えていきますので、衝突判定でくじけず、「そういうものなんだな」と割り切って次に行きましょう。

4-2 衝突したらプレイヤーが赤くなるようにする

プレイヤーを赤くする

プレイヤーとなる四角形を赤くするには、fill命令を使います。

fill(色情報1, 色情報2, 色情報3);

fill命令に色情報を渡すと、四角形に色をつけることができます。

fill(255, 0, 0);

赤色にするには、255と0と0、をfill命令に伝えましょう。

void player() {
speed = speed + 1;
y = y + speed;
// プレイヤーと土管が衝突しているか、isHit関数で調べる
int hit = isHit(x, y, 50, 50, dx, dy, 50, 400 - dy);
// もし、プレイヤーと土管が衝突しているなら
if(hit == 1){
// 衝突時のプログラムを書く
fill(255, 0, 0);
}
rect(x, y, 50, 50);
}

実行すると、衝突時にプレイヤーが赤くなったでしょうか?

衝突していないときは白にする

衝突時に赤くなるようになりましたが、一度衝突するとずっと赤色のままです。

しかも、土管まで赤くなってしまったと思います。

なぜでしょうか?

それは、「fill命令を使って白に戻していないから」です。

まずは土管にfill命令を使いましょう。

土管は常に白色なので、 衝突しているかどうか、チェックはいりませんね。

void dokan() {
// 土管のdraw
dx = dx - 5;
if (dx + 50 < 0) {
dx = 600;
dy = random(200, 300);
}
fill(255, 255, 255);
rect(dx, dy, 50, 400 - dy);
}

次にプレイヤーです。

void player() {
speed = speed + 1;
y = y + speed;
// とりえあえず白に戻す
fill(255, 255, 255);
int hit = isHit(x, y, 50, 50, dx, dy, 50, 400 - dy);
if(hit == 1){
fill(255, 0, 0);
}
rect(x, y, 50, 50);
}

isHit関数で、衝突判定をする前に、 とりあえずfill命令で白色(255, 255, 255)にしています。

もし衝突していたら、赤色のfill命令に上書きされるため問題ありません。

ここまでのプログラムコード

float x = 100;
float y = 200;
float speed = 0;
float dx = 400;
float dy = 100;
void setup() {
size(600, 400);
}
void draw() {
background(0);
// まとめた土管のプログラムを呼び出す
dokan();
// まとめたプレイヤーのプログラムを呼び出す
player();
}
void mousePressed() {
speed = -10;
}
void dokan() {
// 土管のdraw
dx = dx - 5;
if (dx + 50 < 0) {
dx = 600;
dy = random(200, 300);
}
fill(255, 255, 255);
rect(dx, dy, 50, 400 - dy);
}
void player() {
speed = speed + 1;
y = y + speed;
// とりえあえず白に戻す
fill(255, 255, 255);
int hit = isHit(x, y, 50, 50, dx, dy, 50, 400 - dy);
if(hit == 1){
fill(255, 0, 0);
}
rect(x, y, 50, 50);
}
int isHit(float px, float py, float pw, float ph, float ex, float ey, float ew, float eh) {
if (px < ex + ew && px + pw > ex) {
if (py < ey + eh && py + ph > ey) {
return 1;
}
}
return 0;
}