PaNels



昔、よくあった15枚のパネルを動かして、正しい並び方にするゲームです。
タイマー付きです。

ソースのダウンロード

※)サンプルの作成はVC++5.0で行いました。
※2)サンプルはWIN32APIのみでMFCは使っていません。

PaNelsの画面

解説

(全体の流れ)

  1. アプリケーションの初期化
  2. ウィンドウの作成
  3. 各種子ウィンドウの作成初期化
  4. 実際のゲーム処理









(詳細)
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);

2.ウィンドウの作成
次にメインウィンドウを作成します。(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"にこのウィンドウのハンドルを格納します。

3.各種子ウィンドウの作成初期化
先ほど作った親ウィンドウ上に、ツールバー、ステータスバー、クライアントウィンドウを貼り付けます。
このとき、コモンコントロールを使うので次のようにして初期化しておきます。(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関数が行います。(それぞれのソースは別のファイルにあります。)

ウィンドウのサイズ合わせをその後行います。

親ウィンドウのときと同様に、それぞれのウィンドウハンドルをグローバル変数に格納しておきます。

4.実際のゲーム処理
ゲームをはじめるにはツールバー上のスタートボタンを押します。
そのときの処理は次のようになります。(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;


間違いを発見した方、質問をしたい方はこちらへお願いします。


トップページへ

Gポイントポイ活 Amazon Yahoo 楽天

無料ホームページ 楽天モバイル[UNLIMITが今なら1円] 海外格安航空券 海外旅行保険が無料!