c++和QT实现俄罗斯方块,使用GraphicsView。

使用c++和QT图形框架进行实现(QT 5.8)

采用QT提供的图形开发库QGraphicsView QGraphicsScene QGraphicsItem.

项目完整的代码:https://github.com/qiny1012/Tetris

建立一个子类MyItem继承QGraphicsItem。构造一个矩形元素,可以给这个元素贴图设置颜色美化程序。

建立一个类MyGroup用来设置方块组,有o型,L型,J型等,包含一个MyItem的链表。

建立一个类MyScene继承QGraphicsScene,来实现方块的显示和运动。

类的核心参数

  1. MyItem,

MyItem(int w,int h);//构造一个宽为w,高为h的矩形方块

  1. MyGroup:

int pos_x;

     int pos_y;//设置方块组的位置

  QList<QGraphicsItem * > list;//方块的链表

int Type_rotation; 

     int Type_style;//设置当前的状态。方块组的方块有7中类型,每一种类型有4中旋转的状态。

 

  MyGroup();

     MyGroup(int pos_x,int pos_y,int type);//构造函数,前两个是方块组放到位置,第三个参数是方块的类型,默认的旋转类型是0.

     int isColliding();//碰撞检测

     void zhuangLeft();//顺时针旋转90

     void zhuangRigth();//逆时针旋转90

void setPos(int x,int y);//设置方块组中各个元素的坐标

     void moveBy(int x,int y);//移动方块组中的全部元素。

    

  1. MyScene

QGraphicsLineItem * lineItem1;

    QGraphicsLineItem * lineItem2;

    QGraphicsLineItem * lineItem3;//用于构造界面的线元素,也用于碰撞检测

    QTimer *timer;//定时器

    MyGroup * curGroup;//当前可以移动的方块组

   MyGroup * nextGroup;//下一个方块组,设置在游戏的右上角

     QGraphicsItem * type_ret[20][10];//界面上方块的数组,把地图看成一个10*20的数组,通过这个元素,确定是否可以销毁一行数据

     int score;//得分,并没有使用

     int testEnd;//检测游戏是否结束的一个变量

 

explicit MyScene(QObject *parent = 0);使用信号和槽函数就需要这样写,explicit表示参数不可以强制类型转化

    void initScene();//初始化场景

    int IsColliding(); //碰撞检测

         void addGroup(MyGroup * group);//添加一个方块组到场景中

     void creatNewGroup();  //产生一个方块组并且放在场景中

     void setRet(); //方块放置完成记录方块。

     int testRet(int row); //如果有放置之后要检测这一行是不是已经数据完成了,如果成功放回1,否则0

     int destroyFromRet(int row); //把row行的数据删除,然后再把数据全部向下移动

     int moveDownRet(int row);//移动小于row的数据,并且修改他们的位置

     void strat();//开始游戏

     void stop();//暂停游戏

      void starNewGame();//开始一个新的比赛,清除所有的元素,并且重新生成curGroup和nextGroup。

void keyPressEvent(QKeyEvent * keyEvent);//键盘事件

程序执行的过程:

  1. 建立一个QGraphicsView的对象,添加到Ui中,在建立一个myscene的对象,设置他的大小。
  2. 设置要给按钮,可以调用myscene的start()函数。
  3. Myscene内部的定时器,没过0.5秒进行移动一次curGroup
  4. 如果curGroup发生了碰撞,就撤下移动,并判断是否有放满的行。如果有就删除该行,并且移动该行以上的数据。
  5. 如果失败会提示,并且重新开始

难点:

  1. 坐标,myItem移动之后,无法获取他的坐标.pos()总是返回0,0.想要使用myitem的pos必须设置。
  2. 旋转,如果使用库中的QGraphicsItemGroup,有现成的旋转函数,但是使用的过程之后,无法正常的进行碰撞检测,最后没有使用这个类。旋转函数是自己写的,它是通过移动方块到指定的位置,实现旋转的感觉。
  3. 游戏结束。目前如果程序一直按space键,还是会出现bug。

源码(如果没有看懂可以直接去参考源码,写的很稀碎)

1.MyItem.h和MyItem.cpp

#include <QGraphicsRectItem>

class MyItem : public QGraphicsRectItem
{
public:
    MyItem();
    MyItem(int x);
    MyItem(int w,int h);
    MyItem(int x,int y,int w,int h);

};


#include "myitem.h"
//使用这个函数构造一个item的类
MyItem::MyItem(int w, int h)
{
    this->setRect(0,0,18,18);
}

//没有使用到
MyItem::MyItem(int x, int y, int w, int h)
{
    this->setRect(x,y,w,h);

}

2. MyGroup.h和MyGroup.cpp

#include "myitem.h"
#include <QRect>
#include <QDebug>
#include <QGraphicsItemGroup>
#include <QGraphicsObject>
#include <QKeyEvent>

class MyGroup
{
public :
    int pos_x;
    int pos_y;
    //组的位置
public:
    MyGroup();
    MyGroup(int pos_x,int pos_y,int type);
    int isColliding();
    void zhuangLeft();
    void zhuangRigth();
    void clear();

    QList<QGraphicsItem * > list;
    int Type_rotation; //设置当前的状态。
    int Type_style;
    void setPos(int x,int y);
    void moveBy(int x,int y);

};

#include "mygroup.h"
int type_pos_x [7][4][4] = {

  //第一种情况
  {
    {0,20,0,20},
    {0,20,0,20},
    {0,20,0,20},
    {0,20,0,20},
  },

  //第二个图形
    {

      {0,0,0,20},
      {20,0,-20,-20},
      {20,20,20,0},
      {0,20,40,40},

    },

   //第三个图形
  {
    {20,20,20,0},
    {20,0,-20,-20},
    {0,0,0,20},
    {0,20,40,40},
  },

    //第四个图形
   {
     {0,0,20,20},
     {20,0,0,-20},
     {0,0,20,20},
     {20,0,0,-20},
   },

    //第5个图形
   {
     {20,20,0,0},
     {20,0,0,-20},
     {20,20,0,0},
     {20,0,0,-20},
   },
    //第6个图形
   {
     {0,0,0,0},
     {-40,-20,0,20},
     {0,0,0,0},
     {-40,-20,0,20},
   },
    //第7个图形
   {
     {0,-20,0,20},
     {20,0,0,0},
     {0,20,0,-20},
     {-20,0,0,0},
   },
};

int type_pos_y [7][4][4] = {

  //第一种情况
  {
    {0,0,20,20},
    {0,0,20,20},
    {0,0,20,20},
    {0,0,20,20},
  },

  //第二个图形
    {

      {0,20,40,40},
      {0,0,0,20},
      {20,0,-20,-20},
      {20,20,20,0},

    },

   //第三个图形
  {
    {0,20,40,40},
    {20,20,20,0},
    {20,0,-20,-20},
    {0,0,0,20},
  },

    //第四个图形
   {
     {0,20,20,40},
     {0,0,20,20},
     {0,20,20,40},
     {0,0,20,20},
   },

    //第5个图形
   {
     {0,20,20,40},
     {20,20,0,0},
     {0,20,20,40},
     {20,20,0,0},
   },
    //第6个图形
   {
     {0,20,40,60},
     {0,0,0,0},
     {0,20,40,60},
     {0,0,0,0},
   },
    //第7个图形
   {
     {0,20,20,20},
     {0,-20,0,20},
     {20,0,0,0},
     {0,20,0,-20},
   },
};

int type_x[7][4] ={
                    {0,0,20,20}
                    ,{0,0,0,20}
                    ,{20,20,20,0}
                    ,{0,0,0,0}
                    ,{0,0,20,20}
                    ,{20,20,0,0}
                    ,{20,0,20,40}
                };
int type_y[7][4] = {
                    {0,20,0,20}
                    ,{0,20,40,40}
                    ,{0,20,40,40}
                    ,{0,20,40,60}
                    ,{0,20,20,40}
                    ,{0,20,20,40}
                    ,{0,20,20,20}
};

MyGroup::MyGroup()
{

}

MyGroup::MyGroup(int pos_x, int pos_y, int type)
{
    Type_rotation = 0;
    Type_style = type;
 
    this->setPos(pos_x,pos_y);
    MyItem * item1 = new MyItem(18,18);
    MyItem * item2 = new MyItem(18,18);
    MyItem * item3 = new MyItem(18,18);
    MyItem * item4 = new MyItem(18,18);
    item1->setPos(pos_x+type_pos_x[type][Type_rotation][0],pos_y+type_pos_y[type][Type_rotation][0]);
    item2->setPos(pos_x+type_pos_x[type][Type_rotation][1],pos_y+type_pos_y[type][Type_rotation][1]);
    item3->setPos(pos_x+type_pos_x[type][Type_rotation][2],pos_y+type_pos_y[type][Type_rotation][2]);
    item4->setPos(pos_x+type_pos_x[type][Type_rotation][3],pos_y+type_pos_y[type][Type_rotation][3]);
    list.append(item1);
    list.append(item2);
    list.append(item3);
    list.append(item4);
}


int MyGroup::isColliding()
{
       QGraphicsItem *item;
       foreach(item, list)
       {
           if(item->collidingItems(Qt::ContainsItemBoundingRect).count()>0)//collidingItems返回与当前item碰撞的子item列表
               return 1;//代表至少有一个item发生了碰撞
       }
       return 0;
}

void MyGroup::zhuangLeft()
{

    Type_rotation++;
    if(Type_rotation == 4)
    {
        Type_rotation = 0;
    }
    qDebug() << type_pos_x[Type_style][Type_rotation][0] << type_pos_x[Type_style][Type_rotation][0] ;
    list.at(0)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][0],pos_y + type_pos_y[Type_style][Type_rotation][0]);
    list.at(1)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][1],pos_y + type_pos_y[Type_style][Type_rotation][1]);
    list.at(2)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][2],pos_y + type_pos_y[Type_style][Type_rotation][2]);
    list.at(3)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][3],pos_y + type_pos_y[Type_style][Type_rotation][3]);

}

void MyGroup::zhuangRigth()
{
    Type_rotation--;
    if(Type_rotation == -1)
    {
        Type_rotation = 3;
    }
    qDebug() << type_pos_x[Type_style][Type_rotation][0] << type_pos_x[Type_style][Type_rotation][0] ;
    list.at(0)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][0],pos_y + type_pos_y[Type_style][Type_rotation][0]);
    list.at(1)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][1],pos_y + type_pos_y[Type_style][Type_rotation][1]);
    list.at(2)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][2],pos_y + type_pos_y[Type_style][Type_rotation][2]);
    list.at(3)->setPos(pos_x + type_pos_x[Type_style][Type_rotation][3],pos_y + type_pos_y[Type_style][Type_rotation][3]);

}

void MyGroup::clear()
{
    list.clear();
}

void MyGroup::setPos(int x, int y)
{
    pos_x = x ;
    pos_y = y ;

}

void MyGroup::moveBy(int x, int y)
{
    pos_x = pos_x + x;
    pos_y = pos_y + y;
    list.at(0)->moveBy(x,y);
    list.at(1)->moveBy(x,y);
    list.at(2)->moveBy(x,y);
    list.at(3)->moveBy(x,y);
}

3.MyScene.h和MyScene.cpp

#include <QGraphicsScene>
#include "myitem.h"
#include <QDebug>
#include <QList>
#include <QMessageBox>
#include <QTimer>
#include <QVector>
#include <QKeyEvent>
#include <QTime>
#include <QtGlobal>
#include "mygroup.h"
#include <QMessageBox>

class MyScene : public QGraphicsScene
{
    Q_OBJECT
public:
    QGraphicsLineItem * lineItem1;
    QGraphicsLineItem * lineItem2;
    QGraphicsLineItem * lineItem3;
    QTimer *timer;
    MyGroup * curGroup;
    MyGroup * nextGroup;
    QGraphicsItem * type_ret[20][10];
    int score;
    int testEnd;
public:
    explicit MyScene(QObject *parent = 0);
    void initScene();
    int IsColliding(); //return 1 yes return 0 no
    void keyPressEvent(QKeyEvent * keyEvent);
    void addGroup(MyGroup * group);
    void creatNewGroup();  //产生一个方块组并且放在场景中
    void setRet(); //方块放置完成记录方块。
    int testRet(int row); //如果有放置之后要检测这一行是不是已经数据完成了,如果成功放回1,否则0
    int destroyFromRet(int row); //把row行的数据删除,然后再把数据全部向下移动
    int moveDownRet(int row);
    void strat();
    void stop();
    void starNewGame();

public slots:
    int moveDownTest(); //return 1 is over;

};

#include "myscene.h"
MyScene::MyScene(QObject *parent) :
    QGraphicsScene(parent)
{
    initScene();
}

//初始化
void MyScene::initScene()
{
    lineItem1 = new QGraphicsLineItem;
    lineItem1->setLine(198,-10,198,400);
    this->addItem(lineItem1);

    lineItem2 = new QGraphicsLineItem;
    lineItem2->setLine(401,-10,401,400);
    this->addItem(lineItem2);

    lineItem3 = new QGraphicsLineItem;
    lineItem3->setLine(201,400,399,400);
    this->addItem(lineItem3);
    this->setSceneRect(0,0,580,410);

    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    int type = qrand();
    type = type % 7;
    this->curGroup = new MyGroup(280,0,type);
    this->addGroup(curGroup);

    type = qrand();
    type = type % 7;
    this->nextGroup = new MyGroup(450,50,type);
    this->addGroup(nextGroup);


    timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(moveDownTest()));

    //设置状态,当前没有一个方块在界面中
    for(int i = 0; i< 20 ;i ++)
    {
        for(int j = 0 ;j < 10 ;j ++)
        {
            type_ret[i][j] = NULL;
        }
    }
    testEnd = 0;
}


//碰撞检测
int MyScene::IsColliding()
{
    QList<QGraphicsItem * > list = this->collidingItems(lineItem1);
    if(list.size() != 0)
    {
        qDebug() << "line 1 "<<endl;
        return 1;
    }
    list = this->collidingItems(lineItem2);
    if(list.size() != 0)
    {
        //this->group->moveUp();
        qDebug() << "line 2 "<<endl;
        return 1;
    }
    list = this->collidingItems(lineItem3);
    if(list.size() != 0)
    {
        qDebug() << "line 3 "<<endl;
        //this->group->moveUp();
        return 1;
    }


    if(this->curGroup->isColliding())
    {
        return 1;
    };
    return 0;
}

//键盘事件
void MyScene::keyPressEvent(QKeyEvent *keyEvent)
{
    if(!timer->isActive())
    {
        return ;
    }
    if(keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_A)
    {
        curGroup->moveBy(-20,0);
        if(this->IsColliding())
        {
            curGroup->moveBy(20,0);
            return ;
        }
    }
    else if(keyEvent->key() == Qt::Key_Right || keyEvent->key() == Qt::Key_D)
    {
        curGroup->moveBy(20,0);
        if(this->IsColliding())
        {
            curGroup->moveBy(-20,0);
            return ;
        }
    }
    else if(keyEvent->key() == Qt::Key_Down || keyEvent->key() == Qt::Key_S)
    {
        curGroup->moveBy(0,20);
        if(this->IsColliding())
        {
            curGroup->moveBy(0,-20);
            return ;
        }
    }
    else if(keyEvent->key() == Qt::Key_Q)
    {
        curGroup->zhuangLeft();
        if(this->IsColliding())
        {
            curGroup->zhuangRigth();
        }
    }
    if(keyEvent->key() == Qt::Key_Space)
    {
        this->testEnd ++ ;
        while(!this->IsColliding())
        {
            curGroup->moveBy(0,20);
        }
        curGroup->moveBy(0,-20);
    }
    if(keyEvent->key() == Qt::Key_P)
    {
        if(timer->isActive())
        {
            timer->stop();

        }
        else
        {
            timer->start(500);
        }
    }
}

//把一组图形放到scene中
void MyScene::addGroup(MyGroup *group)
{
    this->addItem(group->list.at(0));
    this->addItem(group->list.at(1));
    this->addItem(group->list.at(2));
    this->addItem(group->list.at(3));
}

void MyScene::creatNewGroup()
{
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    int type = qrand();
    type = type % 7;
    curGroup = new MyGroup(280,0,nextGroup->Type_style);
    this->removeItem(nextGroup->list.at(0));
    this->removeItem(nextGroup->list.at(1));
    this->removeItem(nextGroup->list.at(2));
    this->removeItem(nextGroup->list.at(3));
    this->nextGroup = new MyGroup(450,50,type);
    this->addGroup(nextGroup);
    this->addGroup(curGroup);
    testEnd = 0;
}

void MyScene::setRet()
{
    //此处为了解决下面移动问题采用一种笨方法
    int row[4];
    int count = 0;
    QGraphicsItem * item ;
     //qDebug() << curGroup->pos_x  << curGroup->pos_y;
    foreach (item, curGroup->list) {
        int i = (item->pos().x() - 200) / 20;
        int j = item->pos().y() / 20;
        qDebug() << i << j ;
        type_ret[j][i] = item;
        row[count] = j;
        count ++ ;
    }
    //从开始遍历,仅仅遍历row中存在的,也就是刚才存放过的
    //如果消除的顺序不是cong'shang倒下就会出现下面的数据无法处理
    for(int i = 0 ; i < 20;i ++ )
    {
        if(testRet(i))
        {
            destroyFromRet(i);
        }
    }


}

int MyScene::testRet(int row)
{
    for(int i = 0 ; i < 10 ; i++)
    {
        if(type_ret[row][i] == NULL)
            return 0;
    }
    return 1;
}

int MyScene::destroyFromRet(int row)
{
    int pos_x = 210;
    int pos_y = row * 20 + 10;
    //删除一行的元素
    for(int i = 0 ; i < 10 ; i++)
    {
        this->removeItem(type_ret[row][i]);
        type_ret[row][i] = NULL ;
    }
    moveDownRet(row);
    return 0;
}

int MyScene::moveDownRet(int row)
{
    for(int i = row - 1 ;i >-1 ; i --)
    {
        for(int j = 0 ; j < 10 ;j ++)
        {
            if(type_ret[i][j])
            {
                type_ret[i][j]->moveBy(0,20);
                type_ret[i+1][j] = type_ret[i][j];
                type_ret[i][j] = NULL;
            }
        }
    }
    return 0;
}

void MyScene::strat()
{
    timer->start(500);
}

void MyScene::stop()
{
    timer->stop();
}

void MyScene::starNewGame()
{
    timer->stop();
    QGraphicsItem * item ;
    foreach(item,curGroup->list)
    {
        this->removeItem(item);
    }

    foreach(item,nextGroup->list)
    {
        this->removeItem(item);
    }

    for(int i = 0 ; i < 20; i ++)
    {
        for(int j = 0 ; j < 10 ; j++)
        {
            if(type_ret[i][j])
            {
                this->removeItem(type_ret[i][j]);
                type_ret[i][j] = NULL;
            }
        }

    }
    qsrand(QTime(0,0,0).secsTo(QTime::currentTime()));
    int type = qrand();
    type = type % 7;
    this->curGroup = new MyGroup(280,0,type);
    this->addGroup(curGroup);

    qDebug() << "clear" <<endl;
    type = qrand();
    type = type % 7;
    this->nextGroup = new MyGroup(450,50,type);
    this->addGroup(nextGroup);
}


//下落的函数
int MyScene::moveDownTest()
{
    if(curGroup == NULL)
    {
        return 0;
    }
    curGroup->moveBy(0,20);
    if(this->IsColliding())
    {
        if(testEnd == 0)
        {
            //比赛结束
            //QMessageBox::warning(this, tr("game over"),tr("GAME OVER
" "start new game"),QMessageBox::Ok);
            qDebug() << "game over";
            this->starNewGame();
            return 0;
        }

        curGroup->moveBy(0,-20);
        setRet();
        curGroup->clear();
        creatNewGroup();
        return 0;
    }
    testEnd ++;
    return 1;
}

4.mainwindow.cpp

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


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    this->setGeometry(0,30,800,420);
    ui->graphicsView->setGeometry(0,0,610,420);
    scene = new MyScene ();
    ui->graphicsView->setScene(scene);
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::on_pushButton_clicked()
{
    scene->strat();
}

void MainWindow::on_pushButton_2_clicked()
{
    scene->stop();
}

需要在ui中添加两个按钮,和要给GraphicsView

有什么问题可以问我。可以从github上下载完整项目

原文地址:https://www.cnblogs.com/qiny1012/p/9029731.html