热门标签 | HotTags
当前位置:  开发笔记 > 开发工具 > 正文

Qt实现俄罗斯方块

这篇文章主要为大家详细介绍了Qt实现俄罗斯方块,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

本文实例为大家分享了Qt实现俄罗斯方块,供大家参考,具体内容如下

最近在学习Qt,用它来进行图形界面的开发还是很方便的,想着做一个小游戏来锻炼一下自己,就想到了小时候玩的俄罗斯方块。折腾了一段时间,虽然界面做的不美观,但是总算是实现了基本的功能。

首先我写了一个俄罗斯方块的类Tetris,通过这个类来进行这个游戏的数据的处理;然后游戏窗口是继承的QWidget类,用来显示游戏的方块;“下一个方块”窗口也是继承的QWidget类,用来显示下一个方块;控制提示和分数的显示用的QLabel。然后将将这些控件整合到继承自QMainWindow的mainWindow类。运行的结果如下:

Tetris类:一共有7中不同形状的方块(如下图),每个方块由四个方格组成,Block结构体用来存储方块的方格坐、中心方格坐标、ID等数据,移动中的方块和下移个方块都是通过这个结构体来操作的;已经落下的方块的方格存储在二维数组box[][]中,x坐标从左到右为正方向,y坐标从上到下为正方向(如下图)。

项目源文件

  • tetris.h
  • tetris.cpp
  • tetrisbox.h
  • tetrisbox.cpp
  • nexttetris.h
  • nexttetris.cpp
  • mainwindow.h
  • mainwindow.cpp

tetris.h

#ifndef TETRIS_H
#define TETRIS_H
 
//为了获得随机数
#include 
#include 
 
#define MAXX 10     //显示窗口的横向格数
#define MAXY 20     //显示窗口的竖向格数
#define NEXTMAXX 6  //“下一个”显示窗口的横向格数
#define NEXTMAXY 6  //“下一个”显示窗口的竖向格数
#define WIDTH 30    //单格的宽度
#define HEIGHT 30   //单格的高度
#define INTERVAL 4  //单格之间的间隔
#define COUNT 4     //每个方块的格数
 
//Block结构体:一个方块
struct Block
{
    int x[COUNT];   //方块单格的x坐标
    int y[COUNT];   //方块单格的y坐标
    int centerX;    //方块的中心x坐标
    int centerY;    //方块的中心y坐标
    int ID;         //方块的ID
};
 
 
class Tetris
{
public:
    Tetris();
    void createBlock();             //创建当前方块
    Block getNextBlock();           //获得下一个方块
    Block getBlock();               //获得当前方块
    int getScore();                 //获得分数
    int getBox(int x, int y);       //获得相应坐标的状态
    bool rotate();                  //旋转
    bool moveToLeft();              //向左移动
    bool moveToRight();             //向右移动
    bool moveToBottom();            //向下移动
    bool isEnd();                   //判断是否结束
    void killLines();               //消去整行
    void clear();                   //重新初始化
 
    static int getWidth();          //获得窗口的宽度
    static int getHeight();         //获得窗口的高度
    static int getNextWidth();      //获得“下一个”窗口的宽度
    static int getNextHeight();     //获得“下一个”窗口的高度
 
 
 
private:
    void createNextBlock();         //创建下一个方块
    bool move(int dx, int dy);      //是否可以移动
    void blockToBox();              //将block中的数据转移到box中
    bool isRotatable();             //是否可以旋转
    int getFirstFullLine();         //获得第一个整行
 
private:
    int score;          //分数
    Block block;        //当前方块
    Block nextBlock;    //下一个方块
    int box[MAXX][MAXY];//方格的坐标系 1表示右方格,0表示没有方格
 
 
};
 
#endif // TETRIS_H

Tetris.cpp

#include "tetris.h"
 
Tetris::Tetris()
{
    //初始化随机数发生器
    srand(unsigned(time(NULL)));
 
    //初始化成员变量
    score = 0;
    for (int i = 0; i 0; j--)
        {
            for (int i = 0; i = MAXX)
        {
            return false;
        }
        //y坐标在0 - MAXY之间就对box中的状态进行判定
        //box中为1则返回false
        if (newY[i] >=0 && newY[i] = MAXY)
        {
            return false;
        }
 
    }
    newCenterX = block.centerX + dx;
    newCenterY = block.centerY + dy;
 
    //满足条件就将新的x和y坐标赋值给block
    for (int i = 0; i = MAXX)
        {
            return false;
        }
        //y坐标在0 - MAXY 之间就对box中的状态进行判定
        //box中为1则返回false
        if (newY[i] >=0 && newY[i] = MAXY)
        {
            return false;
        }
    }
    newCenterX = block.centerX;
    newCenterY = block.centerY;
 
    //满足条件后进行block的赋值
    for (int i = 0; i = 0)
        {
            box[x][y] = 1;
        }
    }
}
 
 
//获得第一个整行的行数,并返回
int Tetris::getFirstFullLine()
{
    //这里j从1开始就好
    for (int j = 0; j 

tetrisbox.h

#ifndef TETRISBOX_H
#define TETRISBOX_H
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//为了使用Block
#include "tetris.h"
 
class TetrisBox : public QWidget
{
    Q_OBJECT
public:
    explicit TetrisBox(QWidget *parent = nullptr);
    void updateTetris(Tetris tetris);       //更新数据和视图
    void paintEvent(QPaintEvent *event);    //绘制视图
 
signals:
 
public slots:
 
private:
    Block block;            //用来储存Tetris中block的数据
    int box[MAXX][MAXY];    //用来存储Tetris中box的数据
};
 
#endif // TETRISBOX_H

tetrisbox.cpp

#include "tetrisbox.h"
 
TetrisBox::TetrisBox(QWidget *parent) : QWidget(parent)
{
    //对block初始化
    for (int i = 0; i 

nexttetrisbox.h

#ifndef NEXTTETRISBOX_H
#define NEXTTETRISBOX_H
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "tetris.h"
 
#define RESTX (MAXX - NEXTMAXX) / 2     //方块x坐标的转换常数
#define RESTY 4                         //方块y坐标的转换常数
 
class NextTetrisBox : public QWidget
{
    Q_OBJECT
public:
    explicit NextTetrisBox(QWidget *parent = nullptr);
    void updateNextTetris(Tetris tetris);   //更新“下一个”的数据和视图
    void paintEvent(QPaintEvent *event);    //绘制视图
 
signals:
 
public slots:
 
private:
    Block nextBlock;    //“下一个”方块
};
 
#endif // NEXTTETRISBOX_H

nexttetris.cpp

#include "nexttetrisbox.h"
 
NextTetrisBox::NextTetrisBox(QWidget *parent) : QWidget(parent)
{
 
    //初始化nextBlock
    for (int i = 0; i 

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "tetris.h"
#include "tetrisbox.h"
#include "nexttetrisbox.h"
 
//游戏的状态
#define STATUS_ON 0     //游戏正常进行
#define STATUS_PAUSE 1  //游戏暂停
#define STATUS_OFF 2    //游戏未开始
#define STATUS_END 3    //游戏结束
 
class MainWindow : public QMainWindow
{
    Q_OBJECT
 
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void keyPressEvent(QKeyEvent *event);   //响应键盘事件
    void changeEvent(QEvent *event);        //窗口最小化后暂停
    void updateScore();                     //更新分数的数据和显示
 
public slots:
    void onTimer();
 
private:
    int status;                     //游戏状态
    Tetris tetris;                  //俄罗斯方块类对象
    QTimer *timer;                  //计时器
    TetrisBox *tetrisBox;           //游戏窗口
    NextTetrisBox *nextTetrisBox;   //“下一个”窗口
    QGridLayout *mainLayout;        //mainLayout
    QLabel *nextTetrisLabel;        //“下一个”窗口的标签
    QLabel *controlLabel;           //“控制”标签
    QLabel *w_controlLabel;         //W键的标签
    QLabel *s_controlLabel;         //S键的标签
    QLabel *a_controlLabel;         //A键的标签
    QLabel *d_controlLabel;         //D键的标签
    QLabel *h_controlLabel;         //H键的标签
    QLabel *j_controlLabel;         //J键的标签
    QLabel *c_controlLabel;         //C键的标签
    QLabel *m_controlLabel;         //M键的标签
    QLabel *scoreTitleLabel;        //分数标题标签
    QLabel *scoreLabel;             //分数标签(用来显示分数)
 
};
 
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
 
MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    //创建对象
    tetrisBox = new TetrisBox;
    nextTetrisBox = new NextTetrisBox;
    nextTetrisLabel = new QLabel(tr("下一个:"));
    cOntrolLabel= new QLabel(tr("控制:"));
    w_cOntrolLabel= new QLabel(tr("W-旋转"));
    s_cOntrolLabel= new QLabel(tr("S-向下移动"));
    a_cOntrolLabel= new QLabel(tr("A-向左移动"));
    d_cOntrolLabel= new QLabel(tr("D-向右移动"));
    h_cOntrolLabel= new QLabel(tr("H-开始"));
    j_cOntrolLabel= new QLabel(tr("J-暂停"));
    c_cOntrolLabel= new QLabel(tr("C-重新开始"));
    m_cOntrolLabel= new QLabel(tr("M-结束游戏"));
    scoreTitleLabel = new QLabel(tr("得分:"));
    scoreLabel = new QLabel(tr("0"));
    mainLayout = new QGridLayout;
    //设置mainLayout的水平和横向的间隔为20
    mainLayout->setHorizontalSpacing(20);
    mainLayout->setVerticalSpacing(20);
    //设置mainLayout居中
    mainLayout->setAlignment(Qt::AlignCenter);
    //添加各个widget
    mainLayout->addWidget(tetrisBox, 0, 0, 14, 1);
    mainLayout->addWidget(nextTetrisLabel, 0, 1);
    mainLayout->addWidget(nextTetrisBox, 1, 1, 1, 2);
    mainLayout->addWidget(controlLabel, 5, 1);
    mainLayout->addWidget(w_controlLabel, 6, 1);
    mainLayout->addWidget(s_controlLabel, 6, 2);
    mainLayout->addWidget(a_controlLabel, 7, 1);
    mainLayout->addWidget(d_controlLabel, 7, 2);
    mainLayout->addWidget(h_controlLabel, 8, 1);
    mainLayout->addWidget(j_controlLabel, 8, 2);
    mainLayout->addWidget(c_controlLabel, 9, 1);
    mainLayout->addWidget(m_controlLabel, 9, 2);
    mainLayout->addWidget(scoreTitleLabel, 12, 1);
    mainLayout->addWidget(scoreLabel, 12, 2);
 
    //因为mainWindow已有一个layout,所以不能直接将mainLayout
    //设置到mainWindow中,需要先将mainLayout设置为一个widget的layout
    //在将widget设置为mainLayout的centralWidget
    QWidget *widget = new QWidget(this);
    widget->setLayout(mainLayout);
    setCentralWidget(widget);
 
    //设置窗口背景为灰色
    setPalette(Qt::gray);
    //设置窗口在电脑屏幕上居中
    QDesktopWidget *desktopWidget = QApplication::desktop();
    int w = (desktopWidget->width() - this->width()) / 2;
    int h = 5;
    move(w, h);
 
    //初始化
    status = STATUS_OFF;
    nextTetrisBox->updateNextTetris(tetris);
    setWindowTitle(tr("Game_Tetris - OFF"));
    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(onTimer()));
}
 
MainWindow::~MainWindow()
{
 
}
 
 
//相应键盘事件
void MainWindow::keyPressEvent(QKeyEvent *event)
{
    //W键-进行旋转并更新游戏窗口内容
    if (event->key() == Qt::Key_W)
    {
        if (tetris.rotate())
        {
            //需要游戏状态为:正常进行
            if (status == STATUS_ON)
            {
                tetrisBox->updateTetris(tetris);
            }
        }
    }
    //A键-将方块向左移动并更新游戏窗口内容
    else if (event->key() == Qt::Key_A)
    {
        //需要游戏状态为:正常进行
        if (status == STATUS_ON)
        {
            if (tetris.moveToLeft())
            {
                tetrisBox->updateTetris(tetris);
 
            }
        }
    }
    //S键-将方块向下移动并更新游戏窗口内容
    else if (event->key() == Qt::Key_S)
    {
        //需要游戏状态:正常进行
        if (status == STATUS_ON)
        {
            if (tetris.moveToBottom())
            {
                tetrisBox->updateTetris(tetris);
                nextTetrisBox->updateNextTetris(tetris);
                updateScore();
            }
            else    //游戏结束
            {
                //计时器停止
                timer->stop();
                //输出结束提示
                QString str;
                str +=  QString("Game Over!\nYour Score is: %1!").arg(tetris.getScore());
                QMessageBox::information(this, tr("Game Over"), str);
                //更改游戏状态为:游戏结束
                status = STATUS_END;
                setWindowTitle(tr("Game_Tetris - END"));
            }
        }
    }
    //D键-将方块向右移动并更新游戏窗口内容
    else if (event->key() == Qt::Key_D)
    {
        //需要游戏状态为:正常进行
        if (status == STATUS_ON)
        {
            if (tetris.moveToRight())
            {
                tetrisBox->updateTetris(tetris);
            }
        }
    }
    //H键-开始游戏
    //不同状态的相应:
    //之前状态    之后状态
    //游戏暂停 -> 正常进行
    //还未开始 -> 正常进行
    //游戏结束 -> 正常进行
    else if (event->key() == Qt::Key_H)
    {
        if (status == STATUS_PAUSE)
        {
            timer->start(500);
            status = STATUS_ON;
            setWindowTitle(tr("Game_Tetris - ON"));
        }
        else if (status == STATUS_OFF)
        {
            //初始化窗口视图
            tetris.createBlock();
            tetrisBox->updateTetris(tetris);
            nextTetrisBox->updateNextTetris(tetris);
            updateScore();
 
            status = STATUS_ON;
            setWindowTitle(tr("Game_Tetris - ON"));
            timer->start(500);
        }
        else if (status == STATUS_END)
        {
            //初始化tetris
            tetris.clear();
            tetris.createBlock();
            tetrisBox->updateTetris(tetris);
            nextTetrisBox->updateNextTetris(tetris);
            updateScore();
 
            status = STATUS_ON;
            setWindowTitle(tr("Game_Tetris - ON"));
            timer->start(500);
        }
    }
    //J键-游戏暂停
    else if (event->key() == Qt::Key_J)
    {
        //需要游戏状态为:正常进行
        if (status == STATUS_ON)
        {
            timer->stop();
            status = STATUS_PAUSE;
            setWindowTitle(tr("Game_Tetris - PAUSE"));
        }
    }
    //C键-重新开始游戏
    else if (event->key() == Qt::Key_C)
    {
        timer->stop();
        tetris.clear();
        tetrisBox->updateTetris(tetris);
        nextTetrisBox->updateNextTetris(tetris);
        updateScore();
        status = STATUS_OFF;
        setWindowTitle(tr("Game_Tetris - OFF"));
 
    }
    //M键-关闭游戏
    else if (event->key() == Qt::Key_M)
    {
        close();
    }
 
}
 
 
void MainWindow::onTimer()
{
    if(tetris.moveToBottom())
    {
        tetrisBox->updateTetris(tetris);
        nextTetrisBox->updateNextTetris(tetris);
        updateScore();
    }
    else
    {
        timer->stop();
        QString str;
        str +=  QString("Game Over!\nYour Score is: %1!").arg(tetris.getScore());
        QMessageBox::information(this, tr("Game Over"), str);
        status = STATUS_END;
        setWindowTitle(tr("Game_Tetris - END"));
    }
}
 
 
void MainWindow::updateScore()
{
    QString str;
    int score = tetris.getScore();
    str += QString("%1").arg(score);
    scoreLabel->setText(str);
}
 
 
//若窗口最小化就停止计时器
void MainWindow::changeEvent(QEvent *event)
{
    if (event->type() != QEvent::WindowStateChange)
    {
        return;
    }
    if (windowState() == Qt::WindowMinimized)
    {
        timer->stop();
    }
}

main.cpp

#include "mainwindow.h"
#include 
 
int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
 
    return a.exec();
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • Skywalking系列博客1安装单机版 Skywalking的快速安装方法
    本文介绍了如何快速安装单机版的Skywalking,包括下载、环境需求和端口检查等步骤。同时提供了百度盘下载地址和查询端口是否被占用的命令。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了在Windows 8上安装gvim中插件时出现的错误加载问题。作者将EasyMotion插件放在了正确的位置,但加载时却出现了错误。作者提供了下载链接和之前放置插件的位置,并列出了出现的错误信息。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • Win10下游戏不能全屏的解决方法及兼容游戏列表
    本文介绍了Win10下游戏不能全屏的解决方法,包括修改注册表默认值和查看兼容游戏列表。同时提供了部分已经支持Win10的热门游戏列表,帮助玩家解决游戏不能全屏的问题。 ... [详细]
  • 如何在联想win10专业版中修改账户名称
    本文介绍了在联想win10专业版中修改账户名称的方法,包括在计算机管理中找到要修改的账户,通过重命名来修改登录名和属性来修改显示名称。同时指出了windows10家庭版无法使用此方法的限制。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • Windows下配置PHP5.6的方法及注意事项
    本文介绍了在Windows系统下配置PHP5.6的步骤及注意事项,包括下载PHP5.6、解压并配置IIS、添加模块映射、测试等。同时提供了一些常见问题的解决方法,如下载缺失的msvcr110.dll文件等。通过本文的指导,读者可以轻松地在Windows系统下配置PHP5.6,并解决一些常见的配置问题。 ... [详细]
  • 电脑公司win7剪切板位置及使用方法
    本文介绍了电脑公司win7剪切板的位置和使用方法。剪切板一般位于c:\windows\system32目录,程序名为clipbrd.exe。通过在搜索栏中输入cmd打开命令提示符窗口,并输入clip /?即可调用剪贴板查看器。赶紧来试试看吧!更多精彩文章请关注本站。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
author-avatar
XsiaoHan
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有