Qt-俄罗斯方块

Posted Zetaa

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Qt-俄罗斯方块相关的知识,希望对你有一定的参考价值。

声明: 仅个人小记

整个有效项目的文件已经上传csdn: http://download.csdn.net/detail/qq_25847123/9718822

目录:
  1.前言
  2.效果展示
  3.主要代码
  4.开发日志
  5.小结


1.前言

整个程序的完成花了我不少时间, 有许多知识细节不够清楚,边学边做,断断续续完成的。之前有用C++直接做过一次俄罗斯方块,界面简陋,是在控制台运行的。这次用Qt实现,沿用了之前的总体思想,技术细节有所改动。


2.效果展示

  刚开始:
这里写图片描述
开始游戏:
这里写图片描述

这里写图片描述
背景界面随着分数进行随机的切换
这里写图片描述


3.主要代码
项目构架:
这里写图片描述
这里写图片描述

这里写图片描述

**基本单元方块类(Elem):**

#ifndef ELEM_H
#define ELEM_H
#include <QGraphicsObject>
#include <QPainter>
#include <QTime>

enum elemColor {
    red, yellow, blue, green, lightBlue, purple,gray,randomColor
};
const QColor ElemColorArray[] = {
    QColor(255,0,0,100),  QColor(255,255,0,100),QColor(0,0,255,100),
    QColor(0,255,0,100),QColor(0,255,255,100),QColor(255,0,255,100),
    QColor(150,100,100,100)
};
class Elem : public QGraphicsObject
{
public:
    Elem(QPointF pos = QPointF(0,0), int colorId = elemColor::randomColor, QGraphicsItem *parent = Q_NULLPTR);
    void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);
    QRectF boundingRect() const;
    int getCurrentStatus();
    void setCurrentStatus(int status);
private:
    int colorId;
    int currentStatus;
};
#endif // ELEM_H

**基本单元方块类Elem 实现部分:**

#include "elem.h"

Elem::Elem(QPointF pos,int colorId, QGraphicsItem *parent):QGraphicsObject(parent)
{
   this->currentStatus = 0;

   this->setPos(this->mapFromParent(pos));

   this->colorId = colorId;

   if (this->colorId == elemColor::randomColor) {
       qsrand(QTime().currentTime().second());
       this->colorId = qrand() % 7;
   }
}

void Elem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->drawPixmap(0,0,20,20,QPixmap(":/image/images/box.gif"));
    painter->setBrush(QBrush(ElemColorArray[this->colorId]));
    painter->setPen(QPen(ElemColorArray[this->colorId]));
    painter->drawRect(0,0,19,19);
}

QRectF Elem::boundingRect() const
{
    return QRectF(-0.5,-0.5,21,21);
}

int Elem::getCurrentStatus()
{
    return this->currentStatus;
}

void Elem::setCurrentStatus(int status)
{
    this->currentStatus = status;
}



**形状基类MyShape:**

#ifndef MYSHAPE_H
#define MYSHAPE_H
#include <QGraphicsItemGroup>
#include <QKeyEvent>
#include <QObject>
#include <QTime> // 为了引入 随机数
#include "elem.h"

enum shapeCode {
    shapeT,shapeL,shapeMirrorL,shapeSquare,shapeZ,shapeMirrorZ,shapeLine,shapeRandom
};// 这里面的值  0~7

enum shapeCoorId {// 形状坐标id
    LeftTop, RightTop,RightBottom,LeftBottom
};// 分别是 左上, 右上, 右下, 左下 为原点      在旋转的时候坐标系整体旋转

class MyShape : public QObject,public QGraphicsItemGroup
{
    Q_OBJECT
public:
    MyShape(QPoint origin = QPoint(300,40));
    QPoint getOrigin();
    virtual void rolate();
    virtual void rolateBack();
    int getCurrentStatus();

protected:
    QPoint origin;

    // 下面的变量用来标明当前使用的是哪一个形状, 在体现 旋转中心不同 等形状个性 的时候需要用到
protected:
    int currentStatus;// 指明了当前的旋转角度, 同时也表明了当前坐标系的状态,也说明了自己当前属于自己的形态的哪一种
    int colorId;

    Elem * elem[4];
};

#endif // MYSHAPE_H


**MyShape实现部分**:

#include "myshape.h"

MyShape::MyShape(QPoint origin)
{
    this->origin = origin;
    this->setPos(this->origin);
}

QPoint MyShape::getOrigin()
{
    return this->origin;
}

void MyShape::rolate()
{
    this->currentStatus = (this->currentStatus + 90) % 360;
    this->setTransformOriginPoint(20,20);
    this->setRotation(this->currentStatus);

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}

void MyShape::rolateBack()
{
    this->currentStatus = (this->currentStatus-90 + 360) % 360;
    this->setTransformOriginPoint(20,20);
    this->setRotation(this->currentStatus);

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}

int MyShape::getCurrentStatus()
{
    return this->currentStatus;
}


**各个具体的形状这里我只举例一个ShapeLine类:**
#ifndef SHAPELINE_H
#define SHAPELINE_H
#include "myshape.h"

class ShapeLine : public MyShape
{
public:
    ShapeLine(int currentStatus = 0,QPoint origin = QPoint(300,40),int colorId = elemColor::gray);
    virtual void rolate();
};

#endif // SHAPELINE_H

**ShapeLine类实现部分:**

#include "shapeline.h"

ShapeLine::ShapeLine(int currentStatus, QPoint origin,int colorId) : MyShape(origin)
{
    //this->shapeId = shapeCode::shapeLine;
    if (currentStatus % 90 || currentStatus < 0) {
        currentStatus = 0;
        this->currentStatus = currentStatus % 180;
    }
    else {
        qsrand(QTime::currentTime().second());
        this->currentStatus = (qrand() % 2) * 90;
    }
    this->colorId = colorId;

    elem[0] = new Elem(QPointF(0,0),this->colorId,this);
    elem[1] = new Elem(QPointF(0,20),this->colorId,this);
    elem[2] = new Elem(QPointF(0,40),this->colorId,this);
    elem[3] = new Elem(QPointF(0,60),this->colorId,this);

    this->addToGroup(elem[0]);
    this->addToGroup(elem[1]);
    this->addToGroup(elem[2]);
    this->addToGroup(elem[3]);

    while (this->currentStatus != 0) {
        this->rolate();
        this->currentStatus -= 90;
    }

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}

void ShapeLine::rolate()
{
    this->currentStatus = (this->currentStatus + 90) % 180;
    this->setTransformOriginPoint(20,40);
    this->setRotation(this->currentStatus);

    elem[0]->setCurrentStatus(this->currentStatus);
    elem[1]->setCurrentStatus(this->currentStatus);
    elem[2]->setCurrentStatus(this->currentStatus);
    elem[3]->setCurrentStatus(this->currentStatus);
}



**负责核心操控的Game类:**

#ifndef GAME_H
#define GAME_H
#include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsItem>
#include <QGuiApplication>
#include <QLabel>
#include <QPushButton>
#include "myshape.h"
#include <QTimer>
#include <QList>
#include <QPalette>
#include <QLabel>
#include <QGraphicsWidget>
#include <QMediaPlayer>
#include <QScreen>
#include <QDir>
#include "shapet.h"
#include "shapel.h"
#include "shapemirrorl.h"
#include "shapez.h"
#include "shapemirrorz.h"
#include "shapeline.h"
#include "shapesquare.h"
#include <QVector>
#include <iostream>
#include <QMessageBox>

using namespace std;

class Game : public QGraphicsView
{
    Q_OBJECT
public:
    Game();
    void keyPressEvent(QKeyEvent *event);
private slots:
    void init(); // 需要写一个初始化函数,明明已经有了构造函数,构造函数不久可一实现初始化吗,为什么还要重写一个init函数呢? 因为游戏会重新开局!!这个重新开局不是关闭游戏再重新打开游戏,而构造函数在整个过程中只是会执行一次,除非关闭程序重新运行才可以执行构造函数,显然着不符合用户需求,用户希望的是在不需要重新打开游戏的前提下,重新游戏
private:
    void startMenu();
    bool isShapeColliding(); // 方块之间冲突
    bool isBorderColliding(); // 方块和边界冲突
    bool moveDownOneStep();// 只是单纯的下降一步,返回值表示下降是否成功
    void clearUpAndRenewShape(); // 消除判断 和 新生一个形状
    void clearShapeWithNoChild(); // 清除没有孩子的形状
    MyShape * newShape(int shapeId = shapeCode::shapeRandom,int status = -1,QPoint landedPoint = QPoint(300,40));// shapeId 用于选择哪个形状, status 用于确定形状初始的形态 -1 表示随机, landedPoint 指定该形状的落点
    QRectF getShapeCurrentBoundingRectInScene();//这个是用来获取当前形状,无论是否被旋转过,都一律返回当前在scene中的以左上角为中心点的包围矩形
    void gameOver(); // 当消除后,当时形状的上界能够触碰到方块活动区域上边界,那么游戏结束,调用gameOver,做好一些后续处理工作
    void setFlag(bool flag);
protected slots:
    void timeSlice();
    void pause();
    void screenShotSlot(); // 保存游戏界面截图
protected:
    void mousePressEvent(QMouseEvent * event);
    void mouseMoveEvent(QMouseEvent * event);
    void mouseReleaseEvent(QMouseEvent *event);
private:
    MyShape * myshape;
    // 下面变量和用来指定方块活动区域范围
    QPoint areaOrigin; // 区域原点
    int areaWidth; //  区域宽
    int areaHeight; // 区域高

    QGraphicsItem * leftLine; // 左边界线
    QGraphicsItem * bottomLine;// 下边界线
    QGraphicsItem * rightLine;// 右边界线
    QGraphicsItem * topLine;// 上边界线

    // 定时器
    QTimer * timer;
    int timeCount; // 时间计数器   用来配合时间片的分配

    // 下面的变量用来记录方块下降速度 数值越大代表速度越高
    int speed;

    // 下面的变量用来存放 当前所有的形状, 这些形状包括是完整的形状和残缺的形状,一旦该形状一个孩子都没有,就把它删除并且从 下面的这个链表中删除
    QList<MyShape * > currentShapelist;

    int currentShapeId;

    QVector<QVector<bool>> sceneArray;// 这个大概就是我的终极方案了

    // 下面是关于分数的设置
    int grades;
    int rowsNumRemoves; //消除的行数
    int totalNumOfShapesUsed;
    QGraphicsTextItem * gradeText;
    QGraphicsTextItem * gradeNumber;

    QGraphicsWidget * mask;// 遮罩
    QPushButton * startButton;// 游戏开始按钮
    QPushButton * optionButton;// 选择项按钮
    QPushButton * helpButton;// 帮助按钮
    QPushButton * exitButton;// 游戏退出按钮
    QPushButton * exitButton_1;
    QPushButton * replay;// 重新游戏

    QPushButton * pauseButton; // 游戏暂停按钮

    QLabel * gameOverLabel;// 游戏结束文本显示
    QLabel * gradeLabel;

    bool gameStatus; // 游戏状态 运行 或者暂停  这个变量是为辅助实现暂停功能的

    // 下面是音效 背景音乐
    QMediaPlayer * bgm;

    // 下面是关于重新实现框口拖动需要的变量
    QPoint mousePressPos;
    bool mousePressed; // 标记鼠标按下
    int cnt;//用来辅助消除框体移动过程发生抖动现象 对应代码中有详解

    // 保存游戏截图
    QPixmap screenShot;
    QPushButton * screenShotButton;
};

#endif // GAME_H



**负责核心操控的Game类的实现部分:**

#include "game.h"

Game::Game()
{// 在构造函数中的代码都是在整个程序运行过程中只需要做一次的代码
    this->setWindowFlags(Qt::FramelessWindowHint);
    this->setWindowTitle(QStringLiteral("俄罗斯方块"));
    this->setWindowIcon(QIcon(":/image/images/icon.png"));

    // 设置整个游戏界面的尺寸
    this->setMinimumSize(810,510);
    this->setMaximumSize(810,510);

    // 下面是 设置 方块活动区域
    this->areaOrigin.setX(200);
    this->areaOrigin.setY(40);
    this->areaWidth = 240;
    this->areaHeight = 400;

    // 下面是 准备场景
    QGraphicsScene * scene = new QGraphicsScene;
    this->setScene(scene);
    scene->setSceneRect(5,5,800,500);
    scene->setBackgroundBrush(QBrush(QPixmap(":/image/images/Tetris_background_1.jpg")));

    // 绘制方块活动区域边界线 以及线条的颜色
    this->leftLine = scene->addLine(this->areaOrigin.x(),this->areaOrigin.y(),this->areaOrigin.x(),this->areaOrigin.y()+this->areaHeight,QPen(QBrush(QColor(40,40,120,80)),2));
    this->bottomLine = scene->addLine(this->areaOrigin.x(),this->areaOrigin.y()+this->areaHeight,this->areaOrigin.x()+this->areaWidth,this->areaOrigin.y()+this->areaHeight,QPen(QBrush(QColor(40,40,120,80)),2));
    this->rightLine = scene->addLine(this->areaOrigin.x()+this->areaWidth,this->areaOrigin.y()+this->areaHeight,this->areaOrigin.x()+this->areaWidth,this->areaOrigin.y(),QPen(QBrush(QColor(40,40,120,80)),2));
    this->topLine = scene->addLine(this->areaOrigin.x()+this->areaWidth,this->areaOrigin.y(),this->areaOrigin.x(),this->areaOrigin.y(),QPen(QBrush(QColor(40,40,120,80)),2));

    // 准备时间器
    this->timer = new QTimer(this);
    this->timer->start(80);
    this->timer->stop();
    QObject::connect(this->timer,SIGNAL(timeout()),this,SLOT(timeSlice()));

    // 规划场景数组
    this->sceneArray.resize(this->areaHeight/20);

    for (int i = 0; i < this->sceneArray.size(); i ++)
        this->sceneArray[i].resize(this->areaWidth/20);

    // 下面是关于分数
    this->gradeText = scene->addText(QStringLiteral("得分: "),QFont("Times",22,QFont::Bold));
    this->gradeNumber = scene->addText(QString::number(0),QFont("Times",22,QFont::Bold));
    this->gradeNumber->moveBy(600,80);
    this->gradeText->moveBy(520,80);

    this->pauseButton = new QPushButton(QStringLiteral("暂停"),this);
    this->pauseButton->move(520,150);
    QObject::connect(this->pauseButton,SIGNAL(clicked(bool)),this,SLOT(pause()));

    this->exitButton_1 = new QPushButton(QStringLiteral("结束"),this);
    this->exitButton_1->move(520,190);
    QObject::connect(this->exitButton_1,SIGNAL(clicked(bool)),this,SLOT(close()));

    this->replay = new QPushButton(QStringLiteral("重玩"),this);
    this->replay->move(520,230);
    QObject::connect(this->replay,SIGNAL(clicked(bool)),this,SLOT(init()));

    this->screenShotButton = new QPushButton(QStringLiteral("截图"),this);
    this->screenShotButton->move(520,270);
    QObject::connect(this->screenShotButton,SIGNAL(clicked(bool)),this,SLOT(screenShotSlot()));


    // 音效
    this->bgm = new QMediaPlayer(this);

    // 开始界面设置
    this->startMenu();

}

void Game::startMenu()
{
    this->mousePressed = false; // 初始鼠标没有被按下
    this->cnt = 0;
    // 刚开始是隐藏的
    this->pauseButton->hide();
    this->exitButton_1->hide();
    this->replay->hide();
    this->screenShotButton->hide();
    this->gradeText->hide();
    this->gradeNumber->hide();

    this->leftLine->hide();
    this->bottomLine->hide();
    this->rightLine->hide();
    this->topLine->hide();

    QWidget * mask = new QWidget();
    mask->setAutoFillBackground(true);
    mask->setPalette(QPalette(QColor(0,0,0,120)));
    mask->resize(810,510);
    this->mask = (QGraphicsWidget *)this->scene()->addWidget(mask);
    this->mask->setZValue(1);

    this->startButton = new QPushButton("开始",this);
    this->optionButton = new QPushButton("选项",this);
    this->helpButton = new QPushButton("帮助",this);
    this->exitButton = new QPushButton("退出",this);

    this->gameOverLabel = new QLabel("游戏结束",this);
    this->gameOverLabel->hide();
    this->gameOverLabel->setGeometry(300,80,150,50);
    this->gameOverLabel->setFont(QFont("Times",20,QFont::Bold));
    this->gameOverLabel->setPalette(QPalette(QColor(120,40,20)));
    this->scene()->addWidget(this->gameOverLabel);

    this->gradeLabel = new QLabel("得分 : ",this);
    this->gradeLabel->hide();
    this->gradeLabel->setGeometry(300,120,150,50);
    this->gradeLabel->setFont(QFont("Times",20,QFont::Bold));
    this->gradeLabel->setPalette(QPalette(QColor(120,40,20)));
    this->scene()->addWidget(this->gradeLabel);



    this->startButton->move(350,100);
    this->optionButton->move(350,150);
    this->helpButton->move(350,200);
    this->exitButton->move(350,250);

    QObject::connect(startButton,SIGNAL(clicked(bool)),this,SLOT(init()));
    QObject::connect(exitButton,SIGNAL(clicked(bool)),this,SLOT(close()));

    return ;
}

void Game::init()//会被多次调用 每次重新开局都是调用 这个函数
{
    this->mousePressed = false; // 初始鼠标没有被按下
    this->bgm->setMedia(QUrl::fromLocalFile("/home/jacklu/music/Summer.mp3"));
    this->bgm->play();

    // 下面是清空形状列表 考虑到重玩的时候需要清场
    QList<MyShape*>::iterator i = this->currentShapelist.begin();
    while (i !=this->currentShapelist.end()){
            this->scene()->removeItem(*i);// 删除该形状
            i = this->currentShapelist.erase(i);// 从当前有效形状表中删除
    }

    // 设置数组初始值
    for (int i = 0; i < this->sceneArray.size(); i ++)
        for (int j = 0; j < this->sceneArray[i].size(); j ++)
            this->sceneArray[i][j] = false;

    this->gameOverLabel->hide();
    this->pauseButton->setEnabled(true);

    this->gameStatus = true;// 只是标记下游戏的状态 当前为运行状态
    this->mask->hide();
    this->startButton->hide();
    this->optionButton->hide();
    this->helpButton->hide();
    this->exitButton->hide();


    this->leftLine->show();
    this->bottomLine->show();
    this->rightLine->show();
    this->topLine->show();

    this->pauseButton->show();
    this->exitButton_1->show();
    this->replay->show();
    this->screenShotButton->show();
    this->gradeText->show();
    this->gradeNumber->show();
    // 关于分数
    this->totalNumOfShapesUsed=0;
    this->rowsNumRemoves = 0;
    this->grades = 0;
    this->gradeNumber->setPlainText(QString::number(this->grades));
    this->myshape = this->newShape(shapeCode::shapeRandom);
    this->scene()->addItem(this->myshape);

    // 时间器开始
    this->timer->start();
    this->timeCount = 0;
    // 设置方块下降速度
    this->speed = 1;
}

void Game::keyPressEvent(QKeyEvent *event)
{
    if (!this->myshape)
        return ;
    if (!this->gameStatus)// 如果不是运行态,比如暂停态
        return ;
    if (event->key() == Qt::Key_Left) {
        this->setFlag(false);
        this->myshape->moveBy(-20,0);
        // 边界线冲突判断  以及 方块之间的判度阿森纳
        if (this->isBorderColliding() || this->isShapeColliding()) {
            this->myshape->moveBy(20,0);
            this->setFlag(true);
            return ;
        }
    }
    else if (event->key() == Qt::Key_Right) {
        this->setFlag(false);
        this->myshape->moveBy(20,0);
        // 边界线冲突判断 以及 形状之间冲突判断
        if (this->isBorderColliding() || this->isShapeColliding()) {
            this->myshape->moveBy(-20,0);
            this->setFlag(true);
            return ;
        }
    }
//    else if (event->key() == Qt::Key_Up) {
//        this->setFlag(false);
//        this->myshape->moveBy(0,-20);
//        this->setFlag(true);
//    }
    else if (event->key() == Qt::Key_Down) {
        for (int i = 0; i < 2; i ++) { // 用 for 循环来表达做同样的事情
            if (!this->moveDownOneStep())
                break;
        }
    }
    else if (event->key() == Qt::Key_Space) {
        this->setFlag(false);//清除原有标志
        this->myshape->rolate();
        // 边界线冲突判断 以及 方块之间冲突判断
        // 下面的代码同时使得旋转时候因为靠边原因导致有足够的空间却不能旋转的问题得到很好的解决,灵活度得到提升
        if (this->isBorderColliding() || this->isShapeColliding()) {//冲突
            this->myshape->moveBy(20,0);// 尝试右移动
            if (this->isBorderColliding() || this->isShapeColliding()) {// 仍然冲突
                this->myshape->moveBy(-40,0); // 尝试左移动
                if (this->isBorderColliding() || this->isShapeColliding()) { // 还是发生冲突
                    if (this->currentShapeId == shapeCode::shapeLine) {// shapeLine 特殊一点
                        this->myshape->moveBy(-20,0);
                        if (!(this->isBorderColliding() || this->isShapeColliding())) {
                            return ;
                        }
                    }
                    this->myshape->moveBy(20,0);
                    this->myshape->rolateBack();
                    this->setFlag(true);// 恢复标志
                    return ;
                }
            }
        }
        this->setFlag(true);
    }

    return ;
}

bool Game::isShapeColliding()
{
    // 只是判断方块之间的冲突
    int i, j;
    foreach (QGraphicsItem * item, this->myshape->childItems()) {
        switch(this->myshape->getCurrentStatus()) {// 也是因为坐标原点因为旋转而发生了改变引起的动态需求
            case 0:
                i = (item->scenePos().y()-this->areaOrigin.y())/20;
                j = (item->scenePos().x()-this->areaOrigin.x())/20;
                break;
            case 90:
                i = (item->scenePos().y()-this->areaOrigin.y())/20;
                j = (item->scenePos().x()-this->areaOrigin.x())/20-1;
                break;
            case 180:
                i = (item->scenePos().y()-this->areaOrigin.y())/20-1;
                j = (item->scenePos().x()-this->areaOrigin.x())/20-1;
                break;
            case 270:
                i = (item->scenePos().y()-this->areaOrigin.y())/20-1;
                j = (item->scenePos().x()-this->areaOrigin.x())/20;
                break;
        }

        if (this->sceneArray[i][j]) // 如果涉及的范围中存在 true ,则说明会产生冲突
            return true;
    }

    return false;
}

bool Game::isBorderColliding()
{
    // 判断的方法 : 根据 sceneBoundingRect() 提供的值我们得到该形状的包围矩形,判断该矩形各条边是否与边界线冲突
   QRectF rect = this->getShapeCurrentBoundingRectInScene();
   // if ( minX < this->areaOrigin.x() || minY < this->areaOrigin.y() || maxX > (this->areaOrigin.x()+this->areaWidth) || maxY > (this->areaOrigin.y() + this->areaHeight))
   // 不包括对上边界的判度,因为只有游戏结束的时候才会触碰上边界,游戏过程中的触碰都不作数,所以把上边界独立出来,不在判断边界冲突的函数中写出
    if ( rect.x() < this->areaOrigin.x() || (rect.x()+rect.width()) > (this->areaOrigin.x()+this->areaWidth) || (rect.y()+rect.height()) > (this->areaOrigin.y() + this->areaHeight))
         return true;
    return false;
}

bool Game::moveDownOneStep()// 只是单纯的下降一步,返回值表示下降是否成功
{
    this->setFlag(false);// 先清除原占有标志
    this->myshape->moveBy(0,20);
    //下面是 边界线冲突判断 以及 形状之间冲突判断
    if (this->isBorderColliding() || this->isShapeColliding()) {// 检测到有冲突 则下移动失败 采取相应的动作
        this->myshape->moveBy(0,-20);
        this->setFlag(true);// 恢复原占有标志
        // 下降失败 这意味着本次形状运行结束
        // 进入消除判断,然后紧接着 判断是否触碰上边界,因为消除完成后还触碰着上边界意味着游戏结束,如果没有结束,之后才是 新生一个形状
        this->clearUpAndRenewShape();
        return false;
    }

    // 没有发生冲突 直接占有当前位置的所有标志
    this->setFlag(true);

    return true;
}

void Game::clearUpAndRenewShape()
{
    int count = 0; // 用来记录已经消除了的行数
    // 下面是行消除判断和处理
    for (int y = this->areaOrigin.y()+this->areaHeight-20; y >= 0; y -= 20) {
        QList<QGraphicsItem *> list = this->scene()->items(QRectF(this->areaOrigin.x()-1,y-1,this->areaWidth+2,22),Qt::ContainsItemShape);

        if (list.count() == 0) {

            break;
        }
        QList<QGraphicsItem *>::iterator i = list.begin();
        while (i != list.end()) {
            if (!((*i)->sceneBoundingRect().width() <25)) {// 过滤掉 不是基本单位方块的 item
                i = list.erase(i);// erase之后返回值为原来i的下一个节点
            }
            else
                i ++;
        }

        int rowsForArray = (y-this->areaOrigin.y())/20;
        if (list.count() == (this->areaWidth/20)) {// 普通行满
            for (int k = 0; k < this->sceneArray[rowsForArray].size(); k ++)
                this->sceneArray[rowsForArray][k] = false;

            foreach (QGraphicsItem * item, list) {
                this->scene()->removeItem(item);
            }

             count ++;
        }
        else if (count > 0){// 没有满,但是之前发生了消除, 采取的措施就是,下降相应的行数(count即是要下降的行数)
            this->rowsNumRemoves+=count;
            for (int k = 0; k < this->sceneArray[rowsForArray].size(); k ++) {
                this->sceneArray[rowsForArray+count][k] = this->sceneArray[rowsForArray][k];
                this->sceneArray[rowsForArray][k] = false;
            }

            foreach (QGraphicsItem * item, list) {
                Elem * p = (Elem *)item;
                switch(p->getCurrentStatus()) {
                    case 0:
                        p->moveBy(0,count*20);
                        break;
                    case 90:
                        p->moveBy(count*20,0);
                        break;
                    case 180:
                        p->moveBy(0,0-count*20);
                        break;
                    case 270:
                        p->moveBy(0-count*20,0);
                        break;
                    default: QMessageBox::warning(this,"错误","Here , something wrong in game.cpp"); break;
                }
            }
        }
    }
    // 上面的代码进行的是检查和完成消除方块工作,紧接着,如果上面有消除发生,我们就对当前所有形状检查,是否有形状无效(即没有孩子)
    if (count > 0) { // 根据消除的行数来判断是否发生了消除
        this->clearShapeWithNoChild();   
    }

    // 下面是判断当前方块是否接触了上边界 ,也就是意味着判断游戏是否结束
    qDebug() << "this->getShapeCurrentBoundingRectInScene.y() " << this->getShapeCurrentBou

以上是关于Qt-俄罗斯方块的主要内容,如果未能解决你的问题,请参考以下文章

QT实现俄罗斯方块游戏

俄罗斯方块 Tetris

C++俄罗斯方块

发个无聊时写的俄罗斯方块(分为SDL和Qt两个版本)

java的俄罗斯方块代码

求用JAVA编写俄罗斯方块游戏的源代码