使用(C++)QT实现经典小游戏(俄罗斯方块)

一、简介

        学习QT有段时间了,但是也没用来做过啥,这两日就突然想到了这个游戏,于是就打算用QT写一个,也不是纯纯自己写的代码,在写之前我也是找了几个别人的项目看了一下,学习了框架的搭建,和某些类的使用。

首先我也是将整个程序分作了四块:

一、方块类:主要就是对每个方块的坐标的一个封装

二、游戏区域:根据实时的要求,根据坐标画出方块

三、下一块方块:刷新下一块方块

四、计分区域:根据得分显示关卡、分数等

二、代码实现

         在代码中都已经写了注释了,外面就不多累述了

1.ui界面

NextArea和GameArea都是由Qwidget提升上来的。

2.item方块类

2.1头文件

/*
 * 俄罗斯方块的方块类文件
*/
#ifndef ITEM_H
#define ITEM_H
#pragma once
#include <QObject>
#include <QWidget>
#include <QVector>
#include <QPoint>
#include <QPainter>

typedef  QVector<QPoint> povector; //坐标容器

enum ItemType//7种方块,四种朝向
{
    ItemType_I = 0,
    ItemType_L1,
    ItemType_L2,
    ItemType_T,
    ItemType_O,
    ItemType_Z1,
    ItemType_Z2,
    ItemType_MAX,
};

class Item
{

public:
    Item(){}
    Item(ItemType type,int direction);//有参构造方块
    ~Item(){}

    void NewItem(int random_number);//通过随机的一个数来创建一个新的方块
    void initpoint(ItemType type,int direction);//根据方块的类型和朝向初始化四个小方块的坐标
    ItemType type();         //返回方块的类型
    int direction();        //返回方块的朝向
    void ClearPoints();//清除坐标信息
    void drawItem(QPainter &painter,int x,int y,int width,int height);   //根据坐标和宽高画图形
    void changedirection(int direction=1);    //改变方块的朝向,我的朝向加1即为下一个朝向,顺时针旋转90°
    void moveItem(int x,int y);//横向移动x格,竖向移动y格
    void movetoItem(int x,int y);//移动到(x,y)位置
    void AddPoints(povector& points); //添加方块格子坐标
    void Deleterow(int y);          //删除坐标为y的一行
    void MoveDown(int nRow, int y);//删除行以后其它的方块下移


public:
    ItemType mytype; //我的方块
    int mydirection; //我的方块朝向
    QPoint mypos;   //我的方块位置坐标
    povector myPoint;   //我的方块四小块的坐标

};

#endif // ITEM_H

2.2源文件

#include "item.h"

//创建方块
void Item::NewItem(int random_number)
{
    myPoint.clear();
    qsrand(random_number);//获取随机数
    ItemType type = (ItemType)(qrand()%ItemType_MAX); // 产生随机的方块类型
    int direction = qrand()%4; 产生随机的方块的朝向
    initpoint(type,direction);
    mytype = type;
    mydirection = direction;
}

//初始化方块位置信息
void Item::initpoint(ItemType type, int direction)
{
    direction = 0;
    switch (type)
    {
    case ItemType_I://长条形
    {
        if(0==direction%2)//朝向0 2都是横长条
        {
            for (int i = 0; i < 4; i++)
            {
                myPoint.append(mypos + QPoint( i,0));
            }
        }
        else//朝向1 3都是竖长条
        {
            for (int i = 0; i < 4; i++)
            {
                myPoint.append(mypos + QPoint(0,i-3));
            }
        }
        break;
    }

    case ItemType_L1://右枪形
    {
        if(0==direction)//朝向0
        {
            myPoint.append(mypos + QPoint( 0,0));// 1 0 0
            myPoint.append(mypos + QPoint( 0,1));// 1 0 0
            myPoint.append(mypos + QPoint( 0,2));// 1 1 0
            myPoint.append(mypos + QPoint( 1,2));// 0 0 0
        }
        else if(1==direction)//朝向1
        {
            myPoint.append(mypos + QPoint( 0,1));// 0 0 0
            myPoint.append(mypos + QPoint( 1,1));// 1 1 1
            myPoint.append(mypos + QPoint( 2,1));// 1 0 0
            myPoint.append(mypos + QPoint( 0,2));// 0 0 0
        }
        else if(2==direction)//朝向2
        {
            myPoint.append(mypos + QPoint( 0,0));
            myPoint.append(mypos + QPoint( 1,0));// 1 1 0
            myPoint.append(mypos + QPoint( 1,1));// 0 1 0
            myPoint.append(mypos + QPoint( 1,2));// 0 1 0
        }
        else//朝向3
        {
            myPoint.append(mypos + QPoint( 0,2));//0 0 0
            myPoint.append(mypos + QPoint( 1,2));//0 0 1
            myPoint.append(mypos + QPoint( 2,2));//1 1 1
            myPoint.append(mypos + QPoint( 2,1));//
        }
        break;
    }
    case ItemType_L2://左枪形
    {
        if(0==direction)//朝向0
        {
            myPoint.append(mypos + QPoint( 1,0));//0 1 0 0
            myPoint.append(mypos + QPoint( 1,1));//0 1 0 0
            myPoint.append(mypos + QPoint( 1,2));//1 1 0 0
            myPoint.append(mypos + QPoint( 0,2));//0 0 0 0
        }
        else if(1==direction)//朝向1
        {
            myPoint.append(mypos + QPoint( 0,1));// 0 0 0
            myPoint.append(mypos + QPoint( 0,2));// 1 0 0
            myPoint.append(mypos + QPoint( 1,2));// 1 1 1
            myPoint.append(mypos + QPoint( 2,2));// 0 0 0
        }
        else if(2==direction)//朝向2
        {
            myPoint.append(mypos + QPoint( 0,0));//
            myPoint.append(mypos + QPoint( 1,0));// 1 1 0
            myPoint.append(mypos + QPoint( 0,1));// 1 0 0
            myPoint.append(mypos + QPoint( 0,2));// 1 0 0
        }
        else//朝向3
        {
            myPoint.append(mypos + QPoint( 0,1));//0 0 0 0
            myPoint.append(mypos + QPoint( 1,1));//1 1 1 0
            myPoint.append(mypos + QPoint( 2,1));//0 0 1 0
            myPoint.append(mypos + QPoint( 2,2));//0 0 0 0
        }
        break;
    }
    case ItemType_T://T形
    {
        if(0==direction)//朝向0
        {
            myPoint.append(mypos + QPoint( 1,0));//0 1 0 0
            myPoint.append(mypos + QPoint( 1,1));//1 1 1 0
            myPoint.append(mypos + QPoint( 0,1));//0 0 0 0
            myPoint.append(mypos + QPoint( 2,1));//0 0 0 0
        }
        else if(1==direction)//朝向1
        {
            myPoint.append(mypos + QPoint( 0,0));// 1 0 0
            myPoint.append(mypos + QPoint( 0,1));// 1 1 0
            myPoint.append(mypos + QPoint( 0,2));// 1 0 0
            myPoint.append(mypos + QPoint( 1,1));// 0 0 0
        }
        else if(2==direction)//朝向2
        {
            myPoint.append(mypos + QPoint( 0,0));
            myPoint.append(mypos + QPoint( 1,0));//1 1 1 0
            myPoint.append(mypos + QPoint( 2,0));//0 1 0 0
            myPoint.append(mypos + QPoint( 1,1));//0 0 0 0
        }
        else//朝向3
        {
            myPoint.append(mypos + QPoint( 1,0));//0 1 0 0
            myPoint.append(mypos + QPoint( 0,1));//1 1 0 0
            myPoint.append(mypos + QPoint( 1,1));//0 1 0 0
            myPoint.append(mypos + QPoint( 1,2));//0 0 0 0
        }
        break;
    }
    case ItemType_O://正方形
    {
        myPoint.append(mypos + QPoint( 0,0));//1 1 0 0
        myPoint.append(mypos + QPoint( 0,1));//1 1 0 0
        myPoint.append(mypos + QPoint( 1,0));//0 0 0 0
        myPoint.append(mypos + QPoint( 1,1));//0 0 0 0
        break;
    }
    case ItemType_Z1://右Z形
    {
        if(0==direction%2)//朝向0/2
        {
            myPoint.append(mypos + QPoint( 0,0));// 1 0 0
            myPoint.append(mypos + QPoint( 0,1));// 1 1 0
            myPoint.append(mypos + QPoint( 1,1));// 0 1 0
            myPoint.append(mypos + QPoint( 1,2));// 0 0 0
        }

        else//朝向1/3
        {
            myPoint.append(mypos + QPoint( 0,1));//
            myPoint.append(mypos + QPoint( 1,0));//0 1 1 0
            myPoint.append(mypos + QPoint( 1,1));//1 1 0 0
            myPoint.append(mypos + QPoint( 2,0));//0 0 0 0
        }
        break;
    }
    case ItemType_Z2://左Z形
    {
        if(0==direction%2)//朝向0/2
        {
            myPoint.append(mypos + QPoint( 1,0));// 0 1 0
            myPoint.append(mypos + QPoint( 0,1));// 1 1 0
            myPoint.append(mypos + QPoint( 1,1));// 1 0 0
            myPoint.append(mypos + QPoint( 0,2));// 0 0 0
        }

        else//朝向1/3
        {
            myPoint.append(mypos + QPoint( 0,0));
            myPoint.append(mypos + QPoint( 1,0));//1 1 0 0
            myPoint.append(mypos + QPoint( 1,1));//0 1 1 0
            myPoint.append(mypos + QPoint( 2,1));//0 0 0 0
        }
        break;
    }
    default:
        break;
    }
}

//方块类型
ItemType Item::type()
{
    return mytype;
}

//方块朝向
int Item::direction()
{
    return mydirection;
}

//清除方块位置信息
void Item::ClearPoints()
{
    myPoint.clear();
}

//画方块
void Item::drawItem(QPainter &painter,int x,int y,int width, int height)
{
    for (int i = 0; i < myPoint.size(); i++)
    {
        QPoint pt = myPoint[i];
        if(pt.y()>=1)
        {
            painter.drawRect(QRect(x+pt.x() * width,y+pt.y() * height, width, height));
        }
    }
}

//改变方块朝向
void Item::changedirection(int direction)
{
    mydirection = (mydirection+direction)%4;
    initpoint(mytype,mydirection);
}

//移动方块
void Item::moveItem(int x, int y)
{
    for (int i = 0; i < myPoint.size(); i++)
    {
        int x1 = myPoint[i].x() + x;
        int y1 = myPoint[i].y() + y;
        myPoint[i].setX(x1);
        myPoint[i].setY(y1);
    }
    mypos += QPoint(x, y);
}

//将方块移动到指定位置
void Item::movetoItem(int x, int y)
{
    for (int i = 0; i < myPoint.size(); i++)
    {
        int x1 = myPoint[i].x()-mypos.x() + x;
        int y1 = myPoint[i].y()-mypos.y() + y;
        myPoint[i].setX(x1);
        myPoint[i].setY(y1);
    }
    mypos = QPoint(x, y);
}

//添加方块坐标
void Item::AddPoints(povector &points)
{
    for(int i=0;i<points.size();i++)  // 遍历Point
    {
        if(!myPoint.contains(points[i]))  //如果myPoint中没有
            myPoint.append(points[i]);   //添加进myPoint
    }
}

//第nRow行以上的部分下移y行,用在消格之后
void Item::MoveDown(int nRow, int y)
{
    for(int i=0;i<myPoint.size();i++)    //遍历myPoint ( 方块类形坐标容噐)
    {
        if(myPoint[i].y()<nRow)          //行坐标小于nRow 的
            myPoint[i].setY(myPoint[i].y()+y); //行标加y,后回写
    }
}

//删除第y行
void Item::Deleterow(int y)
{
    QVector<QPoint> newPoints;   //新建一个新的容器
    for(int i=0;i<myPoint.size();i++)  //遍历mPoints ( 方块类形坐标容噐)
    {
        if(myPoint[i].y()!=y)            //如果行坐标不等于y,
           newPoints.append(myPoint[i]); //添加到新的点容器

    }
    myPoint=newPoints;  //新的容器回写到mPoints,等于y的方块便不再绘画
}

3.GameArea游戏窗口

3.1头文件

/*
 * 俄罗斯方块的游戏区域文件
*/
#ifndef GAMEAREA_H
#define GAMEAREA_H

#include <QWidget>
#include "item.h"
#include <QTimerEvent>
#include <QMessageBox>
#include <QKeyEvent>
#include <QTime>

class GameArea : public QWidget
{
    Q_OBJECT
public:
    explicit GameArea(QWidget *parent = nullptr);

    void DrawBKRects();         //画游戏区域
    void DrawFixedRects();      //画下落后已固定不动的方块
    void DrawCurItem();         //画当前下落中的方块

    void NewGame();             //创建新游戏界面
    void KeyPressed(int key);   //收到按键值处理

    bool HitSide();             //判断当前下落方块是否超左右边界
    bool HitBottom();           //判断当前下落方块是否达到底部
    bool HitTop();              //判断当前下落方块是否达到顶部
    void AddToFixedRects();     //把当前方块加入到 固定方块
    void Deleterow();           //删除完整的行

    int GetLevelTime(int level);   //获取不同等级关卡对应的定时器时间,关卡越高,时间越快(短)。比如1关=1s,2关=900ms,3关=800ms

signals:
    void sigUpdateNextItem(ItemType type, int direction);   //下一个方块刷新信号
    void sigUpdateScore(int nScore);                        //刷新分数
    void sigUpdateLevel(int nSpeed);                        //刷新等级
    void sigPause(bool bPaused);                            //暂停信号

protected:
    void paintEvent(QPaintEvent *e); //绘图事件
    void timerEvent(QTimerEvent *e);  //定时器事件

private:
    Item myFixItems;     //已落下、固定住了的方块们
    Item myCurItem;      //当前移动中的方块
    Item myNextItem;     //下一个方块

    int myTimerID;       //定时器ID
    int myScore;         //得分
    int myLevel;         //关卡
    bool myStop;        //是否暂停

};

#endif // GAMEAREA_H

3.2源文件

#include "gamearea.h"
#include <QDebug>

//默认游戏区域为15*20的单元格,每个单元格尺寸40*40像素
#define MAX_COLUME		15
#define MAX_ROW			20
#define RECT_WIDTH		40
#define RECT_HEIGHT		40

//GameArea类初始化
GameArea::GameArea(QWidget *parent) : QWidget(parent)
{
    myScore = 0;
    myLevel = 1;
    myStop = false;
    setMinimumSize(MAX_COLUME*RECT_WIDTH, MAX_ROW*RECT_HEIGHT);
}

//画游戏区域的背景
void GameArea::DrawBKRects()
{
    //画背景边框
    QPainter painter(this);
    painter.setBrush(QColor("#696969"));
    painter.setPen(Qt::NoPen);

    for (int i = 0; i < MAX_COLUME; i++)
    {
        for (int j = 0; j < MAX_ROW; j++)
        {
            if (i == 0 || i == MAX_COLUME - 1 || j == 0 || j == MAX_ROW - 1)
            {
                painter.drawRect(i*RECT_WIDTH, j*RECT_HEIGHT, RECT_WIDTH, RECT_HEIGHT);
            }
        }
    }
}

//画已经下落到底部固定的方块
void GameArea::DrawFixedRects()
{
    QPainter painter(this);
    painter.setBrush(QColor("#D3D3D3"));
    painter.setPen(QPen(QColor(Qt::black), 1));
    myFixItems.drawItem(painter,0,0,RECT_WIDTH,RECT_HEIGHT);
}

//画当前正在下落中的方块
void GameArea::DrawCurItem()
{
    QPainter painter(this);
    painter.setBrush(QColor("#FFDEAD"));
    painter.setPen(QPen(QColor(Qt::black), 1));
    myCurItem.drawItem(painter,0,0,RECT_WIDTH,RECT_HEIGHT);
}

//创建一个新的游戏界面
void GameArea::NewGame()
{
    myFixItems.ClearPoints();

    myCurItem.NewItem(QTime::currentTime().msec());//创建新的方块,当前的和下一个应不一样,所以随机数应不同
    myCurItem.movetoItem(MAX_COLUME/2-1,1);           //当前下落方块移动到中心位置

    myNextItem.NewItem(QTime::currentTime().second());
    emit sigUpdateNextItem(myNextItem.type(),myNextItem.direction());//发送信号刷新下一块区域

    myScore=0;
    myLevel=1;
    myTimerID = startTimer(GetLevelTime(myLevel));                    //开启定时器

}

//收到了按键信息
void GameArea::KeyPressed(int key)
{
    int x=0,y=0;
    switch (key) {
    case Qt::Key_Up:
    {
        myCurItem.changedirection(1);
        if (HitSide()||HitBottom())//左右下到了边界
        {
            myCurItem.changedirection(-1);
        }
        return;
    }
    case Qt::Key_Right:
    {
        x=1;
        break;
    }
    case Qt::Key_Down:
    {
        y=1;
        break;
    }
    case Qt::Key_Left:
    {
        x=-1;
        break;
    }
    case Qt::Key_Space:
    {
        while (1)
        {
            myCurItem.moveItem(0,1);
            if(HitBottom())
            {
                myCurItem.moveItem(0,-1);
                break;
            }
        }
        return;
    }
    case Qt::Key_Return:
    {
        if(myCurItem.myPoint.size()!=0)
        {
            if(myStop == true)
                myStop = false;
            else
                myStop = true;
            break;
        }
    }
    }
    myCurItem.moveItem(x,y);
    if (HitSide()||HitBottom())//左右下到了边界
    {
        myCurItem.moveItem(-x, -y);
    }
}

//如果碰到了左右边界
bool GameArea::HitSide()
{
    for (int i=0;i<myCurItem.myPoint.size();i++)
    {
        QPoint pt=myCurItem.myPoint[i];
        if(pt.x()<=0||pt.x()>=MAX_COLUME-1)  //如果x座标小于等于1或大于等于MAX_COLUME-1,则表示撞上左右边界了
            return true;
     }
    return false;
}

//如果到了底部
bool GameArea::HitBottom()
{
    for (int i=0;i<myCurItem.myPoint.size();i++)
    {
        QPoint pt=myCurItem.myPoint[i];
        if(pt.y()>=MAX_ROW-1)  //如果y座标大于等于RECT_ROWS-1,则表示撞上下边界了
            return true;
        if(myFixItems.myPoint.contains(pt))// 如果固定不动的方块的座标包含当前下落方块的座标,则表示下到底了
            return true;
     }
    return false;
}

//如果碰到了顶部
bool GameArea::HitTop()
{
    for (int i=0;i<myCurItem.myPoint.size();i++)
    {
        QPoint pt=myCurItem.myPoint[i];
        if(pt.y()<=1)  //如果y座标大于等于RECT_ROWS-1,则表示撞上下边界了
            return true;
     }
    return false;
}

//将当前的坐标给固定的方块
void GameArea::AddToFixedRects()
{
    myFixItems.AddPoints(myCurItem.myPoint);
}

//删除完整的行
void GameArea::Deleterow()
{
    int nRowsDeleted = 0; //记录消的行数
    for (int i = 1; i<MAX_ROW-1; i++) //遍历游戏区座标
    {
        int nCount = 0;
        for (int j = 1; j<MAX_COLUME-1; j++)
        {
            if (myFixItems.myPoint.contains(QPoint(j,i))) //判定每行有多少位置有方块
            {
                nCount++; // 每行方块的个数
            }
        }
        if (nCount == MAX_COLUME-2)// 如果每行的个数等于MAX_COLUME-2, 则代表是满的
        {
            myFixItems.Deleterow(i);        //将这一行删除
            myFixItems.MoveDown(i,1);    //消除行之上的内容下移一个单位
            nRowsDeleted++;
        }
    }

    //一次元素落下,最多可能消4行
    //一次消除的越多,得分越多
    if (nRowsDeleted == 1)
    {
        myScore += 20;
    }
    else if (nRowsDeleted == 2)
    {
        myScore += 60;
    }
    else if (nRowsDeleted == 3)
    {
        myScore += 120;
    }
    else if (nRowsDeleted == 4)
    {
        myScore += 200;
    }
    emit sigUpdateScore(myScore);    //更新MainWindow界面得分

    //粗略使用每1000分一关
    if(myScore >= 1000 * myLevel)
    {
        myLevel++;

        //随关卡增加下落速度,即把定时器加快
        killTimer(myTimerID);
        myTimerID = startTimer( GetLevelTime(myLevel));

        emit sigUpdateLevel(myLevel);    //更新MainWindow界面关卡
    }
}

//当前等级的速度
int GameArea::GetLevelTime(int level)
{
    if(level>10)
    {
        return 400;
    }
    else
    {
        return (19-level)*50;
    }

}

//绘画事件重写
void GameArea::paintEvent(QPaintEvent *)
{
    DrawBKRects();
    DrawFixedRects();
    DrawCurItem();
    if(myStop)//暂停中
    {
        QFont font;
        font.setPixelSize(100);
        QPainter painter(this);
        painter.setBrush(Qt::NoBrush);
        painter.setFont(font);
        painter.setPen(QPen(QColor("red"),1));
        painter.drawText(rect(), Qt::AlignCenter, "暂停");

    }


    update();
}

//定时器重写
void GameArea::timerEvent(QTimerEvent *e)
{
    if(myStop)
    {
        return;
    }
    else
    {
        if(myTimerID==e->timerId())
        {
            myCurItem.moveItem(0,1);
            if (HitBottom())//碰到底部
            {
                myCurItem.moveItem(0, -1);  //移动到原位
                AddToFixedRects();          //将方块的坐标给固定方块
                Deleterow();//判断是否有完整的行,然后删除
                if(HitTop())//碰到了顶部,游戏结束
                {
                    killTimer(myTimerID);
                    QMessageBox::information(NULL, "GAME OVER", "GAME OVER", QMessageBox::Yes, QMessageBox::Yes);
                    myFixItems.ClearPoints();
                    myCurItem.ClearPoints();
                    return;
                }

                myCurItem = myNextItem;     //下一个方块给当前方块赋值
                myCurItem.movetoItem(MAX_COLUME/2-1, 1);//移动到中心位置

                myNextItem.NewItem(QTime::currentTime().msec());//下一个方块继续创建
                emit sigUpdateNextItem(myNextItem.type(), myNextItem.direction());//发送信号下一块区域刷新方块
            }
        }
    }

}

4.NextArea刷新下一块窗口

4.1头文件

#ifndef NEXTAREA_H
#define NEXTAREA_H

#include <QObject>
#include <QWidget>
#include "item.h"

class NextArea:public QWidget
{
    Q_OBJECT
public:
    explicit NextArea(QWidget *parent = nullptr);

private:
    void paintEvent(QPaintEvent *event);

public slots:
    void UpdateNextItem(ItemType type, int direction);//刷新方块
private:
    Item myItem;

};

#endif // NEXTAREA_H

4.2源文件

#include "nextarea.h"

NextArea::NextArea(QWidget *parent): QWidget(parent)
{

}

void NextArea::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setBrush(QColor("#FFDEAD"));
    painter.setPen(QPen(QColor(Qt::black),1));
    int xStart = 0;    //为了绘制在显示下一个方块区域的中部
    int yStart = 80;
    int w = 30;
    int h = 30;
    for(int i=0;i<myItem.myPoint.size();i++)
    {
        QPoint pt = myItem.myPoint[i];
        int x = xStart + pt.x() * w;
        int y = yStart + pt.y() * h;
        painter.drawRect(x, y, w, h);
    }
    update();
}

void NextArea::UpdateNextItem(ItemType type, int direction)
{
    myItem.initpoint(type,direction);
}




5.主窗口

5.1头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QMediaPlayer>
#include <QMediaPlaylist>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

protected:
    void keyPressEvent(QKeyEvent *e);//按键事件重写


private slots:
    void UpdateLevel(int Level);//刷新等级
    void UpdateScore(int Score);//刷新分数
    void on_pbPuase_clicked();//开始按钮

    void on_checkBox_stateChanged(int arg1);//音乐播放

private:
    Ui::MainWindow *ui;
    QMediaPlayer *player;
    QMediaPlaylist *Playlist;
};
#endif // MAINWINDOW_H

5.2源文件

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

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setFixedSize(1000,800); //设置窗体为固定大小
    setWindowTitle(tr("俄罗斯方块"));//设置窗体标题

    connect(ui->widgetGameArea,&GameArea::sigUpdateLevel,this,&MainWindow::UpdateLevel);//等级
    connect(ui->widgetGameArea,&GameArea::sigUpdateScore,this,&MainWindow::UpdateScore);//分数
    connect(ui->widgetGameArea,&GameArea::sigUpdateNextItem,ui->widgetNextArea,&NextArea::UpdateNextItem);//下一块

    Playlist =new QMediaPlaylist;     //创建新的播放清单
    player=new QMediaPlayer;          //创建新的播放器
    Playlist->addMedia(QUrl::fromLocalFile("F:/MyQT/Teris/media/1.mp3")); //往播放清单中添加音乐文件
    Playlist->addMedia(QUrl::fromLocalFile("F:/MyQT/Teris/media/2.mp3"));
    Playlist->addMedia(QUrl::fromLocalFile("F:/MyQT/Teris/media/3.mp3"));
    Playlist->setCurrentIndex(0);  //设置默认播放开始位置
    Playlist->setPlaybackMode(QMediaPlaylist::Loop); //设置循环播放
    player->setPlaylist(Playlist); //播放清单导入播放器
    player->play(); //播放器开播
}

MainWindow::~MainWindow()
{
    delete ui;

}

//按键由窗口接收,所以传递过去
void MainWindow::keyPressEvent(QKeyEvent *e)
{
    ui->widgetGameArea->KeyPressed(e->key());
    QMainWindow::keyPressEvent(e);
}

void MainWindow::UpdateLevel(int level)
{
    ui->labelLevel->setText(QString::number(level));
}

void MainWindow::UpdateScore(int score)
{
    ui->labelScore->setText(QString::number(score));
}


//开始游戏
void MainWindow::on_pbPuase_clicked()
{
    ui->widgetGameArea->NewGame();
}

//音乐是否播放槽函数
void MainWindow::on_checkBox_stateChanged(int arg1)
{
    if(arg1)
        player->play();
    else
        player->stop();
}

三、概述

        对这个游戏来看,我自己的理解很直白的就是对数组的应用。在这个程序中,最核心的一点在于QVector<QPoint>这个坐标容器,在这个容器中,保存着所有需要画的方块的坐标,这个游戏的运行就是对这个容器里面的坐标增删改查的操作。

1.增

        具体表现就是在程序中落到底部的固定方块,方块到了底部以后,将坐标加入到这个容器中,也就实现了,方块的堆积。

2.删

        这个的直观表现就是完整行的消除,遍历整个游戏区域,每行是有方块限制的,当满了以后,便可以删除相关的坐标,便实现了消除。

3.改

        方块的形状改变便是更改的坐标,在设计中,每一个方块都是由各自的坐标的,当需要改变形状时,返回了当前形状的坐标。

4.查

        在下落的过程中,会一直遍历整个区域的坐标,当下落的方块中有存在了与固定方块相同的坐标时,便代表着该停止了,已经触碰了。