ゲームのコードを記述する
ラケットとボールのアタリ判定
ラケットにボールが当たったかの判定は、ボールの現在位置と次の位置を結ぶ線と、ラケットが交差するかどうかで判別します。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::function func1);
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");
}
今回は実装しませんでしたが、ゲームオーバーでも同様に音楽が鳴るようにしてもいいですね。

