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個の情報が必要です。
- プレイヤーのx
- プレイヤーのy
- プレイヤーの横幅
- プレイヤーのたて幅
- 土管のx
- 土管のy
- 土管の横幅
- 土管のたて幅
これらを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つの条件がすべて満たされた時に衝突していると判定できます。
- Aの左端が、Bの右端より、左にある。
- Aの右端が、Bの左端より、右にある。
- Aの上端が、Bの下端より、上にある。
- 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;}