WINAPI实现简易扫雷游戏
Posted 韦俊宇
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WINAPI实现简易扫雷游戏相关的知识,希望对你有一定的参考价值。
1 //扫雷 2 //-1为雷 3 #include <windows.h> 4 #include <windowsx.h> 5 #include <strsafe.h> 6 #include <time.h> 7 //格子区域大小(DIVISIONS * DIVISIONS) 8 #define DIVISIONS 10 9 //地雷数 10 #define MINECOUNT 10 11 12 //消息处理 13 LRESULT CALLBACK DealMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam); 14 //写入地雷 15 void SetMine(int(*pChess)[DIVISIONS]); 16 //获取随机数 17 unsigned int GetRand(); 18 //判断胜利 19 bool Win(int(*pChess)[DIVISIONS]); 20 //重置 21 void Reset(int(*pChess)[DIVISIONS], int(*pClick)[DIVISIONS]); 22 23 int WINAPI WinMain( 24 HINSTANCE hInstance, // 当前实例句柄 25 HINSTANCE hPrevInstance,// 前一实例句柄 26 LPSTR lpCmdLine, // 指向命令行参数的指针 27 int nCmdShow // 窗口的显示方式 28 ) 29 { 30 HWND hWnd; 31 MSG msg; 32 WNDCLASS wndClass; 33 34 wndClass.cbClsExtra = 0; 35 wndClass.cbWndExtra = 0; 36 wndClass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);//画刷背景BLACK_PEN 37 wndClass.hCursor = LoadCursor(NULL, IDI_APPLICATION); 38 wndClass.hIcon = LoadIcon(NULL, IDC_ARROW); 39 wndClass.hInstance = hInstance; 40 wndClass.lpfnWndProc = DealMessage; 41 wndClass.lpszClassName = TEXT("MineSweeping"); 42 wndClass.lpszMenuName = NULL; 43 wndClass.style = CS_HREDRAW | CS_VREDRAW; 44 45 46 47 if (!RegisterClass(&wndClass)) 48 { 49 MessageBox(NULL, TEXT("注册类失败,请检查参数是否成功设置"), TEXT("提示"), MB_OK); 50 } 51 52 hWnd = CreateWindow(TEXT("MineSweeping"), // 窗口类名称 53 TEXT("扫雷"), // 窗口标题栏名称 54 WS_OVERLAPPEDWINDOW, // 窗口样式 55 CW_USEDEFAULT, // 窗口水平位置 56 CW_USEDEFAULT, // 窗口垂直位置 57 CW_USEDEFAULT, // 窗口宽度 58 CW_USEDEFAULT, // 窗口高度 59 NULL, // 父窗口句柄 60 NULL, // 窗口菜单句柄 61 hInstance, // 窗口实例句柄 62 NULL); // 窗口创建参数 63 if (!hWnd) // 新窗口创建失败 64 { 65 return FALSE; 66 } 67 68 ShowWindow(hWnd, SW_SHOWNORMAL); 69 UpdateWindow(hWnd); 70 71 while (GetMessage(&msg, NULL, 0, 0)) 72 { 73 TranslateMessage(&msg); 74 DispatchMessage(&msg); 75 } 76 77 return msg.wParam; 78 } 79 80 81 LRESULT CALLBACK DealMessage(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 82 { 83 //初始化要用到的变量 84 HDC hdc; 85 PAINTSTRUCT ps; 86 RECT rect; 87 HBRUSH hBrush, hOldBrush; 88 TEXTMETRIC tm; 89 static int cxChar,cxCaps,cyChar; 90 91 //输出整形用的缓冲区 92 TCHAR szBuffer[128]; 93 size_t iTarget; 94 95 //地雷以及点击后雷个数存储区 96 static int iChess[DIVISIONS][DIVISIONS]; 97 int(*pChess)[DIVISIONS] = iChess; 98 //点击后记录点击事件存储区 99 static int iClick[DIVISIONS][DIVISIONS]; 100 int(*pClick)[DIVISIONS] = iClick; 101 102 //pSize:当前一个格子宽高 103 //p:通用 104 static POINT pSize = { 0,0 }, p; 105 106 switch (uMsg) 107 { 108 case WM_CREATE: 109 hdc = GetDC(hWnd); 110 111 //初始化 112 Reset(pChess, pClick); 113 //获取字体高度 114 GetTextMetrics(hdc, &tm); 115 cyChar = tm.tmHeight + tm.tmExternalLeading; 116 //平均宽度 117 cxChar = tm.tmAveCharWidth; 118 //判断是否等宽字体 119 cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2; 120 121 ReleaseDC(hWnd, hdc); 122 return 0; 123 case WM_KEYDOWN: 124 //在没有鼠标的情况下,键盘模拟鼠标操作 125 ShowCursor(false); 126 GetCursorPos(&p); 127 ScreenToClient(hWnd, &p); 128 p.x = max(0, min(DIVISIONS - 1, p.x / pSize.x)); 129 p.y = max(0, min(DIVISIONS - 1, p.y / pSize.y)); 130 131 switch (wParam) 132 { 133 case VK_UP: 134 p.y = max(0, p.y - 1); 135 break; 136 case VK_DOWN: 137 p.y = min(DIVISIONS - 1, p.y + 1); 138 break; 139 case VK_LEFT: 140 p.x = max(0, p.x - 1); 141 break; 142 case VK_RIGHT: 143 p.x = min(DIVISIONS - 1, p.x + 1); 144 break; 145 case VK_HOME: 146 p.x = p.y = 0; 147 break; 148 case VK_END: 149 p.x = p.y = DIVISIONS - 1; 150 break; 151 case VK_RETURN: 152 case VK_SPACE: 153 SendMessage(hWnd, WM_LBUTTONDOWN, MK_LBUTTON, MAKELONG(p.x * pSize.x, p.y * pSize.y)); 154 break; 155 } 156 p.x = (p.x * pSize.x) + (pSize.x / 2); 157 p.y = (p.y * pSize.y) + (pSize.y / 2); 158 159 ClientToScreen(hWnd, &p); 160 SetCursorPos(p.x, p.y); 161 ShowCursor(true); 162 return 0; 163 case WM_SIZE: 164 //pSize每个格子大小,X宽,Y高 165 pSize.x = LOWORD(lParam) / DIVISIONS; 166 pSize.y = HIWORD(lParam) / DIVISIONS; 167 return 0; 168 case WM_LBUTTONDOWN: 169 p.x = GET_X_LPARAM(lParam) / pSize.x; 170 p.y = GET_Y_LPARAM(lParam) / pSize.y; 171 if (p.x >= DIVISIONS || p.y >= DIVISIONS) 172 { 173 MessageBeep(0); 174 return 0; 175 } 176 switch (iClick[p.x][p.y]) 177 { 178 case 1: 179 MessageBeep(0); 180 return 0; 181 case 2: 182 MessageBeep(0); 183 return 0; 184 } 185 //判断是否点击到了雷 186 if (iChess[p.x][p.y] == -1) 187 { 188 if (MessageBox(hWnd, TEXT("你踩到地雷了!"), TEXT("boom"), MB_OK) == IDOK) 189 { 190 Reset(pChess, pClick); 191 InvalidateRect(hWnd, NULL, true); 192 } 193 } 194 else 195 { 196 //当前位置修改为点击过 197 iClick[p.x][p.y] = 1; 198 //判断是否胜利 199 if (Win(pClick)) 200 { 201 if (MessageBox(hWnd, TEXT("you win!"), TEXT("wow"), MB_OK) == IDOK) 202 { 203 Reset(pChess, pClick); 204 } 205 } 206 //计算当前点击位置附近雷数 207 for (int x = -1; x <= 1; x++) 208 { 209 for (int y = -1; y <= 1; y++) 210 { 211 //超出客户区的格子不计算 212 if (p.x + x >= 0 && p.x + x < DIVISIONS && p.y + y >= 0 && p.y + y < DIVISIONS) 213 { 214 //当前格子不计算 215 if (x != 0 || y != 0) { 216 if (iChess[p.x + x][p.y + y] == -1) 217 { 218 iChess[p.x][p.y]++; 219 } 220 } 221 } 222 } 223 } 224 } 225 //重绘格子 226 rect.left = p.x * pSize.x; 227 rect.top = p.y * pSize.y; 228 rect.right = (p.x + 1) * pSize.x; 229 rect.bottom = (p.y + 1) * pSize.y; 230 InvalidateRect(hWnd, &rect, true); 231 return 0; 232 case WM_RBUTTONDOWN: 233 p.x = GET_X_LPARAM(lParam) / pSize.x; 234 p.y = GET_Y_LPARAM(lParam) / pSize.y; 235 if (p.x >= DIVISIONS || p.y >= DIVISIONS) 236 { 237 MessageBeep(0); 238 return 0; 239 } 240 //当前格子标记地雷状态切换 241 switch (iClick[p.x][p.y]) 242 { 243 case 0: 244 iClick[p.x][p.y] = 2; 245 break; 246 case 2: 247 iClick[p.x][p.y] = 0; 248 break; 249 default: 250 MessageBeep(0); 251 return 0; 252 } 253 //重绘格子 254 rect.left = p.x * pSize.x; 255 rect.top = p.y * pSize.y; 256 rect.right = (p.x + 1) * pSize.x; 257 rect.bottom = (p.y + 1) * pSize.y; 258 InvalidateRect(hWnd, NULL, true); 259 return 0; 260 case WM_MOUSEMOVE: 261 //鼠标超出扫雷区域鼠标禁止点击 262 p.x = GET_X_LPARAM(lParam) / pSize.x; 263 p.y = GET_Y_LPARAM(lParam) / pSize.y; 264 if (p.x >= DIVISIONS || p.y >= DIVISIONS) 265 { 266 SetCursor(LoadCursor(NULL, IDC_NO)); 267 } 268 else 269 { 270 SetCursor(LoadCursor(NULL, IDC_ARROW)); 271 } 272 return 0; 273 case WM_PAINT: 274 hdc = BeginPaint(hWnd, &ps); 275 //扫雷棋盘绘画 276 for (int i = 1; i < DIVISIONS; i++) 277 { 278 MoveToEx(hdc, pSize.x * i, 0, NULL); 279 LineTo(hdc, pSize.x * i, pSize.y * DIVISIONS); 280 281 MoveToEx(hdc, 0, pSize.y * i, NULL); 282 LineTo(hdc, pSize.x * DIVISIONS, pSize.y * i); 283 } 284 //扫雷点击绘画 285 for (int x = 0; x < DIVISIONS; x++) 286 { 287 for (int y = 0; y < DIVISIONS; y++) 288 { 289 //0:未开过的格子,1:开过的格子,2:标记过的格子 290 switch (iClick[x][y]) 291 { 292 case 1: 293 //每个格子背景变成灰色 294 hBrush = CreateSolidBrush(RGB(220, 220, 220));hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); 295 Rectangle(hdc, x * pSize.x, y * pSize.y, (x + 1) * pSize.x, (y + 1) * pSize.y); 296 SelectObject(hdc, hOldBrush);DeleteObject(hBrush);DeleteObject(hOldBrush); 297 298 //当前格子写入地雷数 299 //地雷数颜色随地雷数改变 300 switch (iChess[x][y]) 301 { 302 case 0: 303 SetTextColor(hdc, RGB(0, 0, 0)); break; 304 case 1: 305 SetTextColor(hdc, RGB(0, 0, 255)); break; 306 case 2: 307 SetTextColor(hdc, RGB(144, 238, 144)); break; 308 case 3: 309 SetTextColor(hdc, RGB(255, 0, 0)); break; 310 case 4: 311 SetTextColor(hdc, RGB(255, 0, 255)); break; 312 case 5: 313 SetTextColor(hdc, RGB(139, 0, 0)); break; 314 case 6: 315 SetTextColor(hdc, RGB(0, 100, 0)); break; 316 } 317 SetBkMode(hdc, TRANSPARENT); 318 StringCchPrintf(szBuffer, sizeof(szBuffer), TEXT("%d"), iChess[x][y]); 319 StringCchLength(szBuffer, sizeof(szBuffer), &iTarget); 320 TextOut(hdc, (x * pSize.x) + (pSize.x / 2) - (cxChar / 2), (y * pSize.y) + (pSize.y / 2) - (cyChar / 2), szBuffer, iTarget); 321 break; 322 case 2: 323 hBrush = CreateSolidBrush(RGB(255, 0, 0));hOldBrush = (HBRUSH)SelectObject(hdc, hBrush); 324 //画一个标记的旗子 325 MoveToEx(hdc, (x * pSize.x) + (pSize.x / 2), (y * pSize.y) + (pSize.y / 2) - 5, NULL); 326 LineTo(hdc, (x * pSize.x) + (pSize.x / 2), (y * pSize.y) + (pSize.y / 2) + 5); 327 328 Rectangle(hdc, (x * pSize.x) + (pSize.x / 2), (y * pSize.y) + (pSize.y / 2) - 5, (x * pSize.x) + (pSize.x / 2) + 10, (y * pSize.y) + (pSize.y / 2)); 329 330 MoveToEx(hdc, (x * pSize.x) + (pSize.x / 2) - 3, (y * pSize.y) + (pSize.y / 2) + 5, NULL); 331 LineTo(hdc, (x * pSize.x) + (pSize.x / 2) + 3, (y * pSize.y) + (pSize.y / 2) + 5); 332 333 SelectObject(hdc, hOldBrush);DeleteObject(hBrush);DeleteObject(hOldBrush); 334 break; 335 } 336 337 } 338 } 339 EndPaint(hWnd, &ps); 340 return 0; 341 case WM_DESTROY: 342 PostQuitMessage(0); 343 break; 344 default: 345 return DefWindowProc(hWnd, uMsg, wParam, lParam); 346 } 347 return 0; 348 } 349 //产生随机数 350 unsigned int GetRand() 351 { 352 srand((unsigned(time(NULL)))); 353 int r = rand(); 354 r = r % DIVISIONS; 355 return r; 356 } 357 358 //写入地雷 359 void SetMine(int(*pChess)[DIVISIONS]) 360 { 361 POINT pPoint; 362 for (int i = 0; i < MINECOUNT; i++) 363 { 364 while (true) 365 { 366 pPoint.x = GetRand(); 367 pPoint.y = GetRand(); 368 if (pChess[pPoint.x][pPoint.y] != -1) 369 { 370 pChess[pPoint.x][pPoint.y] = -1; 371 break; 372 } 373 } 374 } 375 return; 376 } 377 378 //判断获胜 379 bool Win(int(*pChess)[DIVISIONS]) 380 { 381 int i = 0; 382 for (int x = 0; x < DIVISIONS; x++) 383 { 384 for (int y = 0; y < DIVISIONS; y++) 385 { 386 if (pChess[x][y] == 0) 387 { 388 i++; 389 } 390 } 391 } 392 if (i - MINECOUNT == 0) 393 { 394 return true; 395 } 396 return false; 397 } 398 399 //重置 400 void Reset(int(*pChess)[DIVISIONS], int(*pClick)[DIVISIONS]) 401 { 402 SetCursor(LoadCursor(NULL, IDC_WAIT)); 403 memset(pChess, 0, sizeof(pChess)); 404 memset(pClick, 0, sizeof(pClick)); 405 SetMine(pChess); 406 SetCursor(LoadCursor(NULL, IDC_ARROW)); 407 return; 408 }
以上是关于WINAPI实现简易扫雷游戏的主要内容,如果未能解决你的问题,请参考以下文章