参加为期一周的比赛培训,所以没有更新博客。还是要好好坚持下去!
此次把一些控件回顾一下,且看我的游戏窗口的代码:
这里的QFrame使用了qt自带的框架组件,并且实现给其命名,而标签和按钮在2D画板学习时已经见过。至于QLCD其实就是一种显示数字的方框,如图#ifndef GAMEVIEW_H #define GAMEVIEW_H #include <QFrame> #include <QWidget> QT_BEGIN_NAMESPACE class QLCDNumber; class QLabel; class QPushButton; QT_END_NAMESPACE class TetrixBoard; class gameView : public QWidget { Q_OBJECT public: gameView(); private: QLabel *createLabel(const QString &text); //游戏框架组件 TetrixBoard *board; //标签 QLabel *nextPieceLabel; //3个LCD数字显示框 QLCDNumber *scoreLcd; QLCDNumber *levelLcd; QLCDNumber *linesLcd; //3个按钮 QPushButton *startButton; QPushButton *quitButton; QPushButton *pauseButton; }; #endif
好,接下来看看具体的组件设定:
#include <QtWidgets>
#include <QIcon>
#include "board.h"
#include "gameview.h"
gameView::gameView()
{
QPalette palette;
this->setAutoFillBackground(true);
palette.setBrush(QPalette::Background,QPixmap(":/background.png"));
this->setPalette(palette);
resize(550, 370);
board = new TetrixBoard;
nextPieceLabel = new QLabel;
nextPieceLabel->setFrameStyle(QFrame::Box | QFrame::Raised);//设置框架为凸起的小盒子式
nextPieceLabel->setAlignment(Qt::AlignCenter);
board->setNextPieceLabel(nextPieceLabel);
scoreLcd = new QLCDNumber(5);//分数显示5位数
scoreLcd->setSegmentStyle(QLCDNumber::Filled);
levelLcd = new QLCDNumber(2);//等级显示2位数
levelLcd->setSegmentStyle(QLCDNumber::Filled);
linesLcd = new QLCDNumber(5);//行数显示为5位数
linesLcd->setSegmentStyle(QLCDNumber::Filled);
//按钮与焦点的设置,全不设置焦点
startButton = new QPushButton(tr("&开始"));
startButton->setFocusPolicy(Qt::NoFocus);
quitButton = new QPushButton(tr("&退出"));
quitButton->setFocusPolicy(Qt::NoFocus);
pauseButton = new QPushButton(tr("&暂停"));
pauseButton->setFocusPolicy(Qt::NoFocus);
//将信号与槽连接
connect(startButton, SIGNAL(clicked()), board, SLOT(start()));
connect(quitButton , SIGNAL(clicked()), qApp, SLOT(quit()));
connect(pauseButton, SIGNAL(clicked()), board, SLOT(pause()));
connect(board, SIGNAL(scoreChanged(int)), scoreLcd, SLOT(display(int)));
connect(board, SIGNAL(levelChanged(int)), levelLcd, SLOT(display(int)));
connect(board, SIGNAL(linesRemovedChanged(int)),
linesLcd, SLOT(display(int)));
//addWidget(QWidget*,int row,int column,int rowStretch,int columnStretch,Qt::Alignment),
//row表示行,column表示列,rowStretch表示行方向占据的宽度,columnStretch表示列方向占的宽度,Qt::Alignment表示对齐方式,默认为0
//进行布局
QGridLayout *layout = new QGridLayout;
layout->addWidget(createLabel(tr("下一个")), 0, 1);
layout->addWidget(nextPieceLabel, 1, 1);
layout->addWidget(createLabel(tr("级别")), 2, 1);
layout->addWidget(levelLcd, 3, 1);
layout->addWidget(startButton, 4, 1);
layout->addWidget(board, 0, 0, 6, 1);
layout->addWidget(createLabel(tr("分数")), 0, 2);
layout->addWidget(scoreLcd, 1, 2);
layout->addWidget(createLabel(tr("已满行数")), 2, 2);
layout->addWidget(createLabel(tr("按键说明:\nD键:方块加速坠落\nSpace键:实现一键坠落\n方向键向上/下:\n使方块分别顺时针\n/逆时针旋转90度\n方向键向左/右:\n使方块分别向左右移动")),5,1);
layout->addWidget(linesLcd, 3, 2);
layout->addWidget(quitButton, 4, 2);
layout->addWidget(pauseButton, 5, 2);
setLayout(layout);
setWindowTitle(tr("我的俄罗斯方块"));
setWindowIcon(QIcon(":/box.gif"));
}
//重写一个创建新标签的函数
QLabel *gameView::createLabel(const QString &text)
{
QLabel *lbl = new QLabel(text);
//设置对齐方式为中心对齐或者下平齐
lbl->setAlignment(Qt::AlignHCenter | Qt::AlignBottom);
return lbl;
}
展示的还是比较清楚了,就不再赘述。而接下来要说的应该也是俄罗斯方块里最核心的部分——实现消行。
我们要达到的就是判定一行满后就消掉。这里我写了两个函数:
void removeFullLines();
void newPiece();
这两部分具体实现:
void TetrixBoard::removeFullLines() {//设定默认满行数为0 int numFullLines = 0; for (int i = BoardHeight - 1; i >= 0; --i) { bool lineIsFull = true; for (int j = 0; j < BoardWidth; ++j) { if (shapeAt(j, i) == NoShape) { lineIsFull = false; break; } } //若仍有满行则增加满行的个数,同时销毁掉这些行数,将其上的各行下移 if (lineIsFull) { ++numFullLines; for (int k = i; k < BoardHeight - 1; ++k) { for (int j = 0; j < BoardWidth; ++j) shapeAt(j, k) = shapeAt(j, k + 1); } for (int j = 0; j < BoardWidth; ++j) shapeAt(j, BoardHeight - 1) = NoShape; } } //每消去一行分数加上10 if (numFullLines > 0) { numLinesRemoved += numFullLines; score += 10 * numFullLines; emit linesRemovedChanged(numLinesRemoved); emit scoreChanged(score); timer.start(500, this); isWaitingAfterLine = true; curPiece.setShape(NoShape); update(); } } //初始化新出现的方块 void TetrixBoard::newPiece() { curPiece = nextPiece; nextPiece.setRandomShape(); showNextPiece(); curX = BoardWidth / 2 + 1; curY = BoardHeight - 1 + curPiece.minY(); if (!tryMove(curPiece, curX, curY)) { curPiece.setShape(NoShape); timer.stop(); isStarted = false; } }
是这样的我们已知游戏的窗口的高度,通过遍历我们可以知道每一行的每个方块的区域是否存在没有小方块的部分。若有这种部分,那么我们就认定这行是不满的,就不动。
若检测到没有这种区域存在,就让标兵numfullLines自加,从而认定这行需要消掉。这样之后,我们做的处理是将其上的到最高行的全部下移替代下面的各行。
而这两行:
for (int j = 0; j < BoardWidth; ++j)
shapeAt(j, BoardHeight - 1) = NoShape;
代表:我们要保证最上面一行没有形状,给接下来的产生的形状留一个空间,当然若新形状不能下移,游戏自然结束了!