溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Qt怎么實(shí)現(xiàn)方塊游戲

發(fā)布時(shí)間:2023-03-17 10:40:07 來(lái)源:億速云 閱讀:135 作者:iii 欄目:開(kāi)發(fā)技術(shù)

這篇“Qt怎么實(shí)現(xiàn)方塊游戲”文章的知識(shí)點(diǎn)大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價(jià)值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來(lái)看看這篇“Qt怎么實(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類(lèi),之所以繼承自這個(gè)類(lèi),是因?yàn)檫@樣就可以使用信號(hào)和槽機(jī)制,話(huà)可以使用屬性動(dòng)畫(huà)。小正方形就是一個(gè)寬和高都為20像素的正方形圖形項(xiàng)。游戲中的方塊游戲由方塊組BoxGroup類(lèi)來(lái)實(shí)現(xiàn),繼承自QObject和QGraphicsItemGroup類(lèi),這樣該類(lèi)也可以使用信號(hào)和槽機(jī)制。方塊組是一個(gè)寬和高都是80像素的圖形項(xiàng)組,其中包含了4個(gè)小方塊,通過(guò)設(shè)置小方塊的位置來(lái)實(shí)現(xiàn)7種標(biāo)準(zhǔn)的方塊圖形。它們的形狀和位置如下圖,在BoxGroup類(lèi)中實(shí)現(xiàn)了方塊圖形的創(chuàng)建、移動(dòng)和碰撞檢測(cè)。

Qt怎么實(shí)現(xiàn)方塊游戲

方塊移動(dòng)區(qū)域在游戲場(chǎng)景中使用四條直線(xiàn)圖形項(xiàng)所圍成區(qū)域來(lái)表示,之所以要這樣實(shí)現(xiàn),是因?yàn)檫@樣可以通過(guò)方塊組是否與直線(xiàn)圖形項(xiàng)碰撞來(lái)檢測(cè)是否移動(dòng)出界。整個(gè)游戲界面由MyView類(lèi)來(lái)實(shí)現(xiàn),該類(lèi)繼承自QGraphicsView類(lèi),實(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ā)生了碰撞,先消除滿(mǎn)行的方格,然后出現(xiàn)新的方塊組,并繼續(xù)進(jìn)行整個(gè)流程。其中方程塊的移動(dòng)、旋轉(zhuǎn)、碰撞檢測(cè)等都在BoxGroup類(lèi)中進(jìn)行;游戲的開(kāi)始、結(jié)束、出現(xiàn)新的方程組、消除滿(mǎn)行等都在MyView類(lèi)中進(jìn)行。游戲程序圖以及方塊組移動(dòng)和旋轉(zhuǎn)流程圖如下:

Qt怎么實(shí)現(xiàn)方塊游戲

2.1、方塊組的移動(dòng)和旋轉(zhuǎn)

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

Qt怎么實(shí)現(xià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、消除滿(mǎn)行

游戲開(kāi)始后,每當(dāng)出現(xiàn)一個(gè)新的方塊以前,都判斷游戲移動(dòng)區(qū)域的每一行是否已經(jīng)擁有10個(gè)小方塊。如果有一行已經(jīng)擁有了10個(gè)小方塊,說(shuō)明改行已滿(mǎn),那么就銷(xiāo)毀該行的所有小方塊,然后讓該行上面的所有小方塊都下移一格。

3.效果圖

Qt怎么實(shí)現(xiàn)方塊游戲

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表示不能改變類(lèi)的成員
    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_H

box.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à),框外線(xiàn)條用畫(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移除,不銷(xiāo)毀方塊組
            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的情況下)則銷(xiāo)毀掉
//其本質(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)被刪除,即銷(xiā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í)是該子類(lèi)中的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ù)是父類(lèi)的函數(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是為了防止隱式類(lèi)型轉(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) {   //如果一行已滿(mǎn),則銷(xiāo)毀該行的所有小方塊
            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;//將滿(mǎn)行的行號(hào)保存到rows中
        }
    }
 
    //如果滿(mǎn)行,則下移上面的方塊
    if(rows.count()>0) {
//     move_box();
        QTimer::singleShot(400,this,SLOT(move_box()));
    }
    else {
        //沒(méi)有滿(mǎn)行,則新出現(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);//取出滿(mǎn)行的行號(hào),從最上面的位置開(kāi)始
        //取出從區(qū)域上邊界到當(dāng)前滿(mǎn)行之間所形成的矩形區(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);
 
    //銷(xiāo)毀當(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條線(xiàn),與外界預(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è)類(lèi)
    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)類(lèi)似于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類(lèi)型,而是QGraphicsItem類(lèi)型,后面的類(lèi)似
    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)用的視圖類(lèi)
    view.show();
 
    return app.exec();
}

以上就是關(guān)于“Qt怎么實(shí)現(xiàn)方塊游戲”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對(duì)大家有幫助,若想了解更多相關(guān)的知識(shí)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

qt
AI