diff --git a/list.md b/list.md index 2121330..c8351d2 100644 --- a/list.md +++ b/list.md @@ -50,4 +50,4 @@ 简要说明:完成最终游戏界面绘制,并与前面的逻辑配合形成可运行的完整项目。 -- [ ] `20. TDrawScreen` - `src/source/TetrisRender.cpp` +- [x] `20. TDrawScreen` - `src/source/TetrisRender.cpp` diff --git a/report/code-snippets/Part6/After1.png b/report/code-snippets/Part6/After1.png new file mode 100644 index 0000000..349755b Binary files /dev/null and b/report/code-snippets/Part6/After1.png differ diff --git a/report/code-snippets/Part6/After2.png b/report/code-snippets/Part6/After2.png new file mode 100644 index 0000000..a05447f Binary files /dev/null and b/report/code-snippets/Part6/After2.png differ diff --git a/report/code-snippets/Part6/Before.png b/report/code-snippets/Part6/Before.png new file mode 100644 index 0000000..c786eb8 Binary files /dev/null and b/report/code-snippets/Part6/Before.png differ diff --git a/report/images/Part6/part6Effect.png b/report/images/Part6/part6Effect.png new file mode 100644 index 0000000..ddb05d6 Binary files /dev/null and b/report/images/Part6/part6Effect.png differ diff --git a/src/source/TetrisRender.cpp b/src/source/TetrisRender.cpp index 163acf6..69e458d 100644 --- a/src/source/TetrisRender.cpp +++ b/src/source/TetrisRender.cpp @@ -1,9 +1,228 @@ #include "stdafx.h" #include "Tetris.h" +/** + * @brief 完整绘制当前游戏界面。 + * + * 该函数负责绘制窗口背景、游戏工作区边框、网格、已经固定的方块、 + * 当前活动方块、预测落点、右侧信息面板以及游戏结束提示文字。 + * 绘制过程只负责根据当前全局游戏状态进行显示,不修改任何游戏逻辑数据。 + * + * @param hdc 当前窗口绘图设备上下文。 + * @param hWnd 当前窗口句柄,用于获取客户区尺寸。 + */ void TDrawScreen(HDC hdc, HWND hWnd) { - // TODO(作业20): 完整绘制游戏界面。 - UNREFERENCED_PARAMETER(hdc); - UNREFERENCED_PARAMETER(hWnd); + RECT clientRect; + GetClientRect(hWnd, &clientRect); + + HBRUSH backgroundBrush = CreateSolidBrush(RGB(245, 245, 245)); + FillRect(hdc, &clientRect, backgroundBrush); + DeleteObject(backgroundBrush); + + RECT gameRect = + { + GRID, + GRID, + GRID + nGameWidth * GRID, + GRID + nGameHeight * GRID + }; + + // 绘制游戏区背景 + HBRUSH gameBrush = CreateSolidBrush(RGB(30, 30, 30)); + FillRect(hdc, &gameRect, gameBrush); + DeleteObject(gameBrush); + + // 绘制游戏区边框 + HPEN borderPen = CreatePen(PS_SOLID, 2, RGB(80, 80, 80)); + HPEN oldPen = (HPEN)SelectObject(hdc, borderPen); + HBRUSH oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); + Rectangle(hdc, gameRect.left, gameRect.top, gameRect.right, gameRect.bottom); + SelectObject(hdc, oldBrush); + SelectObject(hdc, oldPen); + DeleteObject(borderPen); + + // 绘制网格线 + HPEN gridPen = CreatePen(PS_SOLID, 1, RGB(55, 55, 55)); + oldPen = (HPEN)SelectObject(hdc, gridPen); + + for (int i = 1; i < nGameWidth; i++) + { + int x = gameRect.left + i * GRID; + MoveToEx(hdc, x, gameRect.top, nullptr); + LineTo(hdc, x, gameRect.bottom); + } + + for (int i = 1; i < nGameHeight; i++) + { + int y = gameRect.top + i * GRID; + MoveToEx(hdc, gameRect.left, y, nullptr); + LineTo(hdc, gameRect.right, y); + } + + SelectObject(hdc, oldPen); + DeleteObject(gridPen); + + // 绘制已经固定的方块 + for (int i = 0; i < nGameHeight; i++) + { + for (int j = 0; j < nGameWidth; j++) + { + if (workRegion[i][j] != 0) + { + int colorIndex = workRegion[i][j] - 1; + RECT brickRect = + { + gameRect.left + j * GRID + 1, + gameRect.top + i * GRID + 1, + gameRect.left + (j + 1) * GRID - 1, + gameRect.top + (i + 1) * GRID - 1 + }; + + HBRUSH brickBrush = CreateSolidBrush(BrickColor[colorIndex]); + FillRect(hdc, &brickRect, brickBrush); + DeleteObject(brickBrush); + } + } + } + + // 绘制预测落点 + if (targetFlag) + { + HPEN targetPen = CreatePen(PS_DOT, 1, RGB(220, 220, 220)); + oldPen = (HPEN)SelectObject(hdc, targetPen); + oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); + + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (bricks[type][state][i][j] != 0) + { + int drawY = target.y + i; + int drawX = target.x + j; + + if (drawY >= 0 && drawY < nGameHeight && drawX >= 0 && drawX < nGameWidth) + { + Rectangle( + hdc, + gameRect.left + drawX * GRID + 4, + gameRect.top + drawY * GRID + 4, + gameRect.left + (drawX + 1) * GRID - 4, + gameRect.top + (drawY + 1) * GRID - 4); + } + } + } + } + + SelectObject(hdc, oldBrush); + SelectObject(hdc, oldPen); + DeleteObject(targetPen); + } + + // 绘制当前活动方块 + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (bricks[type][state][i][j] != 0) + { + int drawY = point.y + i; + int drawX = point.x + j; + + if (drawY >= 0 && drawY < nGameHeight && drawX >= 0 && drawX < nGameWidth) + { + RECT brickRect = + { + gameRect.left + drawX * GRID + 1, + gameRect.top + drawY * GRID + 1, + gameRect.left + (drawX + 1) * GRID - 1, + gameRect.top + (drawY + 1) * GRID - 1 + }; + + HBRUSH brickBrush = CreateSolidBrush(BrickColor[type]); + FillRect(hdc, &brickRect, brickBrush); + DeleteObject(brickBrush); + } + } + } + } + + // 绘制右侧信息面板 + SetBkMode(hdc, TRANSPARENT); + SetTextColor(hdc, RGB(40, 40, 40)); + + RECT infoRect = + { + gameRect.right + GRID, + gameRect.top, + clientRect.right - GRID, + gameRect.bottom + }; + + DrawText(hdc, _T("Tetris"), -1, &infoRect, DT_TOP | DT_LEFT | DT_SINGLELINE); + + TCHAR scoreText[64]; + _stprintf_s(scoreText, _T("Score: %d"), tScore); + TextOut(hdc, infoRect.left, infoRect.top + GRID * 2, scoreText, lstrlen(scoreText)); + + TextOut(hdc, infoRect.left, infoRect.top + GRID * 4, _T("Next:"), 5); + + RECT nextRect = + { + infoRect.left, + infoRect.top + GRID * 5, + infoRect.left + GRID * 4, + infoRect.top + GRID * 9 + }; + + HBRUSH nextBackBrush = CreateSolidBrush(RGB(220, 220, 220)); + FillRect(hdc, &nextRect, nextBackBrush); + DeleteObject(nextBackBrush); + + HPEN nextBorderPen = CreatePen(PS_SOLID, 1, RGB(120, 120, 120)); + oldPen = (HPEN)SelectObject(hdc, nextBorderPen); + oldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); + Rectangle(hdc, nextRect.left, nextRect.top, nextRect.right, nextRect.bottom); + SelectObject(hdc, oldBrush); + SelectObject(hdc, oldPen); + DeleteObject(nextBorderPen); + + // 绘制下一方块预览 + for (int i = 0; i < 4; i++) + { + for (int j = 0; j < 4; j++) + { + if (bricks[nType][0][i][j] != 0) + { + RECT brickRect = + { + nextRect.left + j * GRID, + nextRect.top + i * GRID, + nextRect.left + (j + 1) * GRID, + nextRect.top + (i + 1) * GRID + }; + + HBRUSH brickBrush = CreateSolidBrush(BrickColor[nType]); + FillRect(hdc, &brickRect, brickBrush); + DeleteObject(brickBrush); + } + } + } + + TextOut(hdc, infoRect.left, infoRect.top + GRID * 11, _T("Controls:"), 9); + TextOut(hdc, infoRect.left, infoRect.top + GRID * 12, _T("Arrow Keys"), 10); + TextOut(hdc, infoRect.left, infoRect.top + GRID * 13, _T("Space: Drop"), 11); + + if (suspendFlag) + { + TextOut(hdc, infoRect.left, infoRect.top + GRID * 15, _T("Paused"), 6); + } + + if (gameOverFlag) + { + RECT overRect = gameRect; + SetTextColor(hdc, RGB(255, 80, 80)); + DrawText(hdc, _T("GAME OVER"), -1, &overRect, DT_CENTER | DT_VCENTER | DT_SINGLELINE); + } }