| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
共有 86 人关注过本帖
标题:用VS2022C++控制台应用写的一个简单的俄罗斯方块游戏,带计分功能
只看楼主 加入收藏
ly1599285982
Rank: 1
等 级:新手上路
帖 子:2
专家分:0
注 册:2014-11-13
结帖率:0
收藏
 问题点数:0 回复次数:1 
用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;
}
搜索更多相关主题的帖子: for 游戏 include int case 
前天 19:59
apull
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
来 自:三体星系
等 级:版主
威 望:216
帖 子:1519
专家分:9297
注 册:2010-3-16
收藏
得分:0 
很不错哦

昨晚 23:44
快速回复:用VS2022C++控制台应用写的一个简单的俄罗斯方块游戏,带计分功能
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.023771 second(s), 12 queries.
Copyright©2004-2025, BC-CN.NET, All Rights Reserved