zoukankan      html  css  js  c++  java
  • 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上下载完整项目

  • 相关阅读:
    爬虫防止浏览器防止debug处理
    php
    代码日记
    Linux定时任务
    Linux 三剑客(Awk、Sed、Grep)
    golang中使用ETCD
    golang中使用mysql数据库
    golang中使用Redis
    Linux进阶知识和命令
    Linux核心命令使用方法
  • 原文地址:https://www.cnblogs.com/qiny1012/p/9029731.html
Copyright © 2011-2022 走看看