用VS2022C++控制台应用写的一个简单的俄罗斯方块游戏,带计分功能

#include <iostream> #include <format> #include <vector> #include <string> #include <thread> #include <chrono> #include <random> #include <ranges> #include <conio.h> #include <windows.h> #include <ctime> #include <cstdlib> #include <sstream> #include <io.h> #include <fcntl.h> using namespace std; // 修改后的游戏区尺寸:宽度40,高度20 const int nFieldWidth = 40; const int nFieldHeight = 20; vector<int> field(nFieldWidth * nFieldHeight, 0); // 7 种俄罗斯方块,每个用 16 个字符表示(4×4 方阵) vector<wstring> tetromino(7); // 4×4 方阵旋转函数 static int Rotate(int px, int py, int r) { switch (r % 4) { case 0: return py * 4 + px; // 0 度 case 1: return 12 + py - (px * 4); // 90 度 case 2: return 15 - (py * 4) - px; // 180 度 case 3: return 3 - py + (px * 4); // 270 度 } return 0; } // 判断方块是否可以放置到 (posX, posY) static bool DoesPieceFit(int tetrominoIndex, int rotation, int posX, int posY) { for (int x = 0; x < 4; x++) { for (int y = 0; y < 4; y++) { int pi = Rotate(x, y, rotation); int fi = (posY + y) * nFieldWidth + (posX + x); if (posX + x >= 0 && posX + x < nFieldWidth && posY + y >= 0 && posY + y < nFieldHeight) { if (tetromino[tetrominoIndex][pi] == L'X' && field[fi] != 0) return false; } } } return true; } static int ShowMenu(HANDLE hConsole) { // 清屏并重置光标 system("cls"); CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); int consoleWidth = csbi.dwSize.X; int consoleHeight = csbi.dwSize.Y; int menuY = consoleHeight / 2 - 2; vector<wstring> options = { L"1. 游戏开始", L"2. 游戏说明", L"3. 游戏结束" }; // 定义不同的颜色 WORD colors[3] = { FOREGROUND_GREEN | FOREGROUND_INTENSITY, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, FOREGROUND_RED | FOREGROUND_INTENSITY }; for (int i = 0; i < 3; i++) { int len = options[i].length(); int offsetX = (consoleWidth - len) / 2; COORD pos = { static_cast<SHORT>(offsetX), static_cast<SHORT>(menuY + i) }; SetConsoleCursorPosition(hConsole, pos); SetConsoleTextAttribute(hConsole, colors[i]); wcout << options[i]; } // 恢复默认颜色 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); // 等待键盘输入 int ch = _getch(); return ch - '0'; } static void ShowInstructions(HANDLE hConsole) { system("cls"); SetConsoleTextAttribute(hConsole, FOREGROUND_BLUE | FOREGROUND_INTENSITY); wcout << L"【游戏说明】\n"; wcout << L"1. 用左/右箭头键或A/D 控制方块左右移动。\n"; wcout << L"2. 用上箭头键或Z 控制方块旋转。\n"; wcout << L"3. 用下箭头键或S 控制方块加速下落。\n"; wcout << L"4. 当一整行填满后,该行会消除并获得加分。\n"; wcout << L"按任意键返回菜单..."; (void)_getch(); // 恢复默认颜色 SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); } static void RunGame(HANDLE hConsole) { // 利用 C++ 随机数生成器确保7种不同方块概率均等 mt19937 rng(random_device{}()); uniform_int_distribution<int> piece_dist(0, 6); // 初始化俄罗斯方块形状 tetromino[0] = L"..X...X...X...X."; // I tetromino[1] = L"..X..XX...X....."; // T tetromino[2] = L".....XX..XX....."; // O tetromino[3] = L"..X..XX..X......"; // S tetromino[4] = L".X...XX...X....."; // Z tetromino[5] = L".X...X...XX....."; // J tetromino[6] = L"..X...X..XX....."; // L // 初始化游戏区,设置边界(上、左、右和底部为墙体,墙体值为9) for (int x = 0; x < nFieldWidth; x++) { for (int y = 0; y < nFieldHeight; y++) { if (x == 0 || x == nFieldWidth - 1 || y == 0 || y == nFieldHeight - 1) field[static_cast<size_t>(y) * nFieldWidth + x] = 9; else field[static_cast<size_t>(y) * nFieldWidth + x] = 0; } } // 定义各个方块 (1~7) 的颜色(固定方块及当前活动块均使用对应颜色) WORD pieceColors[8] = { 0, FOREGROUND_RED | FOREGROUND_INTENSITY, // 1:红色 FOREGROUND_GREEN | FOREGROUND_INTENSITY, // 2:绿色 FOREGROUND_BLUE | FOREGROUND_INTENSITY, // 3:蓝色 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, // 4:黄色 FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY, // 5:紫色(品红) FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, // 6:青色 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE // 7:白色 }; // 壁体颜色 WORD wallColor = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE; // 游戏相关变量 bool gameOver = false; int score = 0; int currentPiece = piece_dist(rng); int currentRotation = 0; // 顶部为墙体,所以初始行设置为1 int currentX = nFieldWidth / 2 - 2; int currentY = 1; // 下落速度修改:由20改为10,使得下落速度为之前的两倍 int speed = 10; int speedCount = 0; bool forceDown = false; auto lastTime = chrono::steady_clock::now(); // 游戏主循环 while (!gameOver) { this_thread::sleep_for(chrono::milliseconds(50)); speedCount++; forceDown = (speedCount == speed); // 键盘输入处理,支持扩展键和普通键 if (_kbhit()) { int key = _getch(); if (key == 224) { int key2 = _getch(); switch (key2) { case 75: if (DoesPieceFit(currentPiece, currentRotation, currentX - 1, currentY)) currentX--; break; case 77: if (DoesPieceFit(currentPiece, currentRotation, currentX + 1, currentY)) currentX++; break; case 80: if (DoesPieceFit(currentPiece, currentRotation, currentX, currentY + 1)) currentY++; break; case 72: if (DoesPieceFit(currentPiece, (currentRotation + 1) % 4, currentX, currentY)) currentRotation = (currentRotation + 1) % 4; break; } } else { switch (key) { case 'a': case 'A': if (DoesPieceFit(currentPiece, currentRotation, currentX - 1, currentY)) currentX--; break; case 'd': case 'D': if (DoesPieceFit(currentPiece, currentRotation, currentX + 1, currentY)) currentX++; break; case 's': case 'S': if (DoesPieceFit(currentPiece, currentRotation, currentX, currentY + 1)) currentY++; break; case 'z': case 'Z': if (DoesPieceFit(currentPiece, (currentRotation + 1) % 4, currentX, currentY)) currentRotation = (currentRotation + 1) % 4; break; } } } // 自动下降 if (forceDown) { speedCount = 0; if (DoesPieceFit(currentPiece, currentRotation, currentX, currentY + 1)) currentY++; else { // 固定当前方块到游戏区——固定块统一设置为白色(值7) for (int px = 0; px < 4; px++) { for (int py = 0; py < 4; py++) { if (tetromino[currentPiece][Rotate(px, py, currentRotation)] == L'X') field[static_cast<size_t>(currentY + py) * nFieldWidth + (static_cast<unsigned long long>(currentX) + px)] = 7; } } // 检查是否形成整行 for (int py = 0; py < 4; py++) { if (currentY + py < nFieldHeight - 1) { bool line = true; for (int px = 1; px < nFieldWidth - 1; px++) line &= (field[static_cast<size_t>(currentY + py) * nFieldWidth + px] != 0); if (line) { score += 100; for (int px = 1; px < nFieldWidth - 1; px++) field[static_cast<size_t>(currentY + py) * nFieldWidth + px] = 8; } } } // 删除标记行并向下移动上方块 for (int y = nFieldHeight - 2; y > 0; y--) { for (int x = 1; x < nFieldWidth - 1; x++) { if (field[static_cast<size_t>(y) * nFieldWidth + x] == 8) { for (int ty = y; ty > 1; ty--) field[static_cast<size_t>(ty) * nFieldWidth + x] = field[(static_cast<std::vector<int, std::allocator<int>>::size_type>(ty) - 1) * nFieldWidth + x]; // 清空第1行内部单元格,保持墙体不变 field[static_cast<unsigned long long>(1) * nFieldWidth + static_cast<std::vector<int, std::allocator<int>>::size_type>(x)] = 0; } } } // 生成新方块 currentPiece = piece_dist(rng); currentRotation = 0; currentX = nFieldWidth / 2 - 2; currentY = 1; if (!DoesPieceFit(currentPiece, currentRotation, currentX, currentY)) gameOver = true; } } // 构造显示数组:先拷贝固定方块,再覆盖当前活动块 vector<int> display = field; for (int px = 0; px < 4; px++) { for (int py = 0; py < 4; py++) { // 修正此处,由无限循环改为判断 4 行 if (tetromino[currentPiece][Rotate(px, py, currentRotation)] == L'X') { int dx = currentX + px; int dy = currentY + py; if (dx >= 0 && dx < nFieldWidth && dy >= 0 && dy < nFieldHeight) display[static_cast<std::vector<int, std::allocator<int>>::size_type>(dy) * nFieldWidth + dx] = currentPiece + 1; } } } // 获取控制台缓冲区信息以居中游戏区域 CONSOLE_SCREEN_BUFFER_INFO csbi; GetConsoleScreenBufferInfo(hConsole, &csbi); int consoleWidth = csbi.dwSize.X; int consoleHeight = csbi.dwSize.Y; int offsetX = (consoleWidth - nFieldWidth) / 2; int offsetY = (consoleHeight - nFieldHeight) / 2; for (int y = 0; y < nFieldHeight; y++) { COORD linePos = { static_cast<SHORT>(offsetX), static_cast<SHORT>(offsetY + y) }; SetConsoleCursorPosition(hConsole, linePos); for (int x = 0; x < nFieldWidth; x++) { int value = display[static_cast<std::vector<int, std::allocator<int>>::size_type>(y) * nFieldWidth + x]; if (value == 0) { SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); wcout << L' '; } else if (value == 9) { SetConsoleTextAttribute(hConsole, wallColor); wcout << L'#'; } else { SetConsoleTextAttribute(hConsole, pieceColors[value]); wcout << L'O'; } } } // 输出分数信息 wstringstream scoreStream; scoreStream << L"Score: " << score << L"\n"; COORD scorePos = { static_cast<SHORT>(offsetX), static_cast<SHORT>(offsetY + nFieldHeight) }; SetConsoleCursorPosition(hConsole, scorePos); SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE); wcout << scoreStream.str(); } // 游戏结束,显示结束信息 CONSOLE_SCREEN_BUFFER_INFO csbiFinal; GetConsoleScreenBufferInfo(hConsole, &csbiFinal); int consoleWidthFinal = csbiFinal.dwSize.X; int consoleHeightFinal = csbiFinal.dwSize.Y; int offsetXFinal = (consoleWidthFinal - nFieldWidth) / 2; int offsetYFinal = (consoleHeightFinal - nFieldHeight) / 2; COORD cursorPos = { static_cast<SHORT>(offsetXFinal), static_cast<SHORT>(offsetYFinal) }; SetConsoleCursorPosition(hConsole, cursorPos); wstringstream gameOverStream; gameOverStream << L"Game Over!! Score: " << score << L"\n"; wcout << gameOverStream.str(); cout << "Press any key to return to menu..."; (void)_getch(); } int main() { HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleOutputCP(CP_UTF8); _setmode(_fileno(stdout), _O_U16TEXT); CONSOLE_CURSOR_INFO cursorInfo; GetConsoleCursorInfo(hConsole, &cursorInfo); cursorInfo.bVisible = false; SetConsoleCursorInfo(hConsole, &cursorInfo); while (true) { int choice = ShowMenu(hConsole); if (choice == 1) { RunGame(hConsole); } else if (choice == 2) { ShowInstructions(hConsole); } else if (choice == 3) { break; } } return 0; }