弾は、インベーダーの分と砲台の分の2つがありますが、まずは砲台の方から始めましょう。
今までと同様に砲台の弾の位置を示す変数を作ります。場所はグローバル変数を定義するところ、posHou を定義した下で良いでしょう。
砲台の弾は、発射ボタンが押されてから表示するので、はじめに表示する事はありませんが、表示位置は初期化しておきましょう。場所はWM_CREATE通知の処理の中なのですが、処理っすることがだいぶ増えたので、別関数に分けてしまいましょう。
DoWmCreateという関数を作ります。場所は、DoWmPaintの前が良いでしょう。
砲台の弾はこの時点では表示しません。そのためposTamaHouに入れるのはどんな値でも良いのですが、後で表示するかどうかを判定するためにposTamaHou.xを利用します。posTamaHou.xが-1の時は表示しないという取り決めにしておくのです。その処理は後で出てきます。
WndProcのWM_CREATE通知の部分は、DoWmCreateをコールするよう書き換えます。
ENTERキーが押されたときに弾を発射するためには、GmMoveHou関数の中でキー入力値を判定して発射処理を行うようにします。
仮想キーコードのVK_RETURNは、キーボード上のENTERキーに対応しています。キーが押されていたら砲台の弾が表示中かどうかを判定し、表示中ではない時、つまりposTamaHou.xが-1の時に表示座標を決めます。すると、posTamaHou.xには画面内のX座標値である0~239の値が入ることになり、-1ではなくなります。
GmMoveTamaHou関数は砲台の弾の移動を行います。
弾の移動は、弾が表示されている間のみ行います。ここでは、弾が表示中ではないときを判定し、関数を終了させてしまっています。表示中であれば、今表示されている砲台の弾を消し、Y座標を変更します。現在のY座標に縦幅分の24を引くことで上方への移動を行います。座標を変更したら、そこへ砲台の弾を表示します。
弾が移動する処理は、定期的に実行しなければなりません。タイミング的にはインベーダーを動かすのと同じで良いでしょう。
これで砲台の弾が移動するようになりました。ビルドして実行してみましょう。
ENTERキーを押すと弾が発射されます。発射された弾は勝手に上へ飛んでいき、上端で消えます。まだ当たり判定を入れていないため、インベーダーに当たっても何も起こりませんが、弾とインベーダーの位置関係によってはおかしな表示になるのが分かるでしょうか?これについては後ほど解決することになります。
今までと同様に砲台の弾の位置を示す変数を作ります。場所はグローバル変数を定義するところ、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キーを押すと弾が発射されます。発射された弾は勝手に上へ飛んでいき、上端で消えます。まだ当たり判定を入れていないため、インベーダーに当たっても何も起こりませんが、弾とインベーダーの位置関係によってはおかしな表示になるのが分かるでしょうか?これについては後ほど解決することになります。