一派护法 十九级 |
【main.c】 #include <tchar.h> #include <time.h> #include <Windows.h> #include "diamonds.h"
BOOL bGameOver = FALSE; BOOL bPaused = FALSE; HWND hwndMain; extern LPTSTR lpMsg;
// 窗口过程函数 LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { HDC hdc; PAINTSTRUCT ps;
switch (uMsg) { case WM_CHAR: switch (wParam) { case '+': case '=': if (!bGameOver && !bPaused) AdjustDiamond(DOWN); break; case '-': case '_': if (!bGameOver && !bPaused) AdjustDiamond(UP); break; case 'l': case 'L': if (!bPaused) LoadGame(); break; case 'p': case 'P': if (!bGameOver) { bPaused = !bPaused; UpdateGame(); } break; case 's': case 'S': if (bGameOver) ShowMsg(TEXT("游戏结束时不能存档")); else SaveGame(); break; } break; case WM_CREATE: hwndMain = hWnd; srand((UINT)time(NULL)); InitGame(); break; case WM_DESTROY: KillTimer(hWnd, IDT_TIMER1); // 关闭定时器 ExitGame(); // 清理游戏数据 PostQuitMessage(0); // 结束消息循环 break; case WM_KEYDOWN: if (bPaused) break; // 游戏暂停时不能执行这些操作 switch (wParam) { case VK_DOWN: if (!bGameOver) MoveDiamond(DOWN); break; case VK_ESCAPE: RestartGame(); break; case VK_LEFT: if (!bGameOver) MoveDiamond(LEFT); break; case VK_RIGHT: if (!bGameOver) MoveDiamond(RIGHT); break; } break; case WM_PAINT: hdc = BeginPaint(hWnd, &ps); Draw(hdc, &ps); EndPaint(hWnd, &ps); break; case WM_TIMER: switch (wParam) { case IDT_TIMER1: if (!bPaused) MoveDiamond(DOWN); break; case IDT_TIMER2: lpMsg = NULL; UpdateGame(); KillTimer(hWnd, IDT_TIMER2); } break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return FALSE; }
// 主函数 int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { HWND hWnd; MSG msg; RECT rect; WNDCLASSEX wcex;
// 注册窗口类 ZeroMemory(&wcex, sizeof(wcex)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION); wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wcex.hInstance = hInstance; wcex.lpfnWndProc = WndProc; wcex.lpszClassName = TEXT("Diamonds"); wcex.style = CS_HREDRAW | CS_VREDRAW; RegisterClassEx(&wcex);
// 计算窗口大小 SetRect(&rect, 0, 0, GAME_WIDTH, GAME_HEIGHT); AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE);
// 创建并显示窗口 hWnd = CreateWindow(wcex.lpszClassName, TEXT("宝石方块"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL, hInstance, NULL); if (!hWnd) return 1; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd);
// 消息循环 while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; }
|
一派护法 十九级 |
【diamonds.c】 #include <stdio.h> #include <tchar.h> #include <Windows.h> #include <strsafe.h> #include "diamonds.h"
COLORREF clrColorList[] = {0xa00000, 0xa05000, 0xa0a000, 0xc000, 0xa0a0, 0x4040c0, 0xa000a0, 0x808080, 0x577ab9, 0xec9ff}; // 颜色表
/* 应写入文件的游戏数据 */ GAME game; BYTE bMap[DIAMOND_VERT_NUM][DIAMOND_VERT_NUM]; // 已释放的宝石列表
/* 不应写入文件的游戏数据*/ HFONT hFont = NULL; HFONT hfontWarning = NULL; LPTSTR lpMsg = NULL;
extern BOOL bGameOver, bPaused; extern HWND hwndMain;
// 调整宝石顺序 void AdjustDiamond(BYTE bDirection) { if (bDirection == UP) { game.bDiamond = (game.bDiamond >> 2) | ((game.bDiamond & 0x03) << 4); game.wColor = (game.wColor >> 4) | ((game.wColor & 0x0f) << 8); } else { game.bDiamond = ((game.bDiamond << 2) & 0x3c) | (game.bDiamond >> 4); game.wColor = ((game.wColor << 4) & 0xff0) | (game.wColor >> 8); } UpdateGame(); }
// 检查模型是否与已有宝石碰撞 BOOL CheckCrash(void) { int i, y; for (i = 0; i < 3; i++) { y = game.ptDiamond.y + i; if (bMap[y][game.ptDiamond.x] & 0x80) return TRUE; } return FALSE; }
// 随机生成模型 void CreateDiamond(void) { DWORD dwData[2] = {0}; int nColors = _countof(clrColorList); // 颜色表中的颜色数 int i, j, n; if (!(game.bNextDiamond & 0x80)) { // 如果创建的不是第一个模型 n = 1; game.bDiamond = game.bNextDiamond; game.wColor = game.wNextColor; } else n = 2; for (i = 0; i < n; i++) { // 形状: 00CCBBAA for (j = 0; j < 3; j++) { dwData[i] <<= 2; dwData[i] |= rand() % 4; } // 颜色: 0000CCCC BBBBAAAA for (j = 0; j < 3; j++) { dwData[i] <<= 4; dwData[i] |= rand() % nColors; } } game.bNextDiamond = dwData[0] >> 12; game.wNextColor = dwData[0] & 0xfff; if (n == 2) { game.bDiamond = dwData[1] >> 12; game.wColor = dwData[1] & 0xfff; } game.ptDiamond.y = 0; if (CheckCrash()) GameOver(); }
// 绘制游戏主界面 void Draw(HDC hdc, LPPAINTSTRUCT lpps) { HBITMAP hbmp; HDC hdcMem; LPCTSTR pStr; RECT rect, rcClient; TCHAR szText[50];
// 创建画布 hbmp = CreateCompatibleBitmap(hdc, GAME_WIDTH, GAME_HEIGHT); hdcMem = CreateCompatibleDC(hdc); SelectObject(hdcMem, hbmp); // 将整个画布刷成白色 if (lpps->fErase) { SetRect(&rect, 0, 0, GAME_WIDTH, GAME_HEIGHT); FillRect(hdcMem, &rect, GetSysColorBrush(COLOR_WINDOW)); }
// 绘制当前方块的矩形框 SelectObject(hdcMem, GetStockObject(BLACK_PEN)); // 黑色边框 SelectObject(hdcMem, GetStockObject(NULL_BRUSH)); // 不填充 rect.left = rect.top = 10; rect.right = rect.left + DIAMOND_WIDTH * 4; rect.bottom = rect.top + DIAMOND_HEIGHT * 4; Rectangle(hdcMem, rect.left, rect.top, rect.right + 1, rect.bottom + 1); DrawDiamondIn(hdcMem, &rect, game.bNextDiamond, game.wNextColor);
// 绘制游戏操作说明矩形框 OffsetRect(&rect, 0, rect.bottom - rect.top + 26); // 向下移动矩形 rect.bottom = rect.top + 190; Rectangle(hdcMem, rect.left, rect.top, rect.right, rect.bottom); // 在框中显示文字 SelectObject(hdcMem, hFont); // 选择字体 InflateRect(&rect, -4, -4); // 设置文本边界空白宽度(padding) pStr = TEXT("游戏操作说明\n1.按“←”左移\n2.按“→”右移\n3.按“+”“-”调整顺序\n4.按“↓”加速下落\n5.按“S”存档\n6.按“L”读档\n7.按“ESC”重新开始\n8.按“P”暂停\n9.三个形状一样的宝石相遇即可消除"); DrawText(hdcMem, pStr, lstrlen(pStr), &rect, DT_WORDBREAK);
// 绘制游戏区域框 rect.top = 10; rect.left = rect.right + 36; rect.right = rect.left + DIAMOND_HORZ_NUM * DIAMOND_WIDTH; rect.bottom = rect.top + DIAMOND_VERT_NUM * DIAMOND_HEIGHT; Rectangle(hdcMem, rect.left, rect.top, rect.right + 1, rect.bottom + 1);
// 绘制游戏区域 DrawGameDistrict(hdcMem, &rect);
// 在游戏区域下面显示分数 rect.top = rect.bottom; rect.bottom = GAME_HEIGHT; StringCchPrintf(szText, _countof(szText), TEXT("分数: %u"), game.uScore); SelectObject(hdcMem, hfontWarning); SetTextColor(hdcMem, RGB(163, 73, 164)); SetBkMode(hdcMem, TRANSPARENT); DrawText(hdcMem, szText, lstrlen(szText), &rect, DT_CENTER);
SetRect(&rect, 0, GAME_HEIGHT / 2 - 26, GAME_WIDTH, GAME_HEIGHT / 2 + 16); if (lpMsg != NULL) { SetTextColor(hdcMem, RGB(255, 128, 0)); rect.bottom += 20; DrawText(hdcMem, lpMsg, lstrlen(lpMsg), &rect, DT_CENTER); } else if (bGameOver || bPaused) { SetTextColor(hdcMem, RGB(255, 0, 0)); if (bGameOver) pStr = TEXT("GAME OVER"); else if (bPaused) pStr = TEXT("GAME PAUSED"); DrawText(hdcMem, pStr, lstrlen(pStr), &rect, DT_CENTER); } // 将画布上的内容显示到屏幕上 GetClientRect(hwndMain, &rcClient); StretchBlt(hdc, 0, 0, rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, hdcMem, 0, 0, GAME_WIDTH, GAME_HEIGHT, SRCCOPY);
// 删除画布 DeleteDC(hdcMem); DeleteObject(hbmp); }
// 绘制宝石模型 void DrawDiamond(HDC hdc, int x, int y, BYTE bDiamond, WORD wColor) { HBRUSH hbrOld; HPEN hpenOld; int i; RECT rect; rect.left = x; rect.top = y; rect.right = rect.left + DIAMOND_WIDTH + 1; hbrOld = (HBRUSH)SelectObject(hdc, GetStockObject(DC_BRUSH)); hpenOld = (HPEN)SelectObject(hdc, GetStockObject(BLACK_PEN)); // 选择黑色画笔 for (i = 0; i < 3; i++) { SetDCBrushColor(hdc, clrColorList[wColor & 0x0f]); rect.bottom = rect.top + DIAMOND_HEIGHT + 1; DrawShape(hdc, &rect, bDiamond & 0x03); rect.top += DIAMOND_HEIGHT; bDiamond >>= 2; wColor >>= 4; }
// 还原之前的画刷和画笔 SelectObject(hdc, hbrOld); SelectObject(hdc, hpenOld); }
// 在指定区域内绘制宝石模型 void DrawDiamondIn(HDC hdc, LPRECT lpRect, BYTE bDiamond, WORD wColor) { int x, y; x = lpRect->left + (lpRect->right - lpRect->left - DIAMOND_WIDTH) / 2; y = lpRect->top + (lpRect->bottom - lpRect->top - DIAMOND_HEIGHT * 3) / 2; DrawDiamond(hdc, x, y, bDiamond, wColor); }
void DrawGameDistrict(HDC hdc, LPRECT lpRect) { HBRUSH hbrOld; HPEN hpenOld; int x, y; RECT rect; // 已释放的宝石 hbrOld = (HBRUSH)SelectObject(hdc, GetStockObject(DC_BRUSH)); hpenOld = (HPEN)SelectObject(hdc, GetStockObject(BLACK_PEN)); for (y = 0; y < DIAMOND_VERT_NUM; y++) { for (x = 0; x < DIAMOND_HORZ_NUM; x++) { if (bMap[y][x] & 0x80) { rect.left = lpRect->left + x * DIAMOND_WIDTH; rect.top = lpRect->top + y * DIAMOND_HEIGHT; rect.right = rect.left + DIAMOND_WIDTH + 1; rect.bottom = rect.top + DIAMOND_HEIGHT + 1; SetDCBrushColor(hdc, clrColorList[bMap[y][x] & 0x0f]); DrawShape(hdc, &rect, (bMap[y][x] >> 4) & 0x03); } } }
// 正在下落的宝石 if (!bGameOver) DrawDiamond(hdc, lpRect->left + game.ptDiamond.x * DIAMOND_WIDTH, lpRect->top + game.ptDiamond.y * DIAMOND_HEIGHT, game.bDiamond, game.wColor);
SelectObject(hdc, hbrOld); SelectObject(hdc, hpenOld); }
void DrawShape(HDC hdc, LPRECT lpRect, BYTE bShape) { POINT pt[4]; switch (bShape) { case 0: Rectangle(hdc, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); break; case 1: Ellipse(hdc, lpRect->left, lpRect->top, lpRect->right, lpRect->bottom); break; case 2: pt[0].x = lpRect->left + (lpRect->right - lpRect->left) / 2; pt[0].y = lpRect->top; pt[1].x = lpRect->right - 1; pt[1].y = lpRect->top + (lpRect->bottom - lpRect->top) / 2; pt[2].x = pt[0].x; pt[2].y = lpRect->bottom - 1; pt[3].x = lpRect->left; pt[3].y = pt[1].y; Polygon(hdc, pt, 4); break; case 3: pt[0].x = lpRect->left + (lpRect->right - lpRect->left) / 2; pt[0].y = lpRect->top; pt[1].x = lpRect->left; pt[1].y = lpRect->bottom - 1; pt[2].x = lpRect->right - 1; pt[2].y = pt[1].y; Polygon(hdc, pt, 3); } }
void ExitGame(void) { DeleteObject(hFont); DeleteObject(hfontWarning); }
void GameOver(void) { KillTimer(hwndMain, IDT_TIMER1); bGameOver = TRUE; }
void InitFont(void) { LOGFONT lf; ZeroMemory(&lf, sizeof(lf)); lf.lfCharSet = GB2312_CHARSET; StringCbCopy(lf.lfFaceName, sizeof(lf.lfFaceName), TEXT("宋体")); lf.lfHeight = 12; hFont = CreateFontIndirect(&lf);
lf.lfCharSet = DEFAULT_CHARSET; StringCbCopy(lf.lfFaceName, sizeof(lf.lfFaceName), TEXT("Times New Roman")); lf.lfHeight = 30; lf.lfWeight = FW_BOLD; hfontWarning = CreateFontIndirect(&lf); }
void InitGame(void) { game.ptDiamond.x = 5; game.uScore = 0; if (hFont == NULL) { InitFont(); game.bNextDiamond = 0x80; // 只有游戏最开始时才同时随机生成当前块和下一块 } ZeroMemory(bMap, sizeof(bMap)); CreateDiamond(); StartTimer(); }
BOOL LoadGame(void) { FILE *fp; fopen_s(&fp, FILENAME, "rb"); if (fp == NULL) { ShowMsg(TEXT("读取游戏进度失败\n无法打开文件")); return FALSE; } fread(&game, sizeof(GAME), 1, fp); fread(bMap, sizeof(bMap), 1, fp); fclose(fp);
if (bGameOver) { bGameOver = FALSE; StartTimer(); }
UpdateGame(); return TRUE; }
void MoveDiamond(int iDirection) { BOOL bRecreate = FALSE; POINT ptOld = game.ptDiamond;
switch (iDirection) { case LEFT: if (game.ptDiamond.x > 0) game.ptDiamond.x--; break; case RIGHT: if (game.ptDiamond.x + 1 < DIAMOND_HORZ_NUM) game.ptDiamond.x++; break; case DOWN: if (game.ptDiamond.y + 3 < DIAMOND_VERT_NUM) game.ptDiamond.y++; else bRecreate = TRUE; } if (!bRecreate) { if (CheckCrash()) { game.ptDiamond = ptOld; if (iDirection == DOWN) bRecreate = TRUE; // 只有当移动方向向下时才重新创建方块 } } if (bRecreate) { ReleaseDiamond(); RemoveDiamonds(); CreateDiamond(); } UpdateGame(); }
// 释放宝石模型 void ReleaseDiamond(void) { BYTE bData; BYTE bShape = game.bDiamond; int i; WORD wColor = game.wColor; for (i = 0; i < 3; i++) { bData = 0x80; // 最高位表示是否有宝石 bData |= (bShape & 0x03) << 4; bData |= wColor & 0x0f; bMap[game.ptDiamond.y + i][game.ptDiamond.x] = bData; bShape >>= 2; wColor >>= 4; } }
// 消除相同颜色的宝石 BOOL RemoveDiamonds(void) { BOOL bRemoved; BYTE bShape; int x, y, m, n; int score_unit = SCORE; do { // 寻找可消除的宝石 bRemoved = FALSE; for (y = 0; y < DIAMOND_VERT_NUM; y++) { for (x = 0; x < DIAMOND_HORZ_NUM; x++) { if (!(bMap[y][x] & 0x80)) continue; // 如果没有宝石则跳过 // 向右 bShape = bMap[y][x] & 0xb0; // 当前方块的形状 n = 1; // 找到的相同方块个数(包括本身) for (m = 1; x + m < DIAMOND_HORZ_NUM; m++) { // 如果有宝石且形状相同 if ((bMap[y][x + m] & 0xb0) == bShape) n++; else break; // 形状不同时立即退出 } if (n >= 3) { // 找到3个及以上相同的宝石时可消除 bRemoved = TRUE; game.uScore += score_unit; for (m = 0; m < n; m++) bMap[y][x + m] |= 0x40; // 标记删除 } // 向下 n = 1; for (m = 1; y + m < DIAMOND_VERT_NUM; m++) { if ((bMap[y + m][x] & 0xb0) == bShape) n++; else break; } if (n >= 3) { bRemoved = TRUE; game.uScore += score_unit; for (m = 0; m < n; m++) bMap[y + m][x] |= 0x40; } // 向右下 n = 1; for (m = 1; x + m < DIAMOND_HORZ_NUM && y + m < DIAMOND_VERT_NUM; m++) { if ((bMap[y + m][x + m] & 0xb0) == bShape) n++; else break; } if (n >= 3) { bRemoved = TRUE; game.uScore += score_unit; for (m = 0; m < n; m++) bMap[y + m][x + m] |= 0x40; } // 向左下 n = 1; for (m = 1; x - m >= 0 && y + m < DIAMOND_VERT_NUM; m++) { if ((bMap[y + m][x - m] & 0xb0) == bShape) n++; else break; } if (n >= 3) { bRemoved = TRUE; game.uScore += score_unit; for (m = 0; m < n; m++) bMap[y + m][x - m] |= 0x40; } } } if (bRemoved) { score_unit *= 1.5; for (x = 0; x < DIAMOND_HORZ_NUM; x++) { // 清除加了标记的宝石 for (y = 0; y < DIAMOND_VERT_NUM; y++) { if (bMap[y][x] & 0x40) bMap[y][x] = 0; } // 消除腾出的空位 m = -1; // 空白区域开始处 for (y = DIAMOND_VERT_NUM - 1; y >= 0; y--) { if (bMap[y][x] & 0x80) { if (m != -1) { // 移动宝石 while (y >= 0 && (bMap[y][x] & 0x80)) { bMap[m][x] = bMap[y][x]; bMap[y][x] = 0; m--; y--; } y = m + 1; m = -1; } } else { if (m == -1) m = y; } } } } } while (bRemoved); // 只要有宝石被消去,就必须重新检查 }
void RestartGame(void) { if (bGameOver) bGameOver = FALSE; else KillTimer(hwndMain, IDT_TIMER1); ZeroMemory(bMap, sizeof(bMap)); // 删除所有之前的宝石 InitGame(); UpdateGame(); }
BOOL SaveGame(void) { FILE *fp; fopen_s(&fp, FILENAME, "wb"); if (fp == NULL) { ShowMsg(TEXT("保存游戏进度失败\n无法打开文件")); return FALSE; } fwrite(&game, sizeof(GAME), 1, fp); fwrite(bMap, sizeof(bMap), 1, fp); fclose(fp); return TRUE; }
void ShowMsg(LPTSTR lpStr) { lpMsg = lpStr; UpdateGame(); SetTimer(hwndMain, IDT_TIMER2, 3000, NULL); }
void StartTimer(void) { SetTimer(hwndMain, IDT_TIMER1, 800, NULL); }
void UpdateGame(void) { // 重绘整个窗口 InvalidateRect(hwndMain, NULL, TRUE); }
|