方块游戏架构

  在这个游戏中,有一个区域用来摆放方块,该区域宽为10,高为20,以小正方形为单位,它可以看作是拥有20行10列的一个网格。标准的游戏中一共有7种方块,它们都是由4个小正方形组成的规则图形,依据形状分别用字母I、J、L、O、S、T和Z来命名。
  这里使用图形视图框架来实现整个游戏的设计。小正方形由OneBox来表示,它继承自QGraphicsObject类,之所以继承自这个类,是因为这样就可以使用信号和槽机制,话可以使用属性动画。小正方形就是一个宽和高都为20像素的正方形图形项。游戏中的方块游戏由方块组BoxGroup类来实现,继承自QObject和QGraphicsItemGroup类,这样该类也可以使用信号和槽机制。方块组是一个宽和高都是80像素的图形项组,其中包含了4个小方块,通过设置小方块的位置来实现7种标准的方块图形。它们的形状和位置如下图,在BoxGroup类中实现了方块图形的创建、移动和碰撞检测。
Qt实战(二)——方块游戏-编程之家
  方块移动区域在游戏场景中使用四条直线图形项所围成区域来表示,之所以要这样实现,是因为这样可以通过方块组是否与直线图形项碰撞来检测是否移动出界。整个游戏界面由MyView类来实现,该类继承自QGraphicsView类,实现了场景设置、游戏逻辑设计和游戏声音设置及其他控制功能。整个游戏场景宽800像素,高500像素。方块移动区域宽200像素,高400像素,纵向每20个像素被视作一行,共有20行;横行也是每20个像素视作一列,所以共有10列,该区域可以看作一个由20行10列20×20像素的方格组成的网格。方块组在方块移动区域的初始位置为上方正中间,但方块组的最上方一行小正方形在方块移动区域以外,这样可以保证方块组完全出现在移动区域的最上方,方块组每移动一次,就是移动一个方格的位置。场景还设置了下一个要出现方块的提示方块、游戏暂停等控制按钮和游戏分数级别的显示文本,整个场景的示意图如下图所示:

游戏逻辑

  当游戏开始后,首先创建一个新的方块组,并将其添加到场景中的方块移动区域上方。然后进行碰撞检测,如果这时已经发生了碰撞,那么游戏结束;如果没有发生碰撞,就可以使用键盘的方向键对其进行旋转变形或者左右移动。当到达指定事件时方块组会自动下移一个方格,这时再次判断是否发生碰撞,如果发生了碰撞,先消除满行的方格,然后出现新的方块组,并继续进行整个流程。其中方程块的移动、旋转、碰撞检测等都在BoxGroup类中进行;游戏的开始、结束、出现新的方程组、消除满行等都在MyView类中进行。游戏程序图以及方块组移动和旋转流程图如下:
Qt实战(二)——方块游戏-编程之家
1.方块组的移动和旋转
  方块组的左移、右移、下移和旋转都是先进行该操作,然后判断是否发生碰撞,比如发生了碰撞就再进行反向操作。比如,使用方向键左移方块组,那么就先将方块组左移一格,然后进行碰撞检测,看是否与边界线或者其他方块碰撞了,如果发生了碰撞,那么就再移过来,即右移一个。方块组的移动和旋转流程如下:

Qt实战(二)——方块游戏-编程之家

2.碰撞检测
  对于方块组的碰撞检测,其实是使用方块组中的4个小方块来进行的,这样就不用再为每个方块图形都设置一个碰撞检测时使用的形状。要进行碰撞检测时,对每一个小方块都使用函数来获取与它们碰撞的图形项的数目,因为现在小方块在方块组中,所以应该只有方块组与它们碰撞了(由于我们对小方块的形状进行了设置,所以挨着的四个小方块相互间不会被检测出发生了碰撞),也就是说与它们碰撞的图形项数目应该不会大于1,如果有哪个小方块发现与它碰撞的图形项的数目大于1,那么说明已经发生了碰撞。
3.游戏结束
  当一个新的方块组出现时,就立即对齐进行碰撞检测,如果它一出现就与其他方块发生了碰撞,说明游戏已经结束了,这时由方块组发射游戏结束信号。
4.消除满行
  游戏开始后,每当出现一个新的方块以前,都判断游戏移动区域的每一行是否已经拥有10个小方块。如果有一行已经拥有了10个小方块,说明改行已满,那么就销毁该行的所有小方块,然后让该行上面的所有小方块都下移一格。

效果图

Qt实战(二)——方块游戏-编程之家

具体实现

box.h

#ifndef BOX_H
#define BOX_H
#include <QGraphicsObject>
#include<QGraphicsItemGroup>class OneBox :public QGraphicsObject
{
public:OneBox(const QColor &color = Qt::red);QRectF boundingRect() const;void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);QPainterPath shape() const;private:QColor brushColor;};class BoxGroup : public QObject,public QGraphicsItemGroup
{Q_OBJECT
public:enum BoxShape {IShape, JShape, LShape, OShape, SShape, TShape, ZShape, RandomShape};//8中俄罗斯方框形状BoxGroup();QRectF boundingRect() const;//在函数后用const表示不能改变类的成员void clear_box_group(bool destroy_box = false);void create_box(const QPointF &point, BoxShape shape = RandomShape);//在函数的申明处可以将参数设定为默认值,定义处不需要bool isColliding();BoxShape getCurrentShape() {return current_shape;}//获得当前俄罗斯方块的形状protected:void keyPressEvent(QKeyEvent *event);signals:void need_new_box();void game_finished();public slots:void move_one_step();void startTimer(int interval);void stop_timer();private:BoxShape current_shape;QTransform old_transform;QTimer *timer;};#endif // BOX_H

box.cpp

#include "box.h"
#include<QPainter>
#include<QTimer>
#include<QKeyEvent>//OneBox是从QGraphicsObject继承而来的
OneBox::OneBox(const QColor &color) : brushColor(color) {}//该函数为指定后面的绘图区域的外边框
QRectF OneBox::boundingRect() const {qreal pen_width = 1;//小方块的边长为20.5像素return QRectF(-10-pen_width/2, -10-pen_width/2, 20+pen_width, 20+pen_width);}void OneBox::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget){//贴图,看起来有质感,否则单独用颜色去看,会感觉那些方块颜色很单一painter->drawPixmap(-10, -10, 20, 20, QPixmap(":/images/box.gif"));painter->setBrush(brushColor);//设置画刷颜色QColor penColor = brushColor;penColor.setAlpha(20);//将颜色的透明度减小,使方框边界和填充色直接能区分开painter->setPen(penColor);//色绘制画笔//这里画矩形框,框内填充部分用画刷画,框外线条用画笔画painter->drawRect(-10, -10, 20, 20);//画矩形框
}//在局部坐标点上返回item的shape,但是好像没有其它地方调用了该函数
QPainterPath OneBox::shape() const{//QPainterPath是一个绘图操作的容器QPainterPath path;path.addRect(-9.5, -9.5, 19, 19);return path;
}//BoxGroup是从QGraphicsItemGroup,QObject继承而来的
BoxGroup::BoxGroup() {setFlags(QGraphicsItem::ItemIsFocusable);//允许设置输入焦点old_transform = transform();//返回当前item的变换矩阵,当BoxGroup进行旋转后,可以使用它来进行恢复timer = new QTimer(this);connect(timer, SIGNAL(timeout()), this, SLOT(move_one_step()));current_shape = RandomShape;
}QRectF BoxGroup::boundingRect() const {qreal pen_width = 1;return QRectF(-40-pen_width/2, -40-pen_width/2, 80+pen_width, 80+pen_width);//2*2个小方块组成一个小方块组
}void BoxGroup::keyPressEvent(QKeyEvent *event) {static qreal angle = 0;switch(event->key()){//向下键位坠落键case Qt::Key_Down:{moveBy(0, 20);//moveBy是系统自带的函数,不需要我们自己去实现while(!isColliding()) {moveBy(0, 20);}moveBy(0, -20);//往回跳clear_box_group();//到达底部后就将当前方块组的4个item移除,不销毁方块组emit need_new_box();//发射信号,在MyView中接收break;}case Qt::Key_Left:{moveBy(-20, 0);if(isColliding()) {moveBy(20, 0);}break;}case Qt::Key_Right:{moveBy(20, 0);if(isColliding()) {moveBy(-20, 0);}break;}//实现小方块组变形case Qt::Key_Up:{setRotation(angle+90.0);angle = angle+90.0;//rotation();if(isColliding()){setRotation(angle-90);angle = angle-90.0;//rotation();}break;}}
}//检测是否有碰撞
bool BoxGroup::isColliding() {QList<QGraphicsItem *> item_list = childItems();//返回子item列表QGraphicsItem *item;foreach(item, item_list) {if(item->collidingItems().count()>1)//collidingItems返回与当前item碰撞的子item列表return true;//代表至少有一个item发生了碰撞}return false;
}//将方块组从视图中移除掉,如果有需要(即参数为true的情况下)则销毁掉
//其本质是将所有的小方块从方块组中移除掉,达到从视图中将方块组移除的目的
void BoxGroup::clear_box_group(bool destroy_box) {QList<QGraphicsItem *> item_list = childItems();QGraphicsItem *item;foreach(item, item_list) {removeFromGroup(item);//将item从方块组中移除掉if(destroy_box) {OneBox *box = (OneBox*)item;box->deleteLater();//当控制返回到事件循环时,该目标被删除,即销毁}}
}//创建俄罗斯方块组,根据形状参数选择方块组的颜色和形状
void BoxGroup::create_box(const QPointF &point, BoxShape shape) {static const QColor color_table[7] = {QColor(200, 0, 0, 100), QColor(255, 200, 0, 100), QColor(0, 0, 200, 100),QColor(0, 200, 0, 100), QColor(0, 200, 255, 100), QColor(200, 0, 255, 100),QColor(150, 100, 100, 100)};int shape_id = shape; //Box_Shape是枚举型,其实也是整型,因为它相当于整型的宏定义if(shape == RandomShape) {shape_id = qrand()%7;//随机取一个颜色}QColor color = color_table[shape_id];//根据id选颜色QList<OneBox *> list;setTransform(old_transform);//恢复方块组前的变换矩阵for(int i = 0; i < 4; ++i) { //4个小方块组成一个方块组OneBox *temp  = new OneBox(color);list << temp;//将小方块加入list列表addToGroup(temp);}switch(shape_id) {case IShape:current_shape = IShape;//横着的一杆list.at(0)->setPos(-30, -10);list.at(1)->setPos(-10, -10);list.at(2)->setPos(10, -10);list.at(3)->setPos(30, -10);break;case JShape:current_shape = JShape;//J型list.at(0)->setPos(10, -10);list.at(1)->setPos(10, 10);list.at(2)->setPos(10, 30);list.at(3)->setPos(-10, 30);break;case LShape:current_shape = LShape;//L型的方块组list.at(0)->setPos(-10, -10);list.at(1)->setPos(-10, 10);list.at(2)->setPos(-10, 30);list.at(3)->setPos(10, 30);break;case OShape://田字型current_shape = OShape;list.at(0)->setPos(-10, -10);list.at(1)->setPos(10, -10);list.at(2)->setPos(-10, 10);list.at(3)->setPos(10, 10);break;case SShape://S型current_shape = SShape;list.at(0)->setPos(10, -10);list.at(1)->setPos(30, -10);list.at(2)->setPos(-10, 10);list.at(3)->setPos(10, 10);break;case TShape: //土子型current_shape = TShape;list.at(0)->setPos(-10, -10);list.at(1)->setPos(10, -10);list.at(2)->setPos(30, -10);list.at(3)->setPos(10, 10);break;case ZShape://Z字型current_shape = ZShape;list.at(0)->setPos(-10, -10);list.at(1)->setPos(10, -10);list.at(2)->setPos(10, 10);list.at(3)->setPos(30, 10);break;default: break;}setPos(point);//将准备好的俄罗斯方块放入指定的位置,然后进行碰撞检测if(isColliding()) {//如果俄罗斯方块一出现后就发生了碰撞,因为它是从中间出来的,所以一开始不可能是与左右两边发生碰撞,//只能是与下面碰撞,因此如果发生了碰撞,说明游戏已经结束,就可以发送游戏结束信号了,且定时器停止。stop_timer();emit game_finished();}
}//这个是系统里的函数,本程序中是在主函数中启动的
//其实是该子类中的timeEvent()函数调用的
void BoxGroup::startTimer(int interval) {timer->start(interval);//启动定时器并且设置定时器间隔,然后在BoxGroup()的构造函数中设置了该定时器的信号与槽函数
}//每当定时器到时间了,小方块组就向下移一步
void BoxGroup::move_one_step() {moveBy(0, 20);//该函数是父类的函数,这里指向下移动一个单位,因为向下为正坐标if(isColliding()) {//发生碰撞的情况下moveBy(0, -20);clear_box_group();//将方块组移除视图emit need_new_box();//发生信号通知程序需要新的方块组出现}
}void BoxGroup::stop_timer() {timer->stop();//定时器停止
}

myview.h

#ifndef MYVIEW_H
#define MYVIEW_H
#include <QGraphicsView>
class BoxGroup;class MyView : public QGraphicsView
{Q_OBJECT
public:explicit MyView(QWidget *parent = 0);//关键字explicit是为了防止隐式类型转换public slots:void start_game();void clear_full_rows();void move_box();void game_over();void restartGame();void finishGame();void pauseGame();void returnGame();protected:private://遮罩面板QGraphicsWidget *maskWidget;//各种按钮QGraphicsWidget *mask_widget;//首页和游戏中需要用到的各种按钮QGraphicsWidget *start_button;QGraphicsWidget *finish_button;QGraphicsWidget *restart_button;QGraphicsWidget *pause_button;QGraphicsWidget *option_button;QGraphicsWidget *return_button;QGraphicsWidget *help_button;QGraphicsWidget *exit_button;QGraphicsWidget *show_menu_button;//显示人机交互的文本信息QGraphicsTextItem *game_welcome_text;QGraphicsTextItem *game_pause_text;QGraphicsTextItem *game_over_text;QGraphicsTextItem *gameScoreText;QGraphicsTextItem *gameLevelText;QGraphicsLineItem *top_line;QGraphicsLineItem *bottom_line;QGraphicsLineItem *left_line;QGraphicsLineItem *right_line;BoxGroup *box_group;BoxGroup *next_box_group;qreal game_speed;QList<int> rows;void init_view();void init_game();void update_score(const int full_row_num = 0);};#endif // MYVIEW_H

myview.cpp

#include "myview.h"
#include"box.h"
#include<QIcon>
#include<QPropertyAnimation>
#include<QGraphicsBlurEffect>
#include<QTimer>
#include<QPushButton>
#include<QGraphicsProxyWidget>
#include<QApplication>
#include<QLabel>
#include<QFileInfo>static const qreal INITSSPEED = 500;//游戏的初始化速度MyView::MyView(QWidget *parent)
{init_view();
}void MyView::start_game()
{game_welcome_text->hide();start_button->hide();option_button->hide();help_button->hide();exit_button->hide();mask_widget->hide();init_game();
}void MyView::clear_full_rows()
{for(int y = 429; y > 50; y -= 20) {//每隔20行取一个item出来,括号里面的参数不能弄错,否则没有方块消失的效果QList<QGraphicsItem *> list = scene()->items(199, y, 202, 22, Qt::ContainsItemShape,Qt::DescendingOrder);//返回指定区域内所有可见的itemif(list.count() == 10) {   //如果一行已满,则销毁该行的所有小方块foreach(QGraphicsItem *item, list) {OneBox *box = (OneBox *) item;
//                box->deleteLater();//先为小方块使用了模糊图形效果,然后为其添加了放大再缩小的属性动画,等动画指定结束后才调用deleteLater()槽QGraphicsBlurEffect *blurEffect = new QGraphicsBlurEffect;box->setGraphicsEffect(blurEffect);QPropertyAnimation *animation = new QPropertyAnimation(box,"scale");animation->setEasingCurve(QEasingCurve::OutBounce);animation->setDuration(250);animation->setStartValue(4);animation->setEndValue(0.25);animation->start(QAbstractAnimation::DeleteWhenStopped);//connect(animation,SIGNAL(finished()),box,SLOT(deleteLater()));connect(animation,&QPropertyAnimation::finished,box,&OneBox::deleteLater);}rows << y;//将满行的行号保存到rows中}}//如果满行,则下移上面的方块if(rows.count()>0) {
//     move_box();QTimer::singleShot(400,this,SLOT(move_box()));}else {//没有满行,则新出现提示方块,且提示方块出更新新的提示方块box_group->create_box(QPointF(300, 70), next_box_group->getCurrentShape());next_box_group->clear_box_group(true);next_box_group->create_box(QPointF(500, 70));//}}void MyView::move_box()
{for(int i = rows.count(); i > 0; --i) {int row = rows.at(i-1);//取出满行的行号,从最上面的位置开始//取出从区域上边界到当前满行之间所形成的矩形区域foreach(QGraphicsItem *item, scene()->items(199, 49, 202, row-47, Qt::ContainsItemShape,Qt::DescendingOrder)) {item->moveBy(0, 20);}}//更新分数update_score(rows.count());//出现新的方块组rows.clear();box_group->create_box(QPointF(300, 70), next_box_group->getCurrentShape());next_box_group->clear_box_group(true);next_box_group->create_box(QPoint(500, 70));
}void MyView::game_over()
{//游戏结束pause_button->hide();show_menu_button->hide();mask_widget->show();game_over_text->show();restart_button->setPos(370, 200);finish_button->show();
}void MyView::restartGame()
{mask_widget->hide();game_over_text->hide();finish_button->hide();restart_button->setPos(600, 150);//销毁当前方块组和当前方块中的所有小方块next_box_group->clear_box_group(true);box_group->clear_box_group();box_group->hide();foreach(QGraphicsItem *item, scene()->items(199, 49, 202, 402,Qt::ContainsItemBoundingRect,Qt::DescendingOrder)) {scene()->removeItem(item);OneBox *box = (OneBox*)item;box->deleteLater();}init_game();
}void MyView::finishGame()
{game_over_text->hide();finish_button->hide();restart_button->setPos(600, 150);restart_button->hide();pause_button->hide();show_menu_button->hide();gameScoreText->hide();gameLevelText->hide();top_line->hide();bottom_line->hide();left_line->hide();right_line->hide();next_box_group->clear_box_group(true);box_group->clear_box_group();box_group->hide();foreach(QGraphicsItem *item, scene()->items(199, 49, 202, 402,Qt::ContainsItemBoundingRect,Qt::DescendingOrder)) {scene()->removeItem(item);OneBox *box = (OneBox*)item;box->deleteLater();}mask_widget->show();game_welcome_text->show();start_button->show();option_button->show();help_button->show();exit_button->show();scene()->setBackgroundBrush(QPixmap(":/images/background.png"));
}void MyView::pauseGame()
{box_group->stop_timer();//中断游戏最主要的是停止方块下移的定时器工作restart_button->hide();pause_button->hide();show_menu_button->hide();mask_widget->show();game_pause_text->show();return_button->show();
}void MyView::returnGame()
{return_button->hide();game_pause_text->hide();mask_widget->hide();restart_button->show();pause_button->show();show_menu_button->show();box_group->startTimer(game_speed);
}void MyView::init_view()
{setRenderHint(QPainter::Antialiasing);//使用抗锯齿的方式渲染setCacheMode(CacheBackground);//设置缓存背景,这样可以加快渲染速度setWindowTitle(tr("Teris游戏"));setWindowIcon(QIcon(":/images/icon.png"));//设置标题处的图标setMinimumSize(810, 510);  //2者设置成一样说明视图尺寸不能再更改setMaximumSize(810, 510);QGraphicsScene *scene = new QGraphicsScene;//新建场景指针scene->setSceneRect(5, 5, 800, 500);//场景大小scene->setBackgroundBrush(QPixmap(":/images/background.png"));setScene(scene);//设置场景//俄罗斯方块可移动区域外界的4条线,与外界预留3个像素是为了方便进行碰撞检测top_line = scene->addLine(197, 27, 403, 27);bottom_line = scene->addLine(197, 453, 403, 453);left_line = scene->addLine(197, 27, 197, 453);right_line = scene->addLine(403, 27, 403, 453);//添加当前方块组box_group = new BoxGroup;//通过新建BoxGroup对象间接达到调用box的2个类connect(box_group, SIGNAL(need_new_box()), this, SLOT(clear_full_rows()));connect(box_group, SIGNAL(game_finished()), this, SLOT(game_over()));scene->addItem(box_group);//添加提示方块组next_box_group = new BoxGroup;scene->addItem(next_box_group);gameScoreText = new QGraphicsTextItem();//文本的父item为对应的场景gameScoreText->setFont(QFont("Times", 50, QFont::Bold));//为文本设置字体gameScoreText->setPos(450, 350);//分数在场景中出现的位置gameLevelText = new QGraphicsTextItem();gameLevelText->setFont(QFont("Times", 50, QFont::Bold));gameLevelText->setPos(20, 150);scene->addItem(gameLevelText);scene->addItem(gameScoreText);//开始游戏//start_game();//设置初始为隐藏状态top_line->hide();bottom_line->hide();left_line->hide();right_line->hide();gameScoreText->hide();gameLevelText->hide();//黑色遮罩QWidget *mask = new QWidget;mask->setAutoFillBackground(true);mask->setPalette(QPalette(QColor(0, 0, 0, 50)));//alpha为不透明度mask->resize(900, 600);//addWidget()函数的返回值是QGraphicsProxyWidget,如果不添加相应的头文件,则此处会报错mask_widget = scene->addWidget(mask);mask_widget->setPos(-50, -50);mask_widget->setZValue(1);//该层薄纱放在原图的上面,这里有点类似于opengl中的3维绘图//选项面板QWidget *option = new QWidget;//将关闭按钮放在option上QPushButton *option_close_button = new QPushButton(tr("关  闭"), option);//第2个参数为按钮所在的widget//设置按钮的字体颜色是白色QPalette palette;palette.setColor(QPalette::ButtonText, Qt::black);//第一个参数调色版的role,这里指的是按钮字体颜色option_close_button->setPalette(palette);//设置关闭按钮的位置,和单击后的响应option_close_button->move(120, 300);connect(option_close_button, SIGNAL(clicked()), option, SLOT(hide()));//单击后消失option->setAutoFillBackground(true);option->setPalette(QPalette(QColor(0, 0, 0, 180)));option->resize(300, 400);QGraphicsWidget *option_widget = scene->addWidget(option);option_widget->setPos(250, 50);option_widget->setZValue(3);option_widget->hide();//帮助面板QWidget *help = new QWidget;QPushButton *help_close_button = new QPushButton(tr("帮  助"), help);help_close_button->setPalette(palette);help_close_button->move(120, 300);connect(help_close_button, SIGNAL(clicked()), help, SLOT(hide()));help->setAutoFillBackground(true);help->setPalette(QPalette(QColor(0, 0, 0, 180)));help->resize(300, 400);QGraphicsWidget *help_widget = scene->addWidget(help);help_widget->setPos(250, 50);help_widget->setZValue(3);help_widget->hide();//游戏欢迎文本game_welcome_text = new QGraphicsTextItem();//第一个参数为文本内容,第二个参数为父itemgame_welcome_text->setHtml(tr("<font color=green>Tetris游戏</font>"));game_welcome_text->setFont(QFont("Times", 40, QFont::Bold));game_welcome_text->setPos(300, 100);game_welcome_text->setZValue(2);//放在第2层//游戏暂停文本game_pause_text = new QGraphicsTextItem();//第一个参数为文本内容,第二个参数为父itemgame_pause_text->setHtml(tr("<font color=green>游戏暂停中!</font>"));game_pause_text->setFont(QFont("Times", 40, QFont::Bold));game_pause_text->setPos(300, 100);game_pause_text->setZValue(2);//放在第2层game_pause_text->hide();//游戏结束文本game_over_text = new QGraphicsTextItem();//第一个参数为文本内容,第二个参数为父itemgame_over_text->setHtml(tr("<font color=green>GAME OVER!</font>"));game_over_text->setFont(QFont("Times", 40, QFont::Bold));game_over_text->setPos(300, 100);game_over_text->setZValue(2);//放在第2层game_over_text->hide();scene->addItem(game_welcome_text);scene->addItem(game_pause_text);scene->addItem(game_over_text);// 游戏中使用的按钮QPushButton *button1 = new QPushButton(tr("开    始"));QPushButton *button2 = new QPushButton(tr("选    项"));QPushButton *button3 = new QPushButton(tr("帮    助"));QPushButton *button4 = new QPushButton(tr("退    出"));QPushButton *button5 = new QPushButton(tr("重新开始"));QPushButton *button6 = new QPushButton(tr("暂    停"));QPushButton *button7 = new QPushButton(tr("主 菜 单"));QPushButton *button8 = new QPushButton(tr("返回游戏"));QPushButton *button9 = new QPushButton(tr("结束游戏"));connect(button1, SIGNAL(clicked()), this, SLOT(start_game()));connect(button2, SIGNAL(clicked()), option, SLOT(show()));connect(button3, SIGNAL(clicked()), help, SLOT(show()));connect(button4, SIGNAL(clicked()), qApp, SLOT(quit()));//此处槽函数的接收对象为应用程序本身connect(button5, SIGNAL(clicked()), this, SLOT(restartGame()));connect(button6, SIGNAL(clicked()), this, SLOT(pauseGame()));connect(button7, SIGNAL(clicked()), this, SLOT(show_menu_button()));connect(button8, SIGNAL(clicked()), this, SLOT(returnGame()));//返回主菜单connect(button9, SIGNAL(clicked()), this, SLOT(finishGame()));start_button = scene->addWidget(button1);//restart_button并不是QPushbutton类型,而是QGraphicsItem类型,后面的类似option_button = scene->addWidget(button2);help_button = scene->addWidget(button3);exit_button = scene->addWidget(button4);restart_button = scene->addWidget(button5);pause_button = scene->addWidget(button6);show_menu_button = scene->addWidget(button7);return_button = scene->addWidget(button8);finish_button = scene->addWidget(button9);//设置位置start_button->setPos(370, 200);option_button->setPos(370, 250);help_button->setPos(370, 300);exit_button->setPos(370, 350);restart_button->setPos(600, 150);pause_button->setPos(600, 200);show_menu_button->setPos(600, 250);return_button->setPos(370, 200);finish_button->setPos(370, 250);//将这些按钮都放在z方向的第二层start_button->setZValue(2);option_button->setZValue(2);help_button->setZValue(2);exit_button->setZValue(2);restart_button->setZValue(2);return_button->setZValue(2);finish_button->setZValue(2);//一部分按钮隐藏起来restart_button->hide();finish_button->hide();pause_button->hide();show_menu_button->hide();return_button->hide();}void MyView::init_game()
{box_group->create_box(QPointF(300, 70)); //创建方块组,在中间位置处出现box_group->setFocus();//设置人机交互焦点,这样就可以使用键盘来控制它box_group->startTimer(INITSSPEED);//启动定时器game_speed = INITSSPEED;//游戏速度,暂停时需要用到next_box_group->create_box(QPoint(500, 70));//创建提示方块组scene()->setBackgroundBrush(QPixmap(":/images/background01.png"));gameScoreText->setHtml(tr("<font color=red>00</font>"));gameLevelText->setHtml(tr("<font color=white>第<br>一<br>幕</font>"));restart_button->show();pause_button->show();show_menu_button->show();gameScoreText->show();gameLevelText->show();top_line->show();bottom_line->show();left_line->show();right_line->show();// 可能以前返回主菜单时隐藏了boxGroupbox_group->show();
}void MyView::update_score(const int full_row_num)
{//更新分数int score = full_row_num *100;int currentScore =gameScoreText->toPlainText().toInt();currentScore+=score;//显示当前分数gameScoreText->setHtml(tr("<font color=red>%1</font>").arg(currentScore));//判断级别if(currentScore<500){//第一级,什么都不用做}else if(currentScore <1000){//第二级gameLevelText->setHtml(tr("<font color=white>第<br>二<br>幕</font>"));scene()->setBackgroundBrush(QPixmap(":/images/background02.png"));game_speed =300;box_group->stop_timer();box_group->startTimer(game_speed);}else {//添加下一个级别的设置}
}

main.cpp

#include<QApplication>
#include"myview.h"
#include<QTextCodec>
#include<QTime>int main(int argc, char* argv[]) {QApplication  app(argc, argv);QTextCodec::setCodecForLocale(QTextCodec::codecForLocale());//QTime提供了闹钟功能qsrand(QTime(0, 0, 0).secsTo(QTime::currentTime()));//secsTo()为返回当前的秒数MyView view;//主函数是直接调用的视图类view.show();return app.exec();
}