昔、よくあった15枚のパネルを動かして、正しい並び方にするゲームです。
タイマー付きです。
ソースのダウンロード
※)サンプルの作成はVC++5.0で行いました。
※2)サンプルはWIN32APIのみでMFCは使っていません。
解説
(全体の流れ)
(詳細)
1.アプリケーションの初期化
まずメインウィンドウのウィンドウクラスを定義します。(Initialize.cpp)
WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = 0; wcex.lpfnWndProc = MainWndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hIcon = LoadIcon (hInstance, MAKEINTRESOURCE(IDI_ICON)); wcex.hCursor = LoadCursor (NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = MAKEINTRESOURCE(IDR_MAIN); wcex.lpszClassName = MainWindowClassName; wcex.hIconSm = NULL; return RegisterClassEx(&wcex);
次にメインウィンドウを作成します。(Initialize.cpp)
BOOL InitInstance (HINSTANCE hInstance, int nCmdShow) { HWND hWnd; hInst = hInstance; // ウィンドウの大きさをユーザーが変更できなくする hWnd = CreateWindowEx (NULL, MainWindowClassName, AppName, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, 138, 224, HWND_DESKTOP, NULL, hInst, NULL); if (!hWnd) return FALSE; ShowWindow (hWnd, nCmdShow); UpdateWindow (hWnd); hParent = hWnd; return TRUE; }
ウィンドウのスタイル指定を"WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX"と指定することにより、ユーザーが勝手にウィンドウのサイズ変更をできないようにしています。
最後にグローバル変数"hParent"にこのウィンドウのハンドルを格納します。
先ほど作った親ウィンドウ上に、ツールバー、ステータスバー、クライアントウィンドウを貼り付けます。
このとき、コモンコントロールを使うので次のようにして初期化しておきます。(Main.cpp)
INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(INITCOMMONCONTROLSEX); icc.dwICC = ICC_BAR_CLASSES; InitCommonControlsEx(&icc);
親ウィンドウにWM_CREATEメッセージが送られたときに子ウィンドウの作成と初期化を行います(Main.cpp)
case WM_CREATE: // ↓スクリーンの大きさを取得 nMax_x = GetSystemMetrics(SM_CXSCREEN); nMax_y = GetSystemMetrics(SM_CYSCREEN); // ↓子ウィンドウの初期化 hTool = InitToolbar (hWnd); hStatus = InitStatusbar (hWnd); hClientFrm = InitClientFrm(hWnd); if (!hTool || !hStatus || !hClientFrm) { // 初期化失敗時の処理 MessageBox (NULL, Error2, AppName, MB_OK | MB_ICONSTOP); // ↓残っているウィンドウを破棄する if (hTool) DestroyWindow(hTool); if (hStatus) DestroyWindow(hStatus); if (hClientFrm) DestroyWindow(hClientFrm); DestroyWindow(hWnd); return 1; } // ↓グローバル変数に子ウィンドウのハンドルを格納する hTlb = hTool; hStat = hStatus; hClFrm = hClientFrm; // ↓子ウィンドウをウィンドウに合わせる GetWindowRect(hTlb, &rect); lToolHeight = rect.bottom - rect.top; GetWindowRect(hStat, &rect); lStatusHeight = rect.bottom - rect.top; // ↓メインウィンドウを画面の中央に置く SetWindowPos(hWnd, NULL, nMax_x/2-69, nMax_y/2-112, 0, 0, SWP_NOSIZE); return 0;
子ウィンドウの作成はそれぞれ、InitToolbar、InitStatusbar、InitClientFrm関数が行います。(それぞれのソースは別のファイルにあります。)
ウィンドウのサイズ合わせをその後行います。
親ウィンドウのときと同様に、それぞれのウィンドウハンドルをグローバル変数に格納しておきます。
ゲームをはじめるにはツールバー上のスタートボタンを押します。
そのときの処理は次のようになります。(Main.cpp)
case WM_COMMAND: switch (wParam) { case IDM_START: // ↓乱数を使ってパネルの並びを変える(ここから) // ↓とりあえずリセットをかける。 SendMessage(hWnd, WM_COMMAND, (WPARAM)IDM_RESET, 0); srand((unsigned)time(NULL)); // 乱数のセット { for (int k = 0; k <= 1000; k++) { i = rand()%4+1; j = rand()%4+1; if (i == 4 && j == 4) continue; n = nPanelInfo[i][j]; s = rand()%4+1; t = rand()%4+1; if (s == 4 && t == 4) continue; nPanelInfo[i][j] = nPanelInfo[s][t]; nPanelInfo[s][t] = n; } } PatBlt(hMemDC, 0, 0, nMax_x, nMax_y, PATCOPY); for (i = 1; i <= 4; i++) { for (j = 1; j <=4; j++) { DrawIcon(hMemDC, 32*(j-1), 32*(i-1), hPanel[nPanelInfo[i][j]-1]); } } InvalidateRect(hClFrm, NULL, TRUE); // ↑乱数を使ってパネルの並びを変える(ここまで) bPlay_flg = TRUE; // プレイ中のフラグをセット time_cnt = 0; // タイムカウントをリセット SetTimer(hClFrm, 1, 1000, NULL); // タイマーのセット return 0;
まず、乱数を使ってパネルの並び替えをします。そして仮想画面上に描画を行い、InbalidateRect関数を使ってクライアントウィンドウの再描画をします。
次に、ゲーム中であるといったことを示すフラグをTUREにセットしタイマーを初期化します。
パネルをダブルクリックすることによって移動させていき、パネルが正確に並ぶまでゲームを続けます。(InitClientFrm.cpp)
case WM_LBUTTONDOWN: if (bPlay_flg == TRUE) { // プレイ中かな? // ↓クリックされたパネルを調べる x = LOWORD(lParam)/32; y = HIWORD(lParam)/32; // ↓パネルが移動できるか判定し、可能なら移動する(ここから) if (nPanelInfo[y+1][x+2] == 0) { // 右に移動可能なとき // ↓移動に関係するエリアを黒のブラシで塗りつぶす PatBlt(hMemDC, x*32, y*32, 64, 32, PATCOPY); // ↓パネルの情報の入れ替え nPanelInfo[y+1][x+2] = nPanelInfo[y+1][x+1]; nPanelInfo[y+1][x+1] = 0; // ↓更新領域の短形を定義 rect.left = x*32; rect.top = y*32; rect.right = x*32+64; rect.bottom = y*32+32; // ↓パネルの再描画 DrawIcon(hMemDC, 32*x, 32*y, hPanel[nPanelInfo[y+1][x+1]-1]); DrawIcon(hMemDC, 32*(x+1), 32*y, hPanel[nPanelInfo[y+1][x+2]-1]); } else if (nPanelInfo[y+1][x] == 0) { // 左に移動可能なとき // ↓移動に関係するエリアを黒のブラシで塗りつぶす PatBlt(hMemDC, (x-1)*32, y*32, 64, 32, PATCOPY); // ↓パネルの情報の入れ替え nPanelInfo[y+1][x] = nPanelInfo[y+1][x+1]; nPanelInfo[y+1][x+1] = 0; // ↓更新領域の短形を定義 rect.left = (x-1)*32; rect.top = y*32; rect.right = (x-1)*32+64; rect.bottom = y*32+32; // ↓パネルの再描画 DrawIcon(hMemDC, 32*(x-1), 32*y, hPanel[nPanelInfo[y+1][x]-1]); DrawIcon(hMemDC, 32*x, 32*y, hPanel[nPanelInfo[y+1][x+1]-1]); } else if(nPanelInfo[y+2][x+1] == 0) { // 下に移動可能なとき // ↓移動に関係するエリアを黒のブラシで塗りつぶす PatBlt(hMemDC, x*32, y*32, 32, 64, PATCOPY); // ↓パネルの情報の入れ替え nPanelInfo[y+2][x+1] = nPanelInfo[y+1][x+1]; nPanelInfo[y+1][x+1] = 0; // ↓更新領域の短形を定義 rect.left = x*32; rect.top = y*32; rect.right = x*32+32; rect.bottom = y*32+64; // ↓パネルの再描画 DrawIcon(hMemDC, 32*x, 32*y, hPanel[nPanelInfo[y+1][x+1]-1]); DrawIcon(hMemDC, 32*x, 32*(y+1), hPanel[nPanelInfo[y+2][x+1]-1]); } else if (nPanelInfo[y][x+1] == 0) { // 上に移動可能なとき // ↓移動に関係するエリアを黒のブラシで塗りつぶす PatBlt(hMemDC, x*32, (y-1)*32, 32, 64, PATCOPY); // ↓パネルの情報の入れ替え nPanelInfo[y][x+1] = nPanelInfo[y+1][x+1]; nPanelInfo[y+1][x+1] = 0; // ↓更新領域の短形を定義 rect.left = x*32; rect.top = (y-1)*32; rect.right = x*32+32; rect.bottom = (y-1)*32+64; // ↓パネルの再描画 DrawIcon(hMemDC, 32*x, 32*y, hPanel[nPanelInfo[y+1][x+1]-1]); DrawIcon(hMemDC, 32*x, 32*(y-1), hPanel[nPanelInfo[y][x+1]-1]); } else return 0; InvalidateRect(hWnd, &rect, TRUE); // 更新する // ↑パネルが移動できるか判定し、可能なら移動する(ここまで) if (CheckPanels()) { // 終了判定をする strcpy (str, ""); // ↓終了したなら記録を出力 sprintf(str, "Finish!! 時間 %04d", time_cnt); MessageBox(hWnd, str, "PaNels", MB_OK); bPlay_flg = FALSE; // プレイ終了 KillTimer(hWnd, 1); // タイマーをKill time_cnt = 0; // カウンターを0にセット } } return 0;
間違いを発見した方、質問をしたい方はこちらへお願いします。