PDAのひとりごと

黄昏時、携帯情報端末は薄闇の向こうに何を見るのか

ポポペのプロ~6.ミサイル発射

2004-07-17 17:35:00 | Lesson
 弾は、インベーダーの分と砲台の分の2つがありますが、まずは砲台の方から始めましょう。
 今までと同様に砲台の弾の位置を示す変数を作ります。場所はグローバル変数を定義するところ、posHou を定義した下で良いでしょう。

struct position_t posTamaHou;


 砲台の弾は、発射ボタンが押されてから表示するので、はじめに表示する事はありませんが、表示位置は初期化しておきましょう。場所はWM_CREATE通知の処理の中なのですが、処理っすることがだいぶ増えたので、別関数に分けてしまいましょう。
 DoWmCreateという関数を作ります。場所は、DoWmPaintの前が良いでしょう。

// WM_CREATE
void DoWmCreate(HWND hWnd)
{
    hwndCB = CommandBar_Create(hInst, hWnd, 1);
    CommandBar_InsertMenubar(hwndCB, hInst, IDM_MENU, 0);
    CommandBar_AddAdornments(hwndCB, 0, 0);

    hdcBmp = CreateCompatibleDC(GetDC(hWnd));
    hBmp = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_BITMAP1));
    SelectObject(hdcBmp, hBmp);

    hdcScreen = CreateCompatibleDC(GetDC(hWnd));
    hScreen = CreateCompatibleBitmap(GetDC(hWnd), 320, 240);
    SelectObject(hdcScreen, hScreen);

    PatBlt( hdcScreen, 0, 0, 320, 240, WHITENESS );

    posInv.x = 320;
    posInv.y = 24;

    SetTimer(hWnd, 1, 200, NULL);

    iInvTime = 0;
    wKey = 0;

    posHou.x = 0;
    posHou.y = 240 - 48;
    BitBlt( hdcScreen,   // 転送先デバイスコンテキスト
        posHou.x,        // 表示位置 X座標
        posHou.y,        // 表示位置 Y座標
        32,              // 表示サイズ 幅
        24,              // 表示サイズ 高さ
        hdcBmp,          // 転送元デバイスコンテキスト
        64,              // 転送元 X座標
        24,              // 転送元 Y座標
        SRCCOPY );       // そのまま転送

    posTamaHou.x = -1;
    posTamaHou.y = 0;
}


 砲台の弾はこの時点では表示しません。そのためposTamaHouに入れるのはどんな値でも良いのですが、後で表示するかどうかを判定するためにposTamaHou.xを利用します。posTamaHou.xが-1の時は表示しないという取り決めにしておくのです。その処理は後で出てきます。
 WndProcのWM_CREATE通知の部分は、DoWmCreateをコールするよう書き換えます。

        case WM_CREATE:
            DoWmCreate(hWnd);
            break;


 ENTERキーが押されたときに弾を発射するためには、GmMoveHou関数の中でキー入力値を判定して発射処理を行うようにします。

void GmMoveHou(void)
{
    switch (wKey) {
    case VK_LEFT:
        GmMoveHouSub(-1);    // 左へ移動
        break;
    case VK_RIGHT:
        GmMoveHouSub(1);    // 右へ移動
        break;
    case VK_RETURN:
        if (posTamaHou.x == -1) {    // 弾が表示中でなければ発射
            posTamaHou.x = posHou.x;
            posTamaHou.y = posHou.y;
            GmMoveTamaHou();
            GmMoveHouSub(0);
        }
        break;
    }
}


 仮想キーコードのVK_RETURNは、キーボード上のENTERキーに対応しています。キーが押されていたら砲台の弾が表示中かどうかを判定し、表示中ではない時、つまりposTamaHou.xが-1の時に表示座標を決めます。すると、posTamaHou.xには画面内のX座標値である0~239の値が入ることになり、-1ではなくなります。
 GmMoveTamaHou関数は砲台の弾の移動を行います。
 弾の移動は、弾が表示されている間のみ行います。ここでは、弾が表示中ではないときを判定し、関数を終了させてしまっています。表示中であれば、今表示されている砲台の弾を消し、Y座標を変更します。現在のY座標に縦幅分の24を引くことで上方への移動を行います。座標を変更したら、そこへ砲台の弾を表示します。

// 砲台の弾移動処理
void GmMoveTamaHou(void)
{
    if (posTamaHou.x == -1) {    // 表示中ではない
        return;
    }

    PatBlt( hdcScreen,       // 対象のHDC
        posTamaHou.x,        // 表示位置 X座標
        posTamaHou.y,        // 表示位置 Y座標
        32,                  // 表示サイズ 幅
        24,                  // 表示サイズ 高さ
        WHITENESS );         // 白で塗りつぶす

    posTamaHou.y -= 24;      // 上昇する

    if (posTamaHou.y <= 0) { // 上端を越えた時
        posTamaHou.x = -1;   // 表示終了
    } else {
        BitBlt( hdcScreen,   // 転送先HDC
            posTamaHou.x,    // 表示位置 X座標
            posTamaHou.y,    // 表示位置 Y座標
            32,              // 表示サイズ 幅
            24,              // 表示サイズ 高さ
            hdcBmp,          // 転送元HDC
            96,              // 転送元 X座標
            24,              // 転送先 Y座標
            SRCCOPY );       // そのまま転送
    }
}


 弾が移動する処理は、定期的に実行しなければなりません。タイミング的にはインベーダーを動かすのと同じで良いでしょう。

case WM_TIMER:
    GmMoveInv();
    GmMoveTamaHou();
    GmMoveHou();
    InvalidateRect(hWnd, NULL, FALSE);
    break;


 これで砲台の弾が移動するようになりました。ビルドして実行してみましょう。
 ENTERキーを押すと弾が発射されます。発射された弾は勝手に上へ飛んでいき、上端で消えます。まだ当たり判定を入れていないため、インベーダーに当たっても何も起こりませんが、弾とインベーダーの位置関係によってはおかしな表示になるのが分かるでしょうか?これについては後ほど解決することになります。