ラケットの当たり判定
当たり判定関数の作成
ラケットにボールが当たったかの判定は、ボールの現在位置と次の位置を結ぶ線と、ラケットが交差するかどうかで判別します。Y座標がラケットの位置のときのボールのX座標を求めるには、DeltaX、DeltaYを利用します。
CheckHit関数は、ラケットでボールをヒットしたかどうかの判別を行う関数です。引数のox、oyはボールの移動前の座標です。nx、nyは異動後の座標で、ラケットに当たったときには、ラケット上の座標に変更されます。Position型のr変数は、ラケットの座標を表します。この関数は、ラケットに当たったときはtrueを返し、当たっていないときはfalseを返します。
bool __fastcall TGameForm::CheckHit(int ox, int oy, int &nx, int &ny, TPosition *r) { int ncx; if (oy < ny) { // ボールが下に向かって動いているとき // ボールがラケットの Y座標を通過したか? if (oy < r->Y && ny >= r->Y) { // ラケットとボールの奇跡の交点のX座標を求める ncx = ox + (int)((double)DeltaX * (double)(oy - r->Y) / (double)(oy - ny)); // 交点のX座標がラケットの矩形内にあるか? if (ncx > r->X - Ball->Width && ncx < r->X + Racket->Width) { // 交点の座標を移動後のボール座標にセット nx = ncx; ny = oy + (int)((double)DeltaY * (double)(oy - r->Y) / (double)(oy - ny)); return true; } } } else { // ボールが上に向かって動いているとき // ボールがラケットの Y座標を通過したか? if (oy < r->Y + Racket->Height && ny >= r->Y + Racket->Height) { // ラケットとボールの奇跡の交点のX座標を求める ncx = ox + (int)((double)DeltaX * (double)(oy - (r->Y + Racket->Height)) / (double)(oy - ny)); // 交点のX座標がラケットの矩形内にあるか? if (ncx > r->X - Ball->Width && ncx < r->X + Racket->Width) { // 交点の座標を移動後のボール座標にセット nx = ncx; ny = oy + (int)((double)DeltaY * (double)(oy - (r->Y + Racket->Height)) / (double)(oy - ny)); return true; } } } return false; }
GameUnit.h にこの関数の宣言を追加します。
private: // ユーザー宣言 int Ang, DeltaX, DeltaY; double Speed; void __fastcall MoveBall(); void __fastcall CalcNewDelta(); bool __fastcall CheckHit(int ox, int oy, int &nx, int &ny, TPosition *r);
MoveBall内にラケットの当たり判定を追加
この関数は、MoveBall関数内で使用します。MoveBall関数の以下の箇所にラケットの当たり判定を処理するコードを記述します(太字部分)。ここでは、ラケットの角でボールをひっかけたときに思わぬ方向に跳ね返るコードも追加しています。
void __fastcall TGameForm::MoveBall() { // 移動後のボールの位置を計算します int x = Ball->Position->X + DeltaX; int y = Ball->Position->Y + DeltaY; // ラケットにヒットしたかどうかを判定 TPosition *pos = Racket->Position; // ラケットの座標 if (CheckHit(Ball->Position->X, Ball->Position->Y, x, y, pos)) { if (x < pos->X + 2 || x + Ball->Width > pos->X + pos Width - 2) Ang = 360 - Ang + (random(60) - 30); // 角で引っかけた else Ang = 360 - Ang; // 正しくヒット Ball->Position->X = x; Ball->Position->Y = y; CalcNewDelta(); MessageBeep(-1); return; } // 壁に当たったかの判定 if (y + Ball->Height >= Court->Height) { // 下の壁 // 壁に当たった : : :
動作を確認してみる
では、プログラムを実行して動作を確認してみましょう。
ラケットを上手に動かして、正しい位置でボールを打つとちゃんと跳ね返りますが、ラケットの角でひっかけてしまうと、思わぬ方向に飛びます。このときの角度の計算には、乱数を使っています。
ラケットとボールを移動させるコートを記述して、だいぶゲームらしくなってきましたね。
次回は、スコアのカウントや、ボールを打ち損ねたときのラケットの処理など、ゲームの進行にかかわるコードを記述して、ゲームを完成させます。