方块游戏架构
在这个游戏中,有一个区域用来摆放方块,该区域宽为10,高为20,以小正方形为单位,它可以看作是拥有20行10列的一个网格。标准的游戏中一共有7种方块,它们都是由4个小正方形组成的规则图形,依据形状分别用字母I、J、L、O、S、T和Z来命名。
这里使用图形视图框架来实现整个游戏的设计。小正方形由OneBox来表示,它继承自QGraphicsObject类,之所以继承自这个类,是因为这样就可以使用信号和槽机制,话可以使用属性动画。小正方形就是一个宽和高都为20像素的正方形图形项。游戏中的方块游戏由方块组BoxGroup类来实现,继承自QObject和QGraphicsItemGroup类,这样该类也可以使用信号和槽机制。方块组是一个宽和高都是80像素的图形项组,其中包含了4个小方块,通过设置小方块的位置来实现7种标准的方块图形。它们的形状和位置如下图,在BoxGroup类中实现了方块图形的创建、移动和碰撞检测。
方块移动区域在游戏场景中使用四条直线图形项所围成区域来表示,之所以要这样实现,是因为这样可以通过方块组是否与直线图形项碰撞来检测是否移动出界。整个游戏界面由MyView类来实现,该类继承自QGraphicsView类,实现了场景设置、游戏逻辑设计和游戏声音设置及其他控制功能。整个游戏场景宽800像素,高500像素。方块移动区域宽200像素,高400像素,纵向每20个像素被视作一行,共有20行;横行也是每20个像素视作一列,所以共有10列,该区域可以看作一个由20行10列20×20像素的方格组成的网格。方块组在方块移动区域的初始位置为上方正中间,但方块组的最上方一行小正方形在方块移动区域以外,这样可以保证方块组完全出现在移动区域的最上方,方块组每移动一次,就是移动一个方格的位置。场景还设置了下一个要出现方块的提示方块、游戏暂停等控制按钮和游戏分数级别的显示文本,整个场景的示意图如下图所示:
游戏逻辑
当游戏开始后,首先创建一个新的方块组,并将其添加到场景中的方块移动区域上方。然后进行碰撞检测,如果这时已经发生了碰撞,那么游戏结束;如果没有发生碰撞,就可以使用键盘的方向键对其进行旋转变形或者左右移动。当到达指定事件时方块组会自动下移一个方格,这时再次判断是否发生碰撞,如果发生了碰撞,先消除满行的方格,然后出现新的方块组,并继续进行整个流程。其中方程块的移动、旋转、碰撞检测等都在BoxGroup类中进行;游戏的开始、结束、出现新的方程组、消除满行等都在MyView类中进行。游戏程序图以及方块组移动和旋转流程图如下:
1.方块组的移动和旋转
方块组的左移、右移、下移和旋转都是先进行该操作,然后判断是否发生碰撞,比如发生了碰撞就再进行反向操作。比如,使用方向键左移方块组,那么就先将方块组左移一格,然后进行碰撞检测,看是否与边界线或者其他方块碰撞了,如果发生了碰撞,那么就再移过来,即右移一个。方块组的移动和旋转流程如下:
2.碰撞检测
对于方块组的碰撞检测,其实是使用方块组中的4个小方块来进行的,这样就不用再为每个方块图形都设置一个碰撞检测时使用的形状。要进行碰撞检测时,对每一个小方块都使用函数来获取与它们碰撞的图形项的数目,因为现在小方块在方块组中,所以应该只有方块组与它们碰撞了(由于我们对小方块的形状进行了设置,所以挨着的四个小方块相互间不会被检测出发生了碰撞),也就是说与它们碰撞的图形项数目应该不会大于1,如果有哪个小方块发现与它碰撞的图形项的数目大于1,那么说明已经发生了碰撞。
3.游戏结束
当一个新的方块组出现时,就立即对齐进行碰撞检测,如果它一出现就与其他方块发生了碰撞,说明游戏已经结束了,这时由方块组发射游戏结束信号。
4.消除满行
游戏开始后,每当出现一个新的方块以前,都判断游戏移动区域的每一行是否已经拥有10个小方块。如果有一行已经拥有了10个小方块,说明改行已满,那么就销毁该行的所有小方块,然后让该行上面的所有小方块都下移一格。
效果图
具体实现
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);//返回指定区域内所有可见的item
if(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();//第一个参数为文本内容,第二个参数为父item
game_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();//第一个参数为文本内容,第二个参数为父item
game_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();//第一个参数为文本内容,第二个参数为父item
game_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();
// 可能以前返回主菜单时隐藏了boxGroup
box_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();
}