今天,为大家带来一个用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
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
tetrisitem.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); }
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
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); }
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
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("\n#···· 游戏使用说明 ····#\n\n#·方向键Up/W: 变换形状 \\**/\n\n#·方向键Down/S: 瞬降 \\**/\n\n#·方向键Left/A: 左移,长按加速\**/\n\n#·方向键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; }
main.cpp
#include <QtWidgets> #include "tetriswindow.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); TetrisWindow w; w.show(); return app.exec(); }
三、过程中的一些问题
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关键字
该项目工程中遇到的问题大概就这么多了。
谢谢您的阅读,生活愉快~