SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

EDN CodeZine出張所(AD)

C++Builder 10.2 TokyoでレトロなラケットゲームをiOSで再構築してみる

  • X ポスト
  • このエントリーをはてなブックマークに追加

ゲームのコードを記述する

ラケットとボールのアタリ判定

 ラケットにボールが当たったかの判定は、ボールの現在位置と次の位置を結ぶ線と、ラケットが交差するかどうかで判別します。Y座標がラケットの位置のときのボールのX座標を求めるには、FDeltaX、FDeltaYを利用します。

図12 ボールのアタリ判定
図12 ボールのアタリ判定

 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");
}

 今回は実装しませんでしたが、ゲームオーバーでも同様に音楽が鳴るようにしてもいいですね。

次のページ
ボールを動かすコード

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
EDN CodeZine出張所連載記事一覧

もっと読む

この記事の著者

EDN編集部(イーディーエヌ編集部)

エンバカデロ・デベロッパーネットワーク(EDN)は、ソフトウェア開発者とデータベース技術者のための技術情報サイトです。Delphi、C++Builderをはじめとする開発ツールやER/Studioなどのデータベースツールに関連する技術記事、ビデオなどを提供しています。EDN編集部は、EDN記事と連携...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

【AD】本記事の内容は記事掲載開始時点のものです 企画・制作 株式会社翔泳社

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/10589 2017/12/21 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング