ゲームのコードを記述する
ラケットとボールのアタリ判定
ラケットにボールが当たったかの判定は、ボールの現在位置と次の位置を結ぶ線と、ラケットが交差するかどうかで判別します。Y座標がラケットの位置のときのボールのX座標を求めるには、FDeltaX、FDeltaYを利用します。
CheckHit関数は、ラケットでボールをヒットしたかどうかの判別を行う関数です。引数のox、oyはボールの移動前の座標です。nx、nyは異動後の座標で、ラケットに当たったときには、ラケット上の座標に変更されます。Position型のr変数は、ラケットの座標を表します。この関数は、ラケットに当たったときはtrueを返し、当たっていないときはfalseを返します。
//CheckHitラケットの当たり判定定義 bool __fastcall CheckHit(const int ox, const int oy, int &nx, int &ny, const TPosition *r); bool __fastcall TGameForm::CheckHit(const int ox, const int oy, int &nx, int &ny, const TPosition *r) { int ncx; if (oy < ny) { // ボールが下に向かって動いているとき // ボールがラケットの Y座標を通過したか? if (oy < r->Y && ny >= r->Y) { // ラケットとボールの奇跡の交点のX座標を求める ncx = ox + (int)((double)FDeltaX * (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)FDeltaY * (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)FDeltaX * (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)FDeltaY * (double)(oy - (r->Y + Racket->Height)) / (double)(oy - ny)); return true; } } } return false; }
壁音とラケット音を作る
スマートフォンアプリならではの拡張として、今回は、壁とラケットにヒットした時に音が鳴るようにします。
//MP3サウンド再生関数定義 TMediaPlayer* __fastcall beep_start(const String& fname); static constexpr const wchar_t* racket_mp3 {L"racket.mp3"}; static constexpr const wchar_t* wall_mp3 {L"wall.mp3"}; TMediaPlayer* __fastcall TGameForm::beep_start(const String& fname) { #if defined(_PLAT_IOS) || defined(_PLAT_ANDROID) auto mp = new TMediaPlayer(this); mp->FileName = System::Ioutils::TPath::GetDocumentsPath() + System::Ioutils::TPath::DirectorySeparatorChar + fname; return mp; #else return nullptr; #endif }
TMediaPlayerを使ってbeep_start()関数をコールすると、パラメータ文字列ファイル名のmp3音が再生されます。
ラケットロストとゲームオーバー関数の実装
ボールがラケットからロストした場合のLostRacket()関数とゲームオーバー時のGameOver()関数のコードを記述します。
//ラケットロストとゲームオーバー関数定義 void __fastcall LostRacket(std::functionfunc1); void __fastcall GameOver(); void __fastcall TGameForm::LostRacket(std::function func1) { // ラケットの大きさを保管 const int orgWidth = Racket->Width; const int orgHeight = Racket->Height; TThread::CreateAnonymousThread([this, func1, orgWidth, orgHeight](){ // ラケットを少しずつ消す for (int i = 0, w = orgWidth/2; i < w; i++) { TThread::Synchronize(TThread::CurrentThread, [this, i, w, orgWidth, orgHeight](){ Racket->Width = Racket->Width - 2; Racket->Height = (orgHeight * (w - i))/ w; Racket->Position->X = Racket->Position->X + 1; }); Sleep(20); // 20msec待つ } Sleep(100); // 100msec待つ // ラケットを元の大きさに戻す TThread::Synchronize(TThread::CurrentThread,[this, orgWidth, orgHeight, func1](){ Racket->Width = orgWidth; Racket->Height = orgHeight; // ラケットを中央に配置 Racket->Position->X = (Court->Width - orgWidth) / 2; func1(); }); })->Start(); } void __fastcall TGameForm::GameOver() { // タイマーの停止 Timer1->Enabled = false; // ボールを非表示にする Ball->Visible = false; // ボタンの状態変更 StartButton->Enabled = true; StopButton->Enabled = false; // ゲームオーバーのメッセージ ShowMessage("Game Over"); }
今回は実装しませんでしたが、ゲームオーバーでも同様に音楽が鳴るようにしてもいいですね。