zoukankan      html  css  js  c++  java
  • 俄罗斯方块 Tetris

    今天,为大家带来一个用Qt C++ (Windows环境下)做的一个简易俄罗斯方块小游戏

    思路和模块介绍都在注释里面,其次就是一些项目中遇到的问题以及解决方案,在后面部分说明。

    一、效果

    测试图样

    Qt中文显示不容易啊~

    二、代码

    Tetris.pro

    #-------------------------------------------------
    #
    # Project created by QtCreator 2018-02-11T18:16:14
    #
    #-------------------------------------------------
    
    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    TARGET = Tetris
    TEMPLATE = app
    
    DEFINES += QT_DEPRECATED_WARNINGS
    
    SOURCES  = main.cpp
               tetriswindow.cpp 
               tetrisitem.cpp 
               tetrisboard.cpp
    
    HEADERS  = tetriswindow.h 
               tetrisitem.h 
               tetrisboard.h
    View Code

    tetrisitem.h

    #ifndef TETRISITEM_H
    #define TETRISITEM_H
    
    /*******************************************************
    //! [ TertrisItem 类 ]
    //! [ 这个类主要是俄罗斯方块的方块元素类 设置方块元素的一些属性 ]
    //! [ 设置方块的形状  方块的坐标  方块的生成   方块的旋转等    ]
    //! [ 方块元素坐标化  以便于之后做处理(定位、显示、绘图等)    ]
    ********************************************************/
    
    //! [总共是20种形状,NoShape在内总共6大类,同类型的当然可以通过旋转互相转换]
    //! [但是为了让初始掉落的时候每个种类的形状都能概率性出现,还是把所有的形状都列了出来]
    //! [在之后的绘图等后续工作中还是按6大类进行处理的,只有这里需要细分一下]
    
    enum ItemShape
    {
        NoShape,    SquareShape,    Line1Shape,    Line2Shape,
        L1Shape,    L2Shape,    L3Shape,    L4Shape,
        L5Shape,    L6Shape,    L7Shape,    L8Shape,
        Z1Shape,    Z2Shape,    Z3Shape,    Z4Shape,
        T1Shape,    T2Shape,    T3Shape,    T4Shape,
    };
    
    class TetrisItem
    {
    private:
        //! [data-Member]
        static const int coordList[20][4][2];      //! [方块形状总坐标点列表]
        ItemShape m_Shape;                         //! [方块的形状属性]
        int coordinate[4][2];                      //! [方块对应的坐标点列表]
    
        //! [Member-functions]
        void resetX(const int Index, const int x){ coordinate[Index][0] = x; }
        void resetY(const int Index, const int y){ coordinate[Index][1] = y; }
    
    public:
        explicit TetrisItem( const ItemShape& shape = NoShape ){ resetShape(shape); }
    
        //! [set-Shape-functions]
        void resetShape(const ItemShape&);          //! [重置方块属性]
        void setRandomShape();                      //! [生成随机方块]
        const TetrisItem Rotate()const;             //! [方块元素旋转]
    
        //! [get-Data-functions]
        const ItemShape& get_Shpae() const { return m_Shape; }
        const int get_x(const int Index) const { return coordinate[Index][0]; }
        const int get_y(const int Index) const { return coordinate[Index][1]; }
        const int get_most(const bool isMax = true, const bool x = true)const;   //! [注释见下方]
        //! [求取坐标最值,比如:最大x或最小的y坐标值等,4行代码就ok]
    
    };
    
    #endif // TETRISITEM_H
    View Code

    tetrisitem.cpp

    #include <QtCore>
    #include "tetrisitem.h"
    
    const int TetrisItem::coordList[20][4][2] =
    {
        { {  0,  0 }, {  0,  0 }, {  0,  0 }, {  0,  0 } }    //! [ NoShape ]
    ,   { {  0,  0 }, {  1,  0 }, {  0,  1 }, {  1,  1 } }    //! [ SquareShape ]
    ,   { {  0,  2 }, {  0,  1 }, {  0,  0 }, {  0, -1 } }    //! [ LineShape]
    ,   { {  2,  0 }, {  1,  0 }, {  0,  0 }, { -1,  0 } }
    ,   { {  0, -1 }, { -1, -1 }, { -1,  0 }, { -1,  1 } }    //! [ LShape ]
    ,   { {  0, -1 }, {  1, -1 }, {  1,  0 }, {  1,  1 } }
    ,   { { -1,  0 }, { -1, -1 }, {  0, -1 }, {  1, -1 } }
    ,   { { -1, -1 }, {  0, -1 }, {  1, -1 }, {  1,  0 } }
    ,   { { -1, -1 }, { -1,  0 }, { -1,  1 }, {  0,  1 } }
    ,   { {  0,  1 }, {  1,  1 }, {  1,  0 }, {  1, -1 } }
    ,   { { -1,  1 }, { -1,  0 }, {  0,  1 }, {  1,  1 } }
    ,   { { -1,  1 }, {  0,  1 }, {  1,  1 }, {  1,  0 } }
    ,   { {  0,  1 }, {  0,  0 }, {  1,  0 }, {  1, -1 } }     //! [ ZShape ]
    ,   { {  0,  1 }, {  0,  0 }, { -1,  0 }, { -1, -1 } }
    ,   { { -1,  1 }, {  0,  1 }, {  0,  0 }, {  1,  0 } }
    ,   { { -1,  0 }, {  0,  0 }, {  0,  1 }, {  1,  1 } }
    ,   { { -1,  0 }, {  0,  0 }, {  0,  1 }, {  1,  0 } }     //! [ TShape ]
    ,   { {  0,  1 }, {  0,  0 }, {  1,  0 }, {  0, -1 } }
    ,   { {  1,  0 }, {  0,  0 }, {  0, -1 }, { -1,  0 } }
    ,   { {  0, -1 }, {  0,  0 }, { -1,  0 }, {  0,  1 } }
    };
    
    //////////////////////////////////////////////
    void TetrisItem::resetShape(const ItemShape& shape)
    {
        for(int i = 0; i < 4; ++i)
        {
            coordinate[i][0] = coordList[shape][i][0];
            coordinate[i][1] = coordList[shape][i][1];
        }
        m_Shape = shape;
    }
    
    //////////////////////////////////////////////
    void TetrisItem::setRandomShape()
    {
        resetShape(ItemShape(qrand() % 19 + 1));
    }
    
    //////////////////////////////////////////////
    const TetrisItem TetrisItem::Rotate()const
    {
        if(m_Shape == SquareShape)               //! [如果是田字方块,就无需旋转]
            return*this;
    
        TetrisItem item(m_Shape);
        for(int i = 0; i < 4; ++i)
        {                                      //! [方块元素旋转90°]
            item.resetX(i, get_y(i));
            item.resetY(i, -1 * get_x(i));
        }
        return item;
    }
    
    //////////////////////////////////////////////
    const int TetrisItem::get_most(const bool IsMax, const bool x)const
    {
        int value = coordinate[0][!x];
        for(int i = 1; i < 4; ++i)
            value = IsMax ? qMax(value, coordinate[i][!x]) : qMin(value, coordinate[i][!x]);
        return value;
    }
    View Code

    tetrisboard.h

    #ifndef TETRISBOARD_H
    #define TETRISBOARD_H
    
    /******************************************
    //! [         TetrisBoard 类              ]
    //! [     面板类     设定面板相关的属性       ]
    //! [  面板上的事件响应设定   信号响应设定     ]
    //! [  面板的绘图  事件响应设定  方块移动 等   ]
    //! [  设定面板应该具有的一些数据             ]
    //! [难度等级的改变是随着方块的下降的数量而改变的]
    ******************************************/
    
    #include <QFrame>
    #include <QPointer>
    #include <QBasicTimer>
    #include "tetrisitem.h"
    
    QT_BEGIN_NAMESPACE
    class QLabel;
    class QFont;
    class QColor;
    QT_END_NAMESPACE
    
    class TetrisBoard : public QFrame
    {
        Q_OBJECT
    
        //! [信号-槽]
    public slots:
        void SLOT_start();                    //! [游戏开始设置]
        void SLOT_pause();                    //! [游戏暂停设置]
        void SLOT_reset();                    //! [重新开始设置]
    
    signals:
        void score_change(const int);                //! [改变分数]
        void level_change(const int);                //! [改变等级]
        void Remove_line_change(const int);         //! [改变已消除行数]
        //! [信号-槽 END]
    
    private:
        //! [Data-Member]
        static const int Board_W{ 10 }, Board_H{ 22 }; //! [面板的宽和高]
        static const QColor colorList[6];           //! [6类方块的颜色列表]
        QBasicTimer m_timer;                        //! [ 计时器 ]
        QPointer<QLabel> m_nextItem_L;              //! [QPointer模板类似智能指针]
        TetrisItem currentItem, nextItem;           //! [当前方块  下一个方块]
        bool IsStart, IsPause;                      //! [暂停、开始 与否]
        bool IsFall;                                //! [是否已经落下]
        int currentX, currentY;                     //! [当前的x,y]
        int Lines_moved_num, Item_Fall_num;         //! [消去的行数  下落的方块数]
        int score, level;                           //! [分数  等级]
    
        ItemShape m_board[Board_W * Board_H];       //! [注释见下方]
        //! [此为俄罗斯方块活动的区域中每一个小格子所属方块类型的数组]
    
        //! [ Member-functions ]      [ Inline-functions ]
        ItemShape& Item_type(const int x,const int y) //! [获取(x,y)的方块类型]
        { return m_board[y * Board_W + x]; }
    
        const int TimeOut()const                      //! [设定计时器的流逝速度]
        { return 1000/(1 + level); }
    
        const int grid_W()const                       //! [求划分的一个小格子的宽]
        { return contentsRect().width()/Board_W; }    //! [con..ect()函数返回面板矩形]
    
        const int grid_H()const
        { return contentsRect().height()/Board_H; }
    
        void clearBoard();                        //! [清空面板(将所有小格子的方块类型置0)]
        void Fall();                              //! [瞬降]
        void down();                              //! [下落]
        void RemoveLine();                        //! [消除一行]
        void Fall_after(const int);               //! [落定之后的数据更新]
        void newItem();                           //! [构建下一个俄罗斯方块]
        void showNext();                          //! [展示下一个方块]
        bool Move_(const TetrisItem&,const int,const int);           //! [移动]
        void draw(QPainter&,const int,const int,const ItemShape&);   //! [描绘小格子]
    
    protected:                                        //! [三个事件]
        void paintEvent(QPaintEvent *)Q_DECL_OVERRIDE;
        void keyPressEvent(QKeyEvent*)Q_DECL_OVERRIDE;
        void timerEvent(QTimerEvent *)Q_DECL_OVERRIDE;
    
    public:
        TetrisBoard(QWidget* parent = 0);
        void setNextItem_L(QLabel*);                  //! [设定标签,用于显示下一个方块的标签]
        QPointer<QLabel> m_Pause_L;                   //! [显示暂停的Label]
    
    };
    
    #endif // TETRISBOARD_H
    View Code

    tetrisboard.cpp

    #include <QtWidgets>
    #include "tetrisboard.h"
    
    const QColor TetrisBoard::colorList[6] =
    {
        QColor(255, 255, 255)
    ,   QColor(128,  36, 221)
    ,   QColor( 56, 210,  40)
    ,   QColor(152, 165,   6)
    ,   QColor(223,  22,  22)
    ,   QColor( 37, 237, 244)
    };
    
    //////////////////////////////////////////////
    TetrisBoard::TetrisBoard(QWidget* parent)
        :QFrame(parent)                   //! [构建基类]
    ,    IsStart(false)
    ,    IsPause(false)
    ,    IsFall(false)
    {
        setLineWidth(2);                  //! [边框设定参见QFrame类]
        setMidLineWidth(3);
        setFrameStyle(QFrame::Box | QFrame::Raised);   //! [三个函数用于设定边框]
        setFocusPolicy(Qt::StrongFocus);  //! [设定焦点策略]
    
        clearBoard();                     //! [清空游戏面板]
        nextItem.setRandomShape();        //! [随机生成下一个方块]
    
        //! [下面是暂停标签的设置]
        m_Pause_L = new QLabel("Pause",this);
        m_Pause_L->setAlignment(Qt::AlignHCenter);
        QFont* font = new QFont;          //! [创建文字]
        font->setPointSize(50);           //! [设定字体大小]
        QPalette p;                       //! [调色板]
        p.setColor(QPalette::WindowText,Qt::red);  //! [置调色板的颜色]
    
        m_Pause_L->setPalette(p);         //! [设置标签的调色和文字]
        m_Pause_L->setFont(*font);
        m_Pause_L->setVisible(false);     //! [设置标签可见性,暂停时可见]
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::setNextItem_L(QLabel* label)
    {
        m_nextItem_L = label;
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::SLOT_start()
    {
        if(IsPause||IsStart) return;     //! [如果游戏已经开始或者暂停,则该按钮无效]
    
        IsStart = true;                  //! [游戏开始时的状态数据初始化]
        IsFall = false;
        level = 1;
        score = Item_Fall_num = Lines_moved_num = 0;
        clearBoard();
    
        //! [发射信号]
        emit Remove_line_change(Lines_moved_num);
        emit score_change(score);
        emit level_change(level);
    
        newItem();                        //! [生成新的方块]
        m_timer.start(TimeOut(),this);    //! [时间重新开始,按照para1 毫秒的速度流逝]
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::SLOT_reset()        //! [重新开始,需要将开始和暂停置为false,然后执行start]
    {
        IsStart = false;
        IsPause = false;
        SLOT_start();
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::SLOT_pause()
    {
        if(!IsStart) return;               //! [游戏未开始,无效]
    
        IsPause = !IsPause;
        if(IsPause)
        {
            m_timer.stop();
            m_Pause_L->setVisible(true);
        }
        else
        {
            m_timer.start(TimeOut(),this);
            m_Pause_L->setVisible(false);
        }
    
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::paintEvent(QPaintEvent *event)
    {
        QFrame::paintEvent(event);         //! [先调用基类的]
        QPainter painter(this);            //! [绘图类]
        QRect rect = contentsRect();       //! [矩形类,该函数在头文件中已介绍过]
    
        int boardTop = rect.bottom() - Board_H * grid_H();
        for(int i = 0; i < Board_H; ++i)
            for(int j = 0; j < Board_W; ++j)
            {
                ItemShape shape = Item_type(j, Board_H - i - 1);
                if(shape != NoShape)
                    draw(painter, rect.left() + j * grid_W(), boardTop + i * grid_H(), shape);
            }
    
        if(currentItem.get_Shpae() != NoShape)
            for(int i = 0; i < 4; ++i)
            {
                int x = currentX + currentItem.get_x(i);
                int y = currentY - currentItem.get_y(i);
                draw(painter, rect.left() + x * grid_W(),
                     boardTop + (Board_H - y - 1) * grid_H(), currentItem.get_Shpae());
            }
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::keyPressEvent(QKeyEvent *event)
    {
        if(!IsStart || IsPause || currentItem.get_Shpae()  == NoShape)
        {
            QFrame::keyPressEvent(event);
            return;
        }
        switch(event->key())             //! [判别键盘按键]
        {
        case Qt::Key_Left:
        case Qt::Key_A:
            Move_(currentItem, currentX - 1, currentY);
            break;
        case Qt::Key_Right:
        case Qt::Key_D:
            Move_(currentItem, currentX + 1, currentY);
            break;
        case Qt::Key_Up:
        case Qt::Key_W:
            Move_(currentItem.Rotate(), currentX, currentY);
            break;
        case Qt::Key_Down:
        case Qt::Key_S:
            Fall();
            break;
        default:
            QFrame::keyPressEvent(event);
        }
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::timerEvent(QTimerEvent *event)
    {
        if(event->timerId() == m_timer.timerId())  //! [一个时间单位一个时间单位对应刷新相关的设置]
            if(IsFall)                             //! [如果某一时刻的方块已经落下,那么重新生成一个]
            {
                IsFall = false;
                newItem();
                m_timer.start(TimeOut(),this);
            }
            else down();
        else QFrame::timerEvent(event);
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::clearBoard()
    {
        for(int i = 0; i < Board_H * Board_W; ++i)
            m_board[i] = NoShape;
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::Fall()
    {
        int fall_height = 0, y = currentY;
        while(y > 0)
        {
            if(!Move_(currentItem, currentX, y - 1))
                break;
            --y;
            ++fall_height;
        }
        Fall_after(fall_height);
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::down()
    {
        if(!Move_(currentItem, currentX, currentY - 1))
            Fall_after(0);
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::Fall_after(const int fall_height)
    {
        for(int i = 0; i < 4; ++i)               //! [刷新面板上对应位置的方块类型属性]
        {
            int x = currentX + currentItem.get_x(i);
            int y = currentY - currentItem.get_y(i);
            Item_type(x, y) = currentItem.get_Shpae();
        }
    
        ++Item_Fall_num;
        if(Item_Fall_num % 28 == 0)              //! [如果没28个提升一次等级]
        {
            ++level;
            m_timer.start(TimeOut(), this);      //! [等级提升,刷新时间流逝速度]
            emit level_change(level);
        }
    
        score += fall_height + 8;
        emit score_change(score);
        RemoveLine();
    
        if(!IsFall)
            newItem();
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::RemoveLine()
    {
        int Num_remove = 0;
        for(int i = Board_H - 1; i >= 0; --i)
        {
            bool line_Is_Full = true;
    
            for(int j = 0; j < Board_W; ++j)
                if(Item_type(j, i) == NoShape)
                {
                    line_Is_Full = false;
                    break;
                }
    
            if(line_Is_Full)
            {
                ++Num_remove;
                for(int k = i; k < Board_H - 1; ++k)
                    for(int j = 0; j < Board_W; ++j)
                        Item_type(j, k) = Item_type(j, k + 1);
    
                for(int L = 0; L < Board_W; ++L)
                    Item_type(L, Board_H - 1) = NoShape;
            }
        }
    
        if(Num_remove > 0)
        {
            Lines_moved_num += Num_remove;
            score += 13 * Num_remove;
            emit Remove_line_change(Lines_moved_num);
            emit score_change(score);
    
            IsFall = true;
            currentItem.resetShape(NoShape);
            update();
        }
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::newItem()
    {
        currentItem = nextItem;
        nextItem.setRandomShape();
        showNext();
        currentX = Board_W / 2 + 1;
        currentY = Board_H - 1 + currentItem.get_most(false, false);
    
        if(!Move_(currentItem, currentX, currentY))
        {
            currentItem.resetShape(NoShape);
            m_timer.stop();
            IsStart = false;
        }
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::showNext()
    {
        if(!m_nextItem_L)
            return;
    
        int δx = nextItem.get_most(true, true) - nextItem.get_most(false, true) + 1;
        int δy = nextItem.get_most(true, false) - nextItem.get_most(false, false) + 1;
    
        QPixmap pixmap(δx * grid_W(), δy * grid_H());
        QPainter painter(&pixmap);
        painter.fillRect(pixmap.rect(), m_nextItem_L->palette().background());
    
        for(int i = 0; i < 4; ++i)
        {
            int x = nextItem.get_x(i) - nextItem.get_most(false,true);
            int y = nextItem.get_y(i) - nextItem.get_most(false,false);
            draw(painter, x * grid_W(), y * grid_H(),nextItem.get_Shpae());
        }
        m_nextItem_L->setPixmap(pixmap);
    }
    
    //////////////////////////////////////////////
    bool TetrisBoard::Move_(const TetrisItem& item, const int X, const int Y)
    {
        for(int i = 0; i < 4; ++i)
        {
            int x = X + item.get_x(i);
            int y = Y - item.get_y(i);
            if(x < 0 || x >= Board_W || y < 0 || y >= Board_H)  //! [超过边界返回false]
                return false;
            if(Item_type(x, y) != NoShape)                      //! [如果下一个位置不为空,返回false]
                return false;
        }
    
        currentItem = item;
        currentX = X;
        currentY = Y;
        update();
        return true;
    }
    
    //////////////////////////////////////////////
    void TetrisBoard::draw(QPainter& painter, const int X, const int Y, const ItemShape& shape)
    {
        QColor color;
        if(1 >= shape)             color = colorList[shape];
        else if(3 >= shape)        color = colorList[2];
        else if(11 >= shape)       color = colorList[3];
        else if(15 >= shape)       color = colorList[4];
        else color = colorList[5];
    
        painter.fillRect(X + 1, Y + 1, grid_W() - 2, grid_H() - 2, color);
        painter.setPen(color.light());
        painter.drawLine(X, Y + grid_H() - 1, X, Y);
        painter.drawLine(X, Y,X + grid_W() - 1, Y);
    
        painter.setPen(color.dark());
        painter.drawLine(X + 1, Y + grid_H() - 1, X + grid_W() - 1, Y + grid_H() - 1);
        painter.drawLine(X + grid_W() - 1, Y + grid_H() - 1,  X + grid_W() - 1, Y + 1);
    
    }
    View Code

    tetriswindow.h

    #ifndef TETRISWINDOW_H
    #define TETRISWINDOW_H
    
    /**************************************
    //! [ TetrisWindow 类 ]
    //! [   窗口显示控制    ]
    //! [ 设置布局 信号-槽响应设定 LCD显示 等 ]
    ***************************************/
    
    #include <QWidget>
    
    QT_BEGIN_NAMESPACE
    class QFrame;
    class QString;
    class QLCDNumber;
    class QLabel;
    class QPushButton;
    class TetrisBoard;
    QT_END_NAMESPACE
    
    
    class TetrisWindow : public QWidget
    {
        Q_OBJECT
    private:
        QLabel* newLabel(const QString&);      //! [创建标签]
    
        QLabel* user_State;                    //! [使用说明]
        TetrisBoard* board;
        QLabel* nextItem_L;
        QLCDNumber* score_lcd, *level_lcd, *line_lcd;   //! [LCD数字显示数据]
        QPushButton* start_btn, *restart_btn, *quit_btn, *pause_btn;
    
    public:
        TetrisWindow();
        void set_UserState();
    
    };
    
    #endif // TETRISWINDOW_H
    View Code

    tetriswindow.cpp

    #include <QtWidgets>
    
    #include "tetriswindow.h"
    #include "tetrisboard.h"
    
    TetrisWindow::TetrisWindow()
    {
        set_UserState();
    
        board = new TetrisBoard;
    
        nextItem_L = new QLabel;
        nextItem_L->setLineWidth(3);         //! [以下三行仍然是边框格式设置,参见QFrame类]
        nextItem_L->setMidLineWidth(2);
        nextItem_L->setFrameStyle(QFrame::Panel | QFrame::Sunken);
        nextItem_L->setAlignment(Qt::AlignCenter);
        board->setNextItem_L(nextItem_L);
    
        score_lcd = new QLCDNumber(5);      //! [设定初始LCD数字的位数]
        level_lcd = new QLCDNumber(2);
        line_lcd = new QLCDNumber(3);
    
        score_lcd->setSegmentStyle(QLCDNumber::Flat);
        level_lcd->setSegmentStyle(QLCDNumber::Filled);
        line_lcd->setSegmentStyle(QLCDNumber::Filled);
    
        start_btn = new QPushButton(tr("Start"));
        restart_btn = new QPushButton(tr("Restart"));
        pause_btn = new QPushButton(tr("Pause"));
        quit_btn = new QPushButton(tr("Quit"));
    
        //! [按钮的焦点策略设定为无,不然的话点击开始,按方向键只会使焦点在各个按钮之间流动,而不是进行操作]
        start_btn->setFocusPolicy(Qt::NoFocus);
        restart_btn->setFocusPolicy(Qt::NoFocus);
        pause_btn->setFocusPolicy(Qt::NoFocus);
        quit_btn->setFocusPolicy(Qt::NoFocus);
    
        //! [信号-槽]
        connect(start_btn, SIGNAL(clicked()), board, SLOT(SLOT_start()));
        connect(restart_btn, SIGNAL(clicked()), board, SLOT(SLOT_reset()));
        connect(pause_btn, SIGNAL(clicked()),board,SLOT(SLOT_pause()));
        connect(quit_btn, SIGNAL(clicked()), qApp, SLOT(quit()));
        connect(board, SIGNAL(score_change(int)),score_lcd, SLOT(display(int)));
        connect(board, SIGNAL(level_change(int)),level_lcd, SLOT(display(int)));
        connect(board, SIGNAL(Remove_line_change(int)),line_lcd, SLOT(display(int)));
    
        //! [布局]
        QGridLayout* layout = new QGridLayout;
        layout->addWidget(newLabel(tr("Next")), 0, 0, 1, 2);
        layout->addWidget(nextItem_L, 1, 0, 2, 2);
        layout->addWidget(newLabel(tr("等级")),3,0,1,2);
        layout->addWidget(level_lcd,4,0,2,2);
        layout->addWidget(newLabel(tr("消失行数 ")), 6, 0, 1, 2);
        layout->addWidget(line_lcd, 7, 0, 2, 2);
        layout->addWidget(newLabel("TETRIS   GAME"),0,2,1,2);
        layout->addWidget(board, 1, 2, 8, 2);
        layout->addWidget(board->m_Pause_L, 4, 2, 1, 2);
        layout->addWidget(user_State, 0, 4, 4, 2);
        layout->addWidget(newLabel(tr("分数")), 3, 4, 1, 2);
        layout->addWidget(score_lcd, 4, 4, 2, 2);
        layout->addWidget(start_btn, 7, 4);
        layout->addWidget(pause_btn, 7, 5);
        layout->addWidget(restart_btn, 8, 4);
        layout->addWidget(quit_btn, 8, 5);
        setLayout(layout);
    
        setWindowTitle("Tetris_Lv.");
        resize(700,450);
    }
    
    void TetrisWindow::set_UserState()
    {
        QString State = tr("
    #···· 游戏使用说明 ····#
    
    #·方向键Up/W:   变换形状     \**/
    
    #·方向键Down/S: 瞬降         \**/
    
    #·方向键Left/A: 左移,长按加速**/
    
    #·方向键Right/D:右移,长按加速**/");
        user_State = new QLabel(State);
        user_State->setAlignment(Qt::AlignLeft);    //! [左对齐]
    }
    
    QLabel* TetrisWindow::newLabel(const QString & label)
    {
        QLabel* lab = new QLabel(label);
        lab->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
        return lab;
    }
    View Code

    main.cpp

    #include <QtWidgets>
    
    #include "tetriswindow.h"
    
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
        TetrisWindow w;
        w.show();
        return app.exec();
    }
    View Code

    三、过程中的一些问题

    1)中文注释的问题

    Qt的中文注释有时候会引发编译错误,有时即使编译通过了,执行的时候也会出现一些预料之外的结果,这时候,如果代码逻辑没有问题,那就得考虑是否是中文注释引发的。

    最近终于找到一种注释中文的“官方”形式, 即  //! [ ]  ,我这样写没有出现什么异样。

    2)error LNK2001

    采用 信号-槽 机制的时候,signals信号函数是只有声明,信号由moc自动产生,是无需cpp实现的,必须加上Q_OBJECT宏定义,如下:  

     不然信号函数名会成为无法解析的外部符号   error LNK 2019

    这属于链接错误,没有生成对应的moc文件:比如上述,就没有生成tetrisboard相关的moc文件

    链接正确,构建后会生成生成对应的moc文件:

    出现这种错误还可能因为后面新加了一些信号-槽等

    解决方案:

    1> 将文件中的Debug文件删除,重新构建。

    2>创一个新的.h和.cpp文件,复制原来内容,将原来的文件从工程中删掉,重新构建(编译)

    3>重新创建工程

    了解到的就这些了。

    3)inline关键字会导致无法解析的外部符号??

    类中的内联函数在类外定义需要加上inline关键字,但是Qt 中会出现错误??

    我们来看一下下面这个代码:

    class TetrisItem
    {
    private:
        int m_shape;
    public:
        void setRandomShape();                     
    };

    上面是头文件的类声明

    我们在对应的cpp中实现内联函数

    inline void TetrisItems::setRandomShape()
    {
        m_shape = qrand() % 19 + 1;
    }

    如果该函数没有被其他的cpp文件调用,那么这个是没问题的。

    但是,如果被其他的cpp函数调用了该内联函数,就会出现无法解析的外部符号。

    那么该怎么验证这个呢,那我们俄罗斯方块的一个类中的内联函数来做一个测试

    此时如果要inline关键字类外实现内联必须声明在对应的.h文件中才行,要么就去掉inline关键字

    该项目工程中遇到的问题大概就这么多了。

    谢谢您的阅读,生活愉快~

  • 相关阅读:
    序列化
    restfull风格,规范的项目,django-DRF与django的区别
    JWT的应用
    七牛云上传文件
    微博三方登录
    celery原理与使用
    Redis五大数据类型
    图片验证码
    单点登录
    Django Rest Framework JWT
  • 原文地址:https://www.cnblogs.com/lv-anchoret/p/8446828.html
Copyright © 2011-2022 走看看