Qt項(xiàng)目實(shí)戰(zhàn)之方塊游戲的實(shí)現(xiàn)
1.方塊游戲架構(gòu)
在這個(gè)游戲中,有一個(gè)區(qū)域用來(lái)擺放方塊,該區(qū)域?qū)挒?0,高為20,以小正方形為單位,它可以看作是擁有20行10列的一個(gè)網(wǎng)格。標(biāo)準(zhǔn)的游戲中一共有7種方塊,它們都是由4個(gè)小正方形組成的規(guī)則圖形,依據(jù)形狀分別用字母I、J、L、O、S、T和Z來(lái)命名。
這里使用圖形視圖框架來(lái)實(shí)現(xiàn)整個(gè)游戲的設(shè)計(jì)。小正方形由OneBox來(lái)表示,它繼承自QGraphicsObject類,之所以繼承自這個(gè)類,是因?yàn)檫@樣就可以使用信號(hào)和槽機(jī)制,話可以使用屬性動(dòng)畫(huà)。小正方形就是一個(gè)寬和高都為20像素的正方形圖形項(xiàng)。游戲中的方塊游戲由方塊組BoxGroup類來(lái)實(shí)現(xiàn),繼承自QObject和QGraphicsItemGroup類,這樣該類也可以使用信號(hào)和槽機(jī)制。方塊組是一個(gè)寬和高都是80像素的圖形項(xiàng)組,其中包含了4個(gè)小方塊,通過(guò)設(shè)置小方塊的位置來(lái)實(shí)現(xiàn)7種標(biāo)準(zhǔn)的方塊圖形。它們的形狀和位置如下圖,在BoxGroup類中實(shí)現(xiàn)了方塊圖形的創(chuàng)建、移動(dòng)和碰撞檢測(cè)。

方塊移動(dòng)區(qū)域在游戲場(chǎng)景中使用四條直線圖形項(xiàng)所圍成區(qū)域來(lái)表示,之所以要這樣實(shí)現(xiàn),是因?yàn)檫@樣可以通過(guò)方塊組是否與直線圖形項(xiàng)碰撞來(lái)檢測(cè)是否移動(dòng)出界。整個(gè)游戲界面由MyView類來(lái)實(shí)現(xiàn),該類繼承自QGraphicsView類,實(shí)現(xiàn)了場(chǎng)景設(shè)置、游戲邏輯設(shè)計(jì)和游戲聲音設(shè)置及其他控制功能。
整個(gè)游戲場(chǎng)景寬800像素,高500像素。方塊移動(dòng)區(qū)域?qū)?00像素,高400像素,縱向每20個(gè)像素被視作一行,共有20行;橫行也是每20個(gè)像素視作一列,所以共有10列,該區(qū)域可以看作一個(gè)由20行10列20×20像素的方格組成的網(wǎng)格。方塊組在方塊移動(dòng)區(qū)域的初始位置為上方正中間,但方塊組的最上方一行小正方形在方塊移動(dòng)區(qū)域以外,這樣可以保證方塊組完全出現(xiàn)在移動(dòng)區(qū)域的最上方,方塊組每移動(dòng)一次,就是移動(dòng)一個(gè)方格的位置。場(chǎng)景還設(shè)置了下一個(gè)要出現(xiàn)方塊的提示方塊、游戲暫停等控制按鈕和游戲分?jǐn)?shù)級(jí)別的顯示文本,整個(gè)場(chǎng)景的示意圖如下圖所示:
2.游戲邏輯
當(dāng)游戲開(kāi)始后,首先創(chuàng)建一個(gè)新的方塊組,并將其添加到場(chǎng)景中的方塊移動(dòng)區(qū)域上方。然后進(jìn)行碰撞檢測(cè),如果這時(shí)已經(jīng)發(fā)生了碰撞,那么游戲結(jié)束;如果沒(méi)有發(fā)生碰撞,就可以使用鍵盤(pán)的方向鍵對(duì)其進(jìn)行旋轉(zhuǎn)變形或者左右移動(dòng)。當(dāng)?shù)竭_(dá)指定事件時(shí)方塊組會(huì)自動(dòng)下移一個(gè)方格,這時(shí)再次判斷是否發(fā)生碰撞,如果發(fā)生了碰撞,先消除滿行的方格,然后出現(xiàn)新的方塊組,并繼續(xù)進(jìn)行整個(gè)流程。其中方程塊的移動(dòng)、旋轉(zhuǎn)、碰撞檢測(cè)等都在BoxGroup類中進(jìn)行;游戲的開(kāi)始、結(jié)束、出現(xiàn)新的方程組、消除滿行等都在MyView類中進(jìn)行。游戲程序圖以及方塊組移動(dòng)和旋轉(zhuǎn)流程圖如下:

2.1、方塊組的移動(dòng)和旋轉(zhuǎn)
方塊組的左移、右移、下移和旋轉(zhuǎn)都是先進(jìn)行該操作,然后判斷是否發(fā)生碰撞,比如發(fā)生了碰撞就再進(jìn)行反向操作。比如,使用方向鍵左移方塊組,那么就先將方塊組左移一格,然后進(jìn)行碰撞檢測(cè),看是否與邊界線或者其他方塊碰撞了,如果發(fā)生了碰撞,那么就再移過(guò)來(lái),即右移一個(gè)。方塊組的移動(dòng)和旋轉(zhuǎn)流程如下:

2.2、碰撞檢測(cè)
對(duì)于方塊組的碰撞檢測(cè),其實(shí)是使用方塊組中的4個(gè)小方塊來(lái)進(jìn)行的,這樣就不用再為每個(gè)方塊圖形都設(shè)置一個(gè)碰撞檢測(cè)時(shí)使用的形狀。要進(jìn)行碰撞檢測(cè)時(shí),對(duì)每一個(gè)小方塊都使用函數(shù)來(lái)獲取與它們碰撞的圖形項(xiàng)的數(shù)目,因?yàn)楝F(xiàn)在小方塊在方塊組中,所以應(yīng)該只有方塊組與它們碰撞了(由于我們對(duì)小方塊的形狀進(jìn)行了設(shè)置,所以挨著的四個(gè)小方塊相互間不會(huì)被檢測(cè)出發(fā)生了碰撞),也就是說(shuō)與它們碰撞的圖形項(xiàng)數(shù)目應(yīng)該不會(huì)大于1,如果有哪個(gè)小方塊發(fā)現(xiàn)與它碰撞的圖形項(xiàng)的數(shù)目大于1,那么說(shuō)明已經(jīng)發(fā)生了碰撞。
2.3、游戲結(jié)束
當(dāng)一個(gè)新的方塊組出現(xiàn)時(shí),就立即對(duì)齊進(jìn)行碰撞檢測(cè),如果它一出現(xiàn)就與其他方塊發(fā)生了碰撞,說(shuō)明游戲已經(jīng)結(jié)束了,這時(shí)由方塊組發(fā)射游戲結(jié)束信號(hào)。
2.4、消除滿行
游戲開(kāi)始后,每當(dāng)出現(xiàn)一個(gè)新的方塊以前,都判斷游戲移動(dòng)區(qū)域的每一行是否已經(jīng)擁有10個(gè)小方塊。如果有一行已經(jīng)擁有了10個(gè)小方塊,說(shuō)明改行已滿,那么就銷毀該行的所有小方塊,然后讓該行上面的所有小方塊都下移一格。
3.效果圖

4.具體實(shí)現(xiàn)
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;//在函數(shù)后用const表示不能改變類的成員
void clear_box_group(bool destroy_box = false);
void create_box(const QPointF &point, BoxShape shape = RandomShape);//在函數(shù)的申明處可以將參數(shù)設(shè)定為默認(rèn)值,定義處不需要
bool isColliding();
BoxShape getCurrentShape() {return current_shape;}//獲得當(dāng)前俄羅斯方塊的形狀
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_Hbox.cpp
#include "box.h"
#include<QPainter>
#include<QTimer>
#include<QKeyEvent>
//OneBox是從QGraphicsObject繼承而來(lái)的
OneBox::OneBox(const QColor &color) : brushColor(color) {
}
//該函數(shù)為指定后面的繪圖區(qū)域的外邊框
QRectF OneBox::boundingRect() const {
qreal pen_width = 1;
//小方塊的邊長(zhǎng)為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){
//貼圖,看起來(lái)有質(zhì)感,否則單獨(dú)用顏色去看,會(huì)感覺(jué)那些方塊顏色很單一
painter->drawPixmap(-10, -10, 20, 20, QPixmap(":/images/box.gif"));
painter->setBrush(brushColor);//設(shè)置畫(huà)刷顏色
QColor penColor = brushColor;
penColor.setAlpha(20);//將顏色的透明度減小,使方框邊界和填充色直接能區(qū)分開(kāi)
painter->setPen(penColor);//色繪制畫(huà)筆
//這里畫(huà)矩形框,框內(nèi)填充部分用畫(huà)刷畫(huà),框外線條用畫(huà)筆畫(huà)
painter->drawRect(-10, -10, 20, 20);//畫(huà)矩形框
}
//在局部坐標(biāo)點(diǎn)上返回item的shape,但是好像沒(méi)有其它地方調(diào)用了該函數(shù)
QPainterPath OneBox::shape() const{
//QPainterPath是一個(gè)繪圖操作的容器
QPainterPath path;
path.addRect(-9.5, -9.5, 19, 19);
return path;
}
//BoxGroup是從QGraphicsItemGroup,QObject繼承而來(lái)的
BoxGroup::BoxGroup() {
setFlags(QGraphicsItem::ItemIsFocusable);//允許設(shè)置輸入焦點(diǎn)
old_transform = transform();//返回當(dāng)前item的變換矩陣,當(dāng)BoxGroup進(jìn)行旋轉(zhuǎn)后,可以使用它來(lái)進(jìn)行恢復(fù)
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個(gè)小方塊組成一個(gè)小方塊組
}
void BoxGroup::keyPressEvent(QKeyEvent *event) {
static qreal angle = 0;
switch(event->key())
{
//向下鍵位墜落鍵
case Qt::Key_Down:{
moveBy(0, 20);//moveBy是系統(tǒng)自帶的函數(shù),不需要我們自己去實(shí)現(xiàn)
while(!isColliding()) {
moveBy(0, 20);
}
moveBy(0, -20);//往回跳
clear_box_group();//到達(dá)底部后就將當(dāng)前方塊組的4個(gè)item移除,不銷毀方塊組
emit need_new_box();//發(fā)射信號(hào),在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;}
//實(shí)現(xiàn)小方塊組變形
case Qt::Key_Up:{
setRotation(angle+90.0);
angle = angle+90.0;
//rotation();
if(isColliding())
{
setRotation(angle-90);
angle = angle-90.0;
//rotation();
}
break;}
}
}
//檢測(cè)是否有碰撞
bool BoxGroup::isColliding() {
QList<QGraphicsItem *> item_list = childItems();//返回子item列表
QGraphicsItem *item;
foreach(item, item_list) {
if(item->collidingItems().count()>1)//collidingItems返回與當(dāng)前item碰撞的子item列表
return true;//代表至少有一個(gè)item發(fā)生了碰撞
}
return false;
}
//將方塊組從視圖中移除掉,如果有需要(即參數(shù)為true的情況下)則銷毀掉
//其本質(zhì)是將所有的小方塊從方塊組中移除掉,達(dá)到從視圖中將方塊組移除的目的
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();//當(dāng)控制返回到事件循環(huán)時(shí),該目標(biāo)被刪除,即銷毀
}
}
}
//創(chuàng)建俄羅斯方塊組,根據(jù)形狀參數(shù)選擇方塊組的顏色和形狀
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是枚舉型,其實(shí)也是整型,因?yàn)樗喈?dāng)于整型的宏定義
if(shape == RandomShape) {
shape_id = qrand()%7;//隨機(jī)取一個(gè)顏色
}
QColor color = color_table[shape_id];//根據(jù)id選顏色
QList<OneBox *> list;
setTransform(old_transform);//恢復(fù)方塊組前的變換矩陣
for(int i = 0; i < 4; ++i) { //4個(gè)小方塊組成一個(gè)方塊組
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);//將準(zhǔn)備好的俄羅斯方塊放入指定的位置,然后進(jìn)行碰撞檢測(cè)
if(isColliding()) {
//如果俄羅斯方塊一出現(xiàn)后就發(fā)生了碰撞,因?yàn)樗菑闹虚g出來(lái)的,所以一開(kāi)始不可能是與左右兩邊發(fā)生碰撞,
//只能是與下面碰撞,因此如果發(fā)生了碰撞,說(shuō)明游戲已經(jīng)結(jié)束,就可以發(fā)送游戲結(jié)束信號(hào)了,且定時(shí)器停止。
stop_timer();
emit game_finished();
}
}
//這個(gè)是系統(tǒng)里的函數(shù),本程序中是在主函數(shù)中啟動(dòng)的
//其實(shí)是該子類中的timeEvent()函數(shù)調(diào)用的
void BoxGroup::startTimer(int interval) {
timer->start(interval);//啟動(dòng)定時(shí)器并且設(shè)置定時(shí)器間隔,然后在BoxGroup()的構(gòu)造函數(shù)中設(shè)置了該定時(shí)器的信號(hào)與槽函數(shù)
}
//每當(dāng)定時(shí)器到時(shí)間了,小方塊組就向下移一步
void BoxGroup::move_one_step() {
moveBy(0, 20);//該函數(shù)是父類的函數(shù),這里指向下移動(dòng)一個(gè)單位,因?yàn)橄蛳聻檎鴺?biāo)
if(isColliding()) {//發(fā)生碰撞的情況下
moveBy(0, -20);
clear_box_group();//將方塊組移除視圖
emit need_new_box();//發(fā)生信號(hào)通知程序需要新的方塊組出現(xiàn)
}
}
void BoxGroup::stop_timer() {
timer->stop();//定時(shí)器停止
}
myview.h
#ifndef MYVIEW_H
#define MYVIEW_H
#include <QGraphicsView>
class BoxGroup;
class MyView : public QGraphicsView
{
Q_OBJECT
public:
explicit MyView(QWidget *parent = 0);//關(guān)鍵字explicit是為了防止隱式類型轉(zhuǎn)換
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;
//首頁(yè)和游戲中需要用到的各種按鈕
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;
//顯示人機(jī)交互的文本信息
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行取一個(gè)item出來(lái),括號(hào)里面的參數(shù)不能弄錯(cuò),否則沒(méi)有方塊消失的效果
QList<QGraphicsItem *> list = scene()->items(199, y, 202, 22, Qt::ContainsItemShape,Qt::DescendingOrder);//返回指定區(qū)域內(nèi)所有可見(jiàn)的item
if(list.count() == 10) { //如果一行已滿,則銷毀該行的所有小方塊
foreach(QGraphicsItem *item, list) {
OneBox *box = (OneBox *) item;
// box->deleteLater();
//先為小方塊使用了模糊圖形效果,然后為其添加了放大再縮小的屬性動(dòng)畫(huà),等動(dòng)畫(huà)指定結(jié)束后才調(diào)用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;//將滿行的行號(hào)保存到rows中
}
}
//如果滿行,則下移上面的方塊
if(rows.count()>0) {
// move_box();
QTimer::singleShot(400,this,SLOT(move_box()));
}
else {
//沒(méi)有滿行,則新出現(xiàn)提示方塊,且提示方塊出更新新的提示方塊
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);//取出滿行的行號(hào),從最上面的位置開(kāi)始
//取出從區(qū)域上邊界到當(dāng)前滿行之間所形成的矩形區(qū)域
foreach(QGraphicsItem *item, scene()->items(199, 49, 202, row-47, Qt::ContainsItemShape,Qt::DescendingOrder)) {
item->moveBy(0, 20);
}
}
//更新分?jǐn)?shù)
update_score(rows.count());
//出現(xiàn)新的方塊組
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()
{
//游戲結(jié)束
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);
//銷毀當(dāng)前方塊組和當(dāng)前方塊中的所有小方塊
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();//中斷游戲最主要的是停止方塊下移的定時(shí)器工作
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);//設(shè)置緩存背景,這樣可以加快渲染速度
setWindowTitle(tr("Teris游戲"));
setWindowIcon(QIcon(":/images/icon.png"));//設(shè)置標(biāo)題處的圖標(biāo)
setMinimumSize(810, 510); //2者設(shè)置成一樣說(shuō)明視圖尺寸不能再更改
setMaximumSize(810, 510);
QGraphicsScene *scene = new QGraphicsScene;//新建場(chǎng)景指針
scene->setSceneRect(5, 5, 800, 500);//場(chǎng)景大小
scene->setBackgroundBrush(QPixmap(":/images/background.png"));
setScene(scene);//設(shè)置場(chǎng)景
//俄羅斯方塊可移動(dòng)區(qū)域外界的4條線,與外界預(yù)留3個(gè)像素是為了方便進(jìn)行碰撞檢測(cè)
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);
//添加當(dāng)前方塊組
box_group = new BoxGroup;//通過(guò)新建BoxGroup對(duì)象間接達(dá)到調(diào)用box的2個(gè)類
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為對(duì)應(yīng)的場(chǎng)景
gameScoreText->setFont(QFont("Times", 50, QFont::Bold));//為文本設(shè)置字體
gameScoreText->setPos(450, 350);//分?jǐn)?shù)在場(chǎng)景中出現(xiàn)的位置
gameLevelText = new QGraphicsTextItem();
gameLevelText->setFont(QFont("Times", 50, QFont::Bold));
gameLevelText->setPos(20, 150);
scene->addItem(gameLevelText);
scene->addItem(gameScoreText);
//開(kāi)始游戲
//start_game();
//設(shè)置初始為隱藏狀態(tài)
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()函數(shù)的返回值是QGraphicsProxyWidget,如果不添加相應(yīng)的頭文件,則此處會(huì)報(bào)錯(cuò)
mask_widget = scene->addWidget(mask);
mask_widget->setPos(-50, -50);
mask_widget->setZValue(1);//該層薄紗放在原圖的上面,這里有點(diǎn)類似于opengl中的3維繪圖
//選項(xiàng)面板
QWidget *option = new QWidget;
//將關(guān)閉按鈕放在option上
QPushButton *option_close_button = new QPushButton(tr("關(guān) 閉"), option);//第2個(gè)參數(shù)為按鈕所在的widget
//設(shè)置按鈕的字體顏色是白色
QPalette palette;
palette.setColor(QPalette::ButtonText, Qt::black);//第一個(gè)參數(shù)調(diào)色版的role,這里指的是按鈕字體顏色
option_close_button->setPalette(palette);
//設(shè)置關(guān)閉按鈕的位置,和單擊后的響應(yīng)
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();//第一個(gè)參數(shù)為文本內(nèi)容,第二個(gè)參數(shù)為父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();//第一個(gè)參數(shù)為文本內(nèi)容,第二個(gè)參數(shù)為父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();
//游戲結(jié)束文本
game_over_text = new QGraphicsTextItem();//第一個(gè)參數(shù)為文本內(nèi)容,第二個(gè)參數(shù)為父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("開(kāi) 始"));
QPushButton *button2 = new QPushButton(tr("選 項(xiàng)"));
QPushButton *button3 = new QPushButton(tr("幫 助"));
QPushButton *button4 = new QPushButton(tr("退 出"));
QPushButton *button5 = new QPushButton(tr("重新開(kāi)始"));
QPushButton *button6 = new QPushButton(tr("暫 停"));
QPushButton *button7 = new QPushButton(tr("主 菜 單"));
QPushButton *button8 = new QPushButton(tr("返回游戲"));
QPushButton *button9 = new QPushButton(tr("結(jié)束游戲"));
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()));//此處槽函數(shù)的接收對(duì)象為應(yīng)用程序本身
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);
//設(shè)置位置
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);
//一部分按鈕隱藏起來(lái)
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)); //創(chuàng)建方塊組,在中間位置處出現(xiàn)
box_group->setFocus();//設(shè)置人機(jī)交互焦點(diǎn),這樣就可以使用鍵盤(pán)來(lái)控制它
box_group->startTimer(INITSSPEED);//啟動(dòng)定時(shí)器
game_speed = INITSSPEED;//游戲速度,暫停時(shí)需要用到
next_box_group->create_box(QPoint(500, 70));//創(chuàng)建提示方塊組
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();
// 可能以前返回主菜單時(shí)隱藏了boxGroup
box_group->show();
}
void MyView::update_score(const int full_row_num)
{
//更新分?jǐn)?shù)
int score = full_row_num *100;
int currentScore =gameScoreText->toPlainText().toInt();
currentScore+=score;
//顯示當(dāng)前分?jǐn)?shù)
gameScoreText->setHtml(tr("<font color=red>%1</font>").arg(currentScore));
//判斷級(jí)別
if(currentScore<500)
{
//第一級(jí),什么都不用做
}
else if(currentScore <1000)
{
//第二級(jí)
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 {
//添加下一個(gè)級(jí)別的設(shè)置
}
}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()為返回當(dāng)前的秒數(shù)
MyView view;//主函數(shù)是直接調(diào)用的視圖類
view.show();
return app.exec();
}到此這篇關(guān)于Qt項(xiàng)目實(shí)戰(zhàn)之方塊游戲的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Qt方塊游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++課程設(shè)計(jì)之學(xué)生成績(jī)管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++課程設(shè)計(jì)之學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12
Qt連接數(shù)據(jù)庫(kù)并實(shí)現(xiàn)數(shù)據(jù)庫(kù)增刪改查的圖文教程
QT連接數(shù)據(jù)庫(kù)是應(yīng)用開(kāi)發(fā)的常用基礎(chǔ)操作,經(jīng)過(guò)實(shí)驗(yàn)我總結(jié)了一些例程,下面這篇文章主要給大家介紹了關(guān)于Qt連接數(shù)據(jù)庫(kù)并實(shí)現(xiàn)數(shù)據(jù)庫(kù)增刪改查的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-04-04
減少C++代碼編譯時(shí)間的簡(jiǎn)單方法(必看篇)
下面小編就為大家?guī)?lái)一篇減少C++代碼編譯時(shí)間的簡(jiǎn)單方法(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-01-01
C語(yǔ)言實(shí)現(xiàn)24點(diǎn)游戲計(jì)算器的示例代碼
24點(diǎn)是一種益智游戲,24點(diǎn)是把4個(gè)整數(shù)(一般是正整數(shù))通過(guò)加減乘除以及括號(hào)運(yùn)算,使最后的計(jì)算結(jié)果是24的一個(gè)數(shù)學(xué)游戲,24點(diǎn)可以考驗(yàn)人的智力和數(shù)學(xué)敏感性,它能在游戲中提高人們的心算能力。本文將用C語(yǔ)言實(shí)現(xiàn)這一游戲,感興趣的可以了解一下2022-08-08

