使用Qt框架和C++语言实现俄罗斯方块小游戏(大一C++实训)

一、说明

1. 本人大一,能力有限,时间紧迫,边学边做。

2.代码基本手搓,过程中使用AI简化优化代码或提供思路,俄罗斯方块基本功能可以实现。

3.代码边修边写,注释比较详细,本文不做过多解释,自行阅读代码理解。

4.若对Qt知识不太了解可自行查阅相关资料。

二、详细代码

Tetris.pro

QT       += core gui
QT       += core sql
QT       += multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    block.cpp \
    main.cpp \
    mainwindow.cpp \
    startwindow.cpp

HEADERS += \
    block.h \
    mainwindow.h \
    startwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES +=

block.h

#ifndef CLOCK_H
#define CLOCK_H
#include <QPainter>
#include <QPoint>
#include <QKeyEvent>
#include <QTimer>
#include <QObject>
#include <QRandomGenerator>
#include <QDateTime>

typedef  QVector<QPoint> povector;

class Block: public QObject
{
    Q_OBJECT;
public:
    int type;   //方块类型
    povector myPoint;  //点集
    int fallSpeed;   //下落速度
    QTimer * fallTimer;   //计时器
    bool isBottom;  //是否到底
    QColor color;   //添加颜色
    povector allPoint1;  //记录已经存在的点

public:
    Block(){}
    Block(int type, povector& allPoints,int score);

    ~Block()
    {
        delete fallTimer;    // 释放定时器内存
    }

    //创建方块
    void createBlock();

    //画出方块
    void drawBlock(QPainter &painter,int x,int y);

    //旋转方块
    void rotateBlock();

    //自动下落
    void autoFallBlock();

    //左移
    void moveLeft();

    //右移
    void moveRight();

    //是否在底部
    bool isAtBottom();

    // 旋转碰撞检测
    bool checkCollision4(povector& points);

    //设置下落速度
    int setSpeed(int score);
};

#endif

block.cpp

#include "block.h"

Block::Block(int type, povector& allPoints,int score)
{
    //初始化方块类型
    this->type = type;

    //点集实时转移
    allPoint1=allPoints;

    //初始不在底部
    isBottom=false;

    // 初始化定时器
    fallTimer = new QTimer(this);
    QObject::connect(fallTimer, &QTimer::timeout, this, &Block::autoFallBlock);
    fallSpeed = setSpeed(score);     // 设置下落速度,单位为毫秒
    fallTimer->start(fallSpeed);   // 启动定时器
}

//创建不同类型的方块
void Block::createBlock()
{
    switch(Block::type)
    {
       //T型
    case 1 :
    {
        //这里的坐标顺序与后面操作有关
        myPoint = {QPoint(0, 0), QPoint(1, 0), QPoint(2, 0), QPoint(1, 1)};
        color = QColor(255, 128, 128);   //浅红色
        break;
    }

        //O型
    case 2 :
    {
        myPoint = {QPoint(0, 0), QPoint(1, 0), QPoint(0, 1), QPoint(1, 1)};
        color = QColor(255, 255, 150);   //浅黄色
        break;
    }

        //S型
    case 3 :
    {
        myPoint = {QPoint(0, 1), QPoint(1, 0), QPoint(1, 1), QPoint(2, 0)};
        color = QColor(173, 255, 195);   //浅绿色
        break;
    }

        //Z型
    case 4 :
    {
        myPoint = {QPoint(0, 0), QPoint(1, 0), QPoint(1, 1), QPoint(2, 1)};
        color = QColor(173, 216, 230);   //浅蓝色
        break;
    }

        //L型
    case 5 :
    {
        myPoint = {QPoint(0, 0), QPoint(0, 1), QPoint(0, 2), QPoint(1, 2)};
        color = QColor(175, 238, 238);    //浅青色
        break;
    }

        //J型
    case 6 :
    {
        myPoint = {QPoint(1, 0), QPoint(1, 1), QPoint(1, 2), QPoint(0, 2)};
        color = QColor(186, 147, 211);   //浅紫色
        break;
    }

        //I型
    case 7 :
    {
        myPoint = {QPoint(0, 0), QPoint(0, 1), QPoint(0, 2), QPoint(0, 3)};
        color = QColor(Qt::lightGray);    //浅灰色
        break;
    }

    default :
    {
        break;
    }
    }
}

//画出方块
void Block::drawBlock(QPainter &painter,int x,int y)
{
    //历遍一个俄罗斯方块的所有点绘图
    for (int i = 0; i < myPoint.size(); i++)
    {
        QPoint point = myPoint[i];
        painter.drawRect(QRect(x+point.x()*30, y+point.y()*30, 30, 30));
    }
}

//左移方块
void Block::moveLeft()
{
    bool valid = true;   //检测移动是否合法

    for (QPoint &point : myPoint)
    {
        if (point.x() == -4)     //为了使方块从中间下落,初始加了120像素,水平方向是12*30,这里从而是-4
        {
            valid = false;
            break;
        }
    }

    if (valid)      //合法才能移动方块,下同
    {
        for (QPoint &point : myPoint)
        {
            point.setX(point.x() - 1);
        }
    }
}

//右移方块
void Block::moveRight()
{
    bool valid = true;

    for (QPoint &point : myPoint)
    {
        if (point.x() == 7)    //为了使方块从中间下落,初始加了120像素,水平方向是12*30,这里从而调整
        {
            valid = false;
            break;
        }
    }

    if (valid)
    {
        for (QPoint &point : myPoint)
        {
            point.setX(point.x() + 1);
        }
    }
}

//旋转方块具体操作
void Block::rotateBlock()
{
    int centerX,centerY,relativeX,relativeY,newX,newY;
    bool valid;          //检测是否可以旋转

    // 保存当前方块的位置
    povector originalPoints = myPoint;
    // 保存旋转后的点集
    povector rotatedPoints = myPoint;

    if (type == 1|| type == 3 || type == 4 || type == 5 || type == 6)  // L, J, T ,Z,S型旋转方式
    {
        centerX = myPoint[1].x();  // 找到方块的中心点
        centerY = myPoint[1].y();

        // 计算旋转后的新坐标
        for (QPoint &point : rotatedPoints)
        {
            // 计算相对于中心点的坐标
            relativeX = point.x() - centerX;
            relativeY = point.y() - centerY;

            // 旋转变换
            if(type!=3)
            {
                newX = relativeY;
                newY = -relativeX;
            }
            else  //S型
            {
                newX = -relativeY;
                newY = relativeX;
            }

            // 更新点的坐标
            point.setX(newX + centerX);
            point.setY(newY + centerY);
        }

        // 检查旋转后的方块是否合法
        valid = true;
        for (QPoint &point : rotatedPoints)
        {
            //为了使方块从中间下落,初始加了120像素,水平方向是12*30,这里从而调整
            if (point.x() < -4 || point.x() > 7 || point.y() > 23 ||checkCollision4(rotatedPoints))
            {
                valid = false;
                break;
            }
        }

        // 如果不合法,恢复到原来的位置
        if (!valid)
        {
            myPoint = originalPoints;
        }
        else
        {
            myPoint = rotatedPoints;
        }
    }
    else if (type == 7)    //I型旋转方式
    {
        centerX = myPoint[2].x();
        centerY = myPoint[2].y();

        // 计算旋转后的新坐标
        for (QPoint &point : rotatedPoints)
        {
            // 计算相对于中心点的坐标
            relativeX = point.x() - centerX;
            relativeY = point.y() - centerY;

            // 旋转变换
            newX = relativeY;
            newY = relativeX;

            // 更新点的坐标
            point.setX(newX + centerX);
            point.setY(newY + centerY);
        }

        // 检查旋转后的方块是否合法
        valid = true;
        for (QPoint &point : rotatedPoints)
        {
            //为了使方块从中间下落,初始加了120像素,水平方向是12*30,这里从而调整
            if (point.x() < -4 || point.x() > 7 || point.y() > 23 ||  checkCollision4(rotatedPoints))
            {
                valid = false;
                break;
            }
        }

        // 如果不合法,恢复到原来的位置
        if (!valid)
        {
            myPoint = originalPoints;
        }
        else
        {
            myPoint = rotatedPoints;
        }
    }
    else  // O 型不需要旋转
    {
        ;
    }
}

//自动下落方块
void Block::autoFallBlock()
{
    bool valid = true;
    for (QPoint &point : myPoint)
    {
        if (point.y() == 23)
        {
            valid = false;
            break;
        }
    }

    //根据下落是否合法判断是否可以继续下落
    if (valid)
    {
        for (QPoint &point : myPoint)
        {
            point.setY(point.y() + 1);
        }
        isBottom=false;
    }
    else
    {
        isBottom=true;
    }
}

//判断方块是否到达底部
bool Block::isAtBottom()
{
    return isBottom;
}

// 旋转碰撞检测
bool Block::checkCollision4(povector& points)
{
    for (QPoint& pointInFallingBlock : points)
    {
        for (QPoint& pointInAllPoints : allPoint1)
        {
            if (pointInFallingBlock == pointInAllPoints)
            {
                return true;  // 发现碰撞
            }
        }
    }
    return false;  // 没有碰撞
}

//设置速度等级
int Block::setSpeed(int score)
{
    //根据分数设置自动下落速度
    if (score <= 100)
    {
        return 1000;
    }
    else if (score <= 200 && score >100)
    {
        return 750;
    }
    else if (score <= 500 && score >200)
    {
        return 500;
    }
    else
    {
        return 250;
    }
}




mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPaintEvent>
#include <QPainter>
#include <vector>
#include <QRandomGenerator>
#include <QDateTime>
#include <QMessageBox>
#include <QPushButton>
#include <QTextStream>
#include "block.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    //调用KeyPressEvent函数
    void keyPressEvent(QKeyEvent *event) override;

    //生成新方块
    void generateNewBlock();

    //下移碰撞检测
    bool checkCollision1();

    //左移碰撞检测
    bool checkCollision2();

    //右移碰撞检测
    bool checkCollision3();

    //检查满行
    void checkFullRows();

    //删除满行
    void removeFullRows();

    //检查游戏结束
    bool checkGameOver();

    //暂停游戏
    void pauseGame();

    //保存分数
    void saveScore(int score);

    //消除最下面一行
    void removeBottomRow();

protected:
    //画出方块
    void paintEvent(QPaintEvent *event) override;

private:
    Ui::MainWindow *ui;

public:
    std::vector<Block*> currentBlocks; // 前活动的方块的容器
    povector allPoint2;    //记录所有存在的点
    int score;     //分数统计
    int tempScore;  //临时分数
    bool isPaused; // 游戏是否暂停
    std::vector<int> fullRows; //存储所有已检测到的满行
    int specialAbility;   //特殊能力
    bool gameSaved;
};
#endif


mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //初始化得分
    score=0;
    tempScore=0;

    // 初始化游戏状态
    isPaused = false;
    gameSaved =false;
    specialAbility=0;

    //生成第一个方块

    // 创建随机数生成器
    QRandomGenerator generator;
    // 使用当前时间毫秒值作为种子
    generator.seed(static_cast<quint64>(QDateTime::currentMSecsSinceEpoch()));
    // 生成范围内的随机数
    int randomValue = generator.bounded(1,8);

    currentBlocks.push_back(new Block(randomValue, allPoint2,score));
    currentBlocks.back()->createBlock();
}

MainWindow::~MainWindow()
{
    delete ui;
    for (auto block : currentBlocks)
    {
        delete block;
    }
}

void MainWindow::paintEvent(QPaintEvent * event)
{
    Q_UNUSED(event);

    QPainter painter(this);

    painter.setBrush(QColor(224, 234, 244));
    painter.drawRect(QRect(0,0, 360, 120));

    //画网格线
    painter.setPen(Qt::lightGray);
    for(int i=150;i<720;i+=30)
    {
        painter.drawLine(0, i, 360, i);
    }
    for(int j=0;j<720;j+=30)
    {
        painter.drawLine(j, 120, j, 720);
    }

    //画分界线
    painter.setPen(Qt::black);
    painter.drawLine(0, 120, 360, 120);

    //画上界面
    painter.setFont(QFont("微软雅黑", 45));
    painter.drawText(100,80,"下");
    painter.drawText(190,80,"落");
    painter.drawText(280,80,"区");

    //绘制得分和特殊能力值
    painter.setPen(Qt::black);
    painter.setFont(QFont("微软雅黑", 14));
    QString scoreText = "分数:" + QString::number(score);
    QString specialAbilityText = "能力:" + QString::number(specialAbility);
    painter.drawText(1, 40, scoreText);
    painter.drawText(1, 90, specialAbilityText);

    //画方块
    for (auto block : currentBlocks)
    {
        //设置颜色
        painter.setBrush(block->color);

        //为了使方块从中间下落,初始加120像素
        block->drawBlock(painter, 120, 0);
    }

    //检查方块是否到达底层或碰撞停止
    if (checkCollision1()||currentBlocks.back()->isAtBottom())
    {
        //停止下落计时器
        currentBlocks.back()->fallTimer->stop();

        bool gameOver=checkGameOver();

        //只有正在下落的方块停止且判断游戏是否结束后才填加它此时的点进allPoint2
        allPoint2.append(currentBlocks.back()->myPoint);

        //检查满行
        checkFullRows();

        //删除满行
        removeFullRows();

        //如果游戏结束,弹出提示框
        if (gameOver)
        {
            //结束画笔
            painter.end();

            //游戏正常结束时保存分数
            saveScore(score);

            QMessageBox::information(this, "游戏结束", "游戏结束!你的得分是:" + QString::number(score));

            // 关闭游戏窗口
            close();
        }
        //生成新的方块
        generateNewBlock();
    }
    update();   //重新绘制窗口
}

//按键处理
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    //按空格键可以暂停游戏
    if (event->key() == Qt::Key_Space)
    {
        isPaused = true;
        if (isPaused)
        {
            // 如果游戏暂停,停止所有方块的计时器
            for (auto block : currentBlocks)
            {
                block->fallTimer->stop();
            }
        }

        //调用暂停游戏的函数
        pauseGame();
    }

    //在允许情况下按左移键向左移动方块
    else if (!checkCollision2()&&event->key() == Qt::Key_Left)
    {
        currentBlocks.back()->moveLeft();
    }

    //在允许情况下按右移键向右移动方块
    else if (!checkCollision3()&&event->key() == Qt::Key_Right)
    {
        currentBlocks.back()->moveRight();
    }

    //按向下键加速下落
    else if (event->key() == Qt::Key_Down)
    {
        currentBlocks.back()->fallSpeed = 100; // 加速下落
        currentBlocks.back()->fallTimer->start(currentBlocks.back()->fallSpeed);   //重启定时器
    }

    //按向上键旋转方块
    else if (event->key() == Qt::Key_Up)
    {
        //旋转功能在Block中,其中包含了判断是否可以旋转的函数
        currentBlocks.back()->rotateBlock();
    }

    //能力够的话按换行键会消除最下面一行,但不会得分
    else if (specialAbility>0 && (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter))
    {
        tempScore-=100;   //使用能力会变动临时分数
        currentBlocks.back()->fallTimer->stop();   //停止定时器
        removeBottomRow();   // 消去最下面的一行
        currentBlocks.back()->fallTimer->start(currentBlocks.back()->fallSpeed);   //重启定时器
    }
    else    // 其他按键按了没用
    {
        ;
    }

    //重新绘制窗口
    update();
}

//生成新方块
void MainWindow::generateNewBlock()
{
    // 创建随机数生成器
    QRandomGenerator generator;
    generator.seed(static_cast<quint64>(QDateTime::currentMSecsSinceEpoch()));

    // 生成新的方块类型
    int randomValue = generator.bounded(1, 8);

    // 初始化新的方块并将其添加到列表
    currentBlocks.push_back(new Block(randomValue, allPoint2,score));
    currentBlocks.back()->createBlock();

    currentBlocks.back()->fallTimer->start(currentBlocks.back()->fallSpeed);   //重启定时器
}

//下落判断
bool MainWindow::checkCollision1()
{
    //把下落方块的点和已有方块的点逐个对比,下同
    Block* fallingBlock = currentBlocks.back();
    for (QPoint& pointInFallingBlock : fallingBlock->myPoint)
    {
        for (QPoint& pointInAllPoints : allPoint2)
        {
            if (pointInFallingBlock.x() == pointInAllPoints.x()&&pointInFallingBlock.y()+1 == pointInAllPoints.y())
            {
                return true; //发现碰撞
            }
        }
    }
    return false; //没有碰撞
}

//左移判断
bool MainWindow::checkCollision2()
{
    Block* fallingBlock = currentBlocks.back();
    for (QPoint& pointInFallingBlock : fallingBlock->myPoint)
    {
        for (QPoint& pointInAllPoints : allPoint2)
        {
            if (pointInFallingBlock.x()-1 == pointInAllPoints.x()&&pointInFallingBlock.y() == pointInAllPoints.y())
            {
                return true;   //发现碰撞
            }
        }
    }
    return false;   //没有碰撞
}

//右移判断
bool MainWindow::checkCollision3()
{
    Block* fallingBlock = currentBlocks.back();
    for (QPoint& pointInFallingBlock : fallingBlock->myPoint)
    {
        for (QPoint& pointInAllPoints : allPoint2)
        {
            if (pointInFallingBlock.x()+1 == pointInAllPoints.x()&&pointInFallingBlock.y() == pointInAllPoints.y())
            {
                return true; //发现碰撞
            }
        }
    }
    return false;   //没有碰撞
}

//检查满行
void MainWindow::checkFullRows()
{
    // 从最后一行开始向前遍历,直到第四行(前四行非游戏区域)
    for (int row = 23; row >=4; row--)
    {
        bool isFull = true;     //假设当前行是满的

        //遍历当前行的每一列(索引从-4开始,因为初始加了120像素,为了使方块从中间下落)
        for (int col = -4; col < 8; col++)
        {
            bool hasBlock = false;      //标记当前列是否有方块,假设没有
            // 遍历所有方块点,检查是否有方块在当前行和列上
            for (auto& point : allPoint2)
            {
                if (point.x() == col && point.y() == row)
                {
                    hasBlock = true;     //发现方块
                    break;
                }
            }
            if (!hasBlock)     //如果当前列没有方块,则当前行不是满行
            {
                isFull = false;
                break;
            }
        }

        if (isFull)
        {
            fullRows.push_back(row);   //记录满行
        }
    }
}

//删除满行
void MainWindow::removeFullRows()
{
    // 消除所有记录的满行
    while (!fullRows.empty())
    {
        int row = fullRows.back();
        fullRows.pop_back(); // 从列表中移除最后一个元素

        //更新分数和能力
        score += 10; // 每消除一行得10分
        tempScore+=10;
        specialAbility=(tempScore/100);

        // 更新方块位置
        for (Block* block : currentBlocks)
        {
            for (QPoint& point : block->myPoint)
            {
                if (point.y() == row)
                {
                    point = QPoint(-100, -100); //标记为无效点
                }
                else if (point.y() < row)
                {
                    point.setY(point.y() + 1);   //将上面的所有点的纵坐标加1
                }
                else{}
            }
        }

        //更新allPoint2中的点
        for (auto it = allPoint2.begin(); it != allPoint2.end();)
        {
            if (it->y() == row)
            {
                it = allPoint2.erase(it);    //删除当前行的所有点,此时会自动迭代
            }
            else if (it->y() < row)
            {
                it->setY(it->y() + 1);   //将上面的所有点的纵坐标加1
                ++it;    //手动增加迭代器以继续检查下一个点
            }
            else
            {
                ++it;
            }
        }
        update(); // 重新绘制窗口
    }
}

//检查游戏结束
bool MainWindow::checkGameOver()
{
    // 判断游戏是否结束
    for (auto& point : allPoint2)
    {
        if (point.y() <= 3)
        {
            return true;
        }
    }
    return false;
}

//暂停游戏
void MainWindow::pauseGame()
{
    if (isPaused)
    {
        QMessageBox msgBox;
        msgBox.setWindowTitle("游戏暂停");
        msgBox.setText("请选择");

        // 创建自定义按钮
        QPushButton *continueButton = msgBox.addButton("继续", QMessageBox::RejectRole);   //方便叉掉时的信号管理,自动继续
        QPushButton *resetButton = msgBox.addButton("重置", QMessageBox::AcceptRole);

        msgBox.exec();

        // 检查哪个按钮被点击
        if (msgBox.clickedButton() == continueButton)
        {
            // 继续游戏
            isPaused = false;

            currentBlocks.back()->fallTimer->start(currentBlocks.back()->fallSpeed);   //重启定时器
        }
        else if (msgBox.clickedButton() == resetButton)
        {
            // 重置游戏
            score = 0;       //清空得分
            tempScore = 0;
            specialAbility=(tempScore/100);
            allPoint2.clear();          //删点

            for (auto block : currentBlocks)     //删方块
            {
                delete block;
            }
            currentBlocks.clear();

            generateNewBlock();        //重新下落方块
        }
        else
        {
            ;
        }
    }
}

//使用能力删除底行
void MainWindow::removeBottomRow()
{
    // 更新方块位置
    for (Block* block : currentBlocks)
    {
        for (QPoint& point : block->myPoint)
        {
            if (point.y() == 23)
            {
                point = QPoint(-100, -100); //标记为无效点
            }
            else if (point.y() < 23)
            {
                point.setY(point.y() + 1);   //将上面的所有点的纵坐标加1
            }
            else{}
        }
    }

    //更新allPoint2中的点
    for (auto it = allPoint2.begin(); it != allPoint2.end();)
    {
        if (it->y() == 23)
        {
            it = allPoint2.erase(it);    //删除当前行的所有点
        }
        else if (it->y() < 23)
        {
            it->setY(it->y() + 1);   //将上面的所有点的纵坐标加1
            ++it;    //手动增加迭代器以继续检查下一个点
        }
        else
        {
            ++it;
        }
    }

    //更新能力
    specialAbility=(tempScore/100);
}

//游戏结束时保存分数
void MainWindow::saveScore(int score)
{
    if (gameSaved)
    {
        return; //已经保存过,直接返回,避免重复保存
    }

    QFile file("scores.txt");
    if (file.open(QIODevice::Append | QIODevice::Text))
    {
        QTextStream out(&file);
        out << score << "\n";    //将分数追加到文件
        file.close();
    }

    gameSaved = true;   //表示已保存过分数
}


startwindow.h

#ifndef STARTWINDOW_H
#define STARTWINDOW_H
#include <QWidget>
#include <QPushButton>
#include <QFile>
#include <QTextStream>
#include <QMessageBox>
#include <QVector>
#include <QPixmap>
#include <QImage>
#include <QMediaPlayer>
#include <QMediaPlaylist>
#include "mainwindow.h"
#include "block.h"

class StartWindow : public QWidget
{
    Q_OBJECT;

public:
    StartWindow(QWidget *parent = nullptr);

public slots:

    //新游戏按钮的点击处理函数
    void newGameButtonClicked();

    //历史排行按钮的点击处理函数
    void historyButtonClicked();

    //清除历史排行按钮的点击处理函数
    void clearHistoryButtonClicked();

    //游戏帮助按钮的点击处理函数
    void gameHelpButtonClicked();

    //音乐播放设置按钮的点击处理函数
    void musicPlayerButtonClicked();

public:
    QMediaPlayer *player;  //定义音乐播放器

};
#endif // STARTWINDOW_H

startwindow.cpp

#include "startwindow.h"
#include <QDialog>
#include <QVBoxLayout>
#include <QLabel>
#include <QSlider>

StartWindow::StartWindow(QWidget *parent) : QWidget(parent)
{
    //音乐播放
    player = new QMediaPlayer(this);    //设置播放器
    QMediaPlaylist *playlist = new QMediaPlaylist(player);   //设置播放列表
    playlist->addMedia(QUrl::fromLocalFile("1.mp3"));    //添加音乐
    playlist->setPlaybackMode(QMediaPlaylist::Loop);     //设置为循环播放
    player->setPlaylist(playlist);     //传播放列表
    player->play();    //启动播放器


//游戏帮助按钮:
    //游戏帮助按钮
    QPushButton *gameHelpButton = new QPushButton("游戏帮助", this);
    gameHelpButton->setGeometry(400, 20, 180, 85);

    //调整背景属性,下同
    gameHelpButton->setStyleSheet("background-color: lightGray; color: black; font-size: 30px; font-family: Arial;");

    // 连接游戏帮助按钮的点击信号到槽函数
    connect(gameHelpButton, &QPushButton::clicked, this, &StartWindow::gameHelpButtonClicked);

//新游戏按钮:
    // 创建新游戏按钮
    QPushButton *newGameButton = new QPushButton("新游戏", this);
    newGameButton->setGeometry(400, 140, 180, 85);

    newGameButton->setStyleSheet("background-color: lightGray; color: black; font-size: 30px; font-family: Arial;");

    // 连接新游戏按钮的点击信号到槽函数
    connect(newGameButton, &QPushButton::clicked, this, &StartWindow::newGameButtonClicked);


//历史排行按钮:
    // 创建历史排行按钮
    QPushButton *historyButton = new QPushButton("历史排行", this);
    historyButton->setGeometry(400, 260, 180, 85);

    historyButton->setStyleSheet("background-color: lightGray; color: black; font-size: 30px; font-family: Arial;");

    // 连接历史排行按钮的点击信号到槽函数
    connect(historyButton, &QPushButton::clicked, this, &StartWindow::historyButtonClicked);


//清除历史排行按钮:
    // 创建清除历史排行按钮
    QPushButton *clearHistoryButton = new QPushButton("清除历史排行", this);
    clearHistoryButton->setGeometry(400, 380, 180, 85);

    clearHistoryButton->setStyleSheet("background-color: lightGray; color: black; font-size: 30px; font-family: Arial;");

    // 连接清除历史排行按钮的点击信号到槽函数
    connect(clearHistoryButton, &QPushButton::clicked, this, &StartWindow::clearHistoryButtonClicked);


//音乐播放设置按钮:
    // 创建音乐播放设置按钮
    QPushButton *musicPlayerButton = new QPushButton("音乐播放设置", this);
    musicPlayerButton->setGeometry(400, 500, 180, 85);

    musicPlayerButton->setStyleSheet("background-color: lightGray; color: black; font-size: 30px; font-family: Arial;");

    // 连接音乐播放设置按钮的点击信号到槽函数
    connect(musicPlayerButton, &QPushButton::clicked, this, &StartWindow::musicPlayerButtonClicked);
}

//游戏帮助按钮的点击处理函数
void StartWindow::gameHelpButtonClicked()
{
    QString text="1.点击新游戏可以开始进行一局新的俄罗斯方块游戏\n"
                 "2.点击历史排行可以看见依此最高的10次历史分数\n"
                 "3.点击清除历史排行会删除历史排行数据\n"
                 "4.点击音乐播放设置可以进行音量大小的调节\n"
                 "5.游戏中,可按左右移动键控制方块的左右移动\n"
                 "6.游戏中,可按向下键,加速方块向下移动\n"
                 "7.游戏中,可按向上键,使方块旋转\n"
                 "8.游戏中,按空格键可以暂停游戏,之后可以选择是继续还是重置\n"
                 "9.游戏中,每消一满行可获得10分,每100分可以获得1能力点\n"
                 "10.游戏中,如果有能力点,可以按空格键消去最后一行,但不会获得分数,能力点-1\n"
                 "11.游戏中,分数到达100、200、500时,自然下落速度会提升\n"
                 "12.游戏中,当有方块超出游戏区域进入下落区域时,会结束游戏";

    QMessageBox::information(this, "游戏帮助", text);
}

//新游戏按钮的点击处理函数
void StartWindow::newGameButtonClicked()
{
    this->hide();
    MainWindow* w = new MainWindow();  // 使用指针创建对象
    w->setWindowTitle("俄罗斯方块");
    w->setFixedSize(360, 720);   //设置游戏界面大小
    w->show();
}

//历史排行按钮的点击处理函数
void StartWindow::historyButtonClicked()
{
    QFile file("scores.txt");
    QVector<int> scores;
    bool ok;

    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&file);
        int score;

        // 读取文件中的所有分数
        while (!in.atEnd())
        {
            QString line = in.readLine().trimmed();   //读取一行文本,删去空白字符
            // 尝试将行转换为整数,并用bool指针检查转换是否成功
            score = line.toInt(&ok);
            if (!line.isEmpty() && ok)
            {
                scores.append(score);
            }
        }

        // 对分数进行排序(降序)
        std::sort(scores.begin(), scores.end(), std::greater<int>());

        // 构建只包含前十名的字符串
        QString scoreText = "历史分数(前十位):\n";
        for (int i = 0; i < std::min(scores.size(), 10); ++i)
        {
            scoreText += "Top"+QString::number(i+1)+":    "+QString::number(scores[i]) + "\n";
        }

        // 显示对话框
        QMessageBox::information(this, "历史排行", scoreText);

        file.close();
    }
    else
    {
        QMessageBox::information(this, "历史排行", "没有历史分数记录。");
    }
}

//清除历史排行按钮的点击处理函数
void StartWindow::clearHistoryButtonClicked()
{
    QFile file("scores.txt");
    if (file.open(QIODevice::WriteOnly | QIODevice::Text))
    {
        // 不写入任何内容,这样文件就被清空了
        file.close();
    }

    QMessageBox::information(this, "清除历史排行", "已清除");
}

void StartWindow::musicPlayerButtonClicked()
{
    QDialog *musicPlayerSettingsDialog = new QDialog(this);     //创建新窗口,一个对话框
    musicPlayerSettingsDialog->setWindowTitle("音量");

    QVBoxLayout *layout = new QVBoxLayout(musicPlayerSettingsDialog);    //设置垂直布局管理器

    QSlider *volumeSlider = new QSlider(Qt::Horizontal, musicPlayerSettingsDialog);  //创建滑动条控件,用于调整音量
    volumeSlider->setRange(0, 100);   // 设置音量范围为0到100
    volumeSlider->setValue(static_cast<int>(player->volume() * 100)); //设置初始音量
    layout->addWidget(volumeSlider);

    // 当滑动条的值改变时,调整音量
    QObject::connect(volumeSlider, &QSlider::valueChanged, [=](int value) {//lambda表达式,会捕获value(从valueChanged信号传递过来的滑动条当前位置值)
        player->setVolume(value);
    });

    musicPlayerSettingsDialog->exec();
}




 

main.cpp

#include "mainwindow.h"
#include "startwindow.h"
#include <QApplication>
#include <QDebug>
#include <QMediaPlayer>
#include <QMediaPlaylist>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    StartWindow startWindow;

    //设置主界面大小
    startWindow.setFixedSize(990,600);
    startWindow.setWindowTitle("俄罗斯方块");

    // 加载图片文件
    QPixmap background("1.jpg");

    //创建一个新的调色板并设置背景画刷
    QPalette palette;
    palette.setBrush(QPalette::Background, background);

    //将调色板应用到窗口
    startWindow.setPalette(palette);

    startWindow.show();
    return a.exec();
}




三、提醒

1.照片和音频需自行添加,没有资源会出错,资源自行寻找。

2.添加时要注意文件夹名称,保证添加位置正确。

 四、结果展示

1.主界面

2.游戏界面 

五、未来展望 

本人实训结束后就懒得完善了,读者可以在此代码基础上进一步丰富,下面给出建议

1.本人艺术细胞缺乏,读者愿意的话美术方面可以做的更好。

2.可以使用数据库技术。比如暂停后新增一个存档按钮,点击后会保存游戏进度,主界面新增继续游戏按钮,点击后会继续上次未完成的游戏。有想法的话也可以做个登录界面。

3.玩法功能上可以创新增加新的趣味功能,需要自己动脑思考。(不妨借鉴开心消消乐)

4. 最后,我看见有同学做了多人模式,聊天室等功能,可以参考。

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值