QT實現貪吃蛇遊戲

為瞭熟悉QT的相關知識,我用瞭大約8個小時的時間用QT再次寫瞭一遍貪吃蛇。

因為QT的機制和平時寫的程序流程不同,所以程序中可能沒有遵守代碼規范。

運行效果:

程序內除瞭實現貪吃蛇的基本功能外,還添加瞭記錄得分、調節遊戲速度、重新開始遊戲等功能。

遊戲內容使用QPainter類進行繪制。

編譯環境:

Windows Qt 5.9.0 Qt Creator 4.3.0

思路:

數組存儲蛇節點坐標的貪吃蛇遊戲思路。

因為編寫過程中使用瞭分文件編寫,且QT框架的其他部分較多,這裡僅展示程序實現主要代碼。

代碼:

MainWindow.h 主窗口類頭文件

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPainter>     //畫傢類
#include "snake.h"      //蛇類
#include <QKeyEvent>    //鍵盤事件
#include "food.h"       //食物類
#include <QTimer>       //定時器類

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = 0);
    ~Widget();

    //繪圖事件
    void paintEvent(QPaintEvent *);

    //遊戲結束的情況
    void GameOver();

private:
    Ui::Widget *ui;

    Snake snake;        //蛇對象
    char key = 'd';     //接收鍵值,初始向右
    Food food;          //食物對象
    //創建定時器
    QTimer *timer = new QTimer(this);
    bool gameflag = false;//遊戲結束標記
    int score = 0;      //記錄得分
    int timerspeed = 200; //定時器速度
    bool bugflag = false; //用於解決操控過快導致撞死自己的bug的標志

    //鍵盤事件,改變鍵值
    void keyPressEvent(QKeyEvent *ev);
};

#endif // WIDGET_H

MainWindow.cpp函數實現

#include "MainWindow.h"
#include "ui_widget.h"
#include <QLabel>
#include <QPushButton>

//主窗口構造函數
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);      //初始化出.ui界面

    //一個格子寬高25像素,地圖寬高600像素,每行有24個格子

    //設置固定窗口大小
    setFixedSize(1000,600);

    //設置得分標簽控件的屬性
    ui->label->setFont(QFont("黑體", 20, QFont::Bold, false));

    //設置遊戲速度標簽內容
    ui->label_2->setFont(QFont("楷體", 16, QFont::Bold, false));
    ui->label_2->setText("遊戲速度:");

    //滑動條控件,這裡將數值按一定比例放大縮小
    ui->horizontalSlider->setValue(200 >> 2);

    //監聽滑動條控件滑動時,改變定時器速度為滑動條的值
    connect(ui->horizontalSlider,QSlider::valueChanged,[=]()
    {
        timerspeed = ui->horizontalSlider->value() << 2;
    });

    //設置重新開始按鈕屬性和內容
    ui->pushButton_3->setFont(QFont("黑體",12));
    ui->pushButton_3->setText("重新開始");

    //監聽重新開始按鈕
    connect(ui->pushButton_3,QPushButton::clicked,[=]()
    {
        snake.Reset();      //重置蛇
        score = 0;          //得分清0
        key = 'd';          //key值向右
        timerspeed = 200 << 2;//重置遊戲速度
        timer->start(timerspeed);//重置定時器
        ui->horizontalSlider->setValue(200 >> 2);//重置滑動條
        gameflag = false;   //重置遊戲結束標志
    });

    //開啟定時器
    timer->start(timerspeed);

    //監聽定時器的信號,執行表達式內容
    connect(timer,&QTimer::timeout,[=]()
    {
        timer->start(timerspeed); //調整定時器的速度

        snake.Move(key);    //蛇移動函數

        //吃到食物的情況
        (food.getfoodX() == snake.snakevec.at(0).x && food.getfoodY() == snake.snakevec.at(0).y) ?
        score++, food.randfood():    //得分+1,隨機產生一個食物坐標
        snake.snakevec.pop_back();   //刪除蛇尾

        GameOver();    //遊戲失敗的情況

        update();      //調用繪圖函數

        //更新標簽控件的文本內容
        ui->label->setText(QString("得分:%1").arg(score));
    });
}

//遊戲失敗情況
void Widget::GameOver()
{
    //撞墻失敗
    if(snake.snakevec.at(0).x >= 24 * 25 || snake.snakevec.at(0).x < 0 ||
       snake.snakevec.at(0).y < 0 || snake.snakevec.at(0).y >= 24 * 25)
    {
        gameflag = true;
        timer->stop();      //暫停定時器
        return;
    }

    //撞自己失敗
    for(int i = 1; i < snake.snakevec.size(); i++)
    {
        if(snake.snakevec.at(0).x == snake.snakevec.at(i).x &&
           snake.snakevec.at(0).y == snake.snakevec.at(i).y)
            gameflag = true, timer->stop();
    }
}


//繪圖事件
void Widget::paintEvent(QPaintEvent *)
{
    //創建畫傢對象,指定繪圖設備為this主窗口
    QPainter painter(this);

    //設置畫刷為紅色
    QBrush brush1(Qt::red);
    //讓畫傢使用畫刷
    painter.setBrush(brush1);
    //畫食物
    painter.drawRect(QRect(food.getfoodX(),food.getfoodY(),25,25));

    //設置畫刷為黑色
    QBrush brush2(Qt::blue);
    painter.setBrush(brush2);

    //遍歷蛇坐標容器,畫出代表蛇身的矩形
    for(auto snakenodei : snake.snakevec)
        //蛇頭畫圓形
        (snakenodei.x == snake.snakevec.at(0).x &&
         snakenodei.y == snake.snakevec.at(0).y) ?
        //蛇頭畫圓形
        painter.setRenderHint(QPainter::Antialiasing),  //用抗鋸齒畫圓
        painter.drawEllipse(QPoint(snakenodei.x + 12,snakenodei.y + 12),12,12):
        //蛇身畫矩形
        painter.drawRect(QRect(snakenodei.x,snakenodei.y,25,25));

    bugflag = false;    //在畫出蛇之後將標志設為假

    //設置畫刷為黑色
    QBrush brush3(QColor(128, 64, 0));
    painter.setBrush(brush3);

    //畫出墻壁
    for(int i = 0; i < 24; i++)
          painter.drawRect(QRect(25 * 25, i * 25, 25, 25));

    //遊戲結束時的繪圖
    if(gameflag)
    {   //設置文字屬性
        QFont font("楷體", 80, QFont::Bold, false);
        painter.setFont(font);      //使用字體
        painter.drawText(QRect(240, 200, 1000, 500), "遊戲結束");   //畫出遊戲結束文字
    }
}

//鍵盤事件,改變貪吃蛇移動方向
void Widget::keyPressEvent(QKeyEvent *ev)
{
    if(bugflag)     //bug標志為真直接返回
        return;

    switch (ev->key())
    {
    case Qt::Key_W:
        if(key == 's') break;   //按下與當前方向相反按鍵時break
        key = 'w';
        bugflag = true; break;        //按鍵之後將標志設為真
    case Qt::Key_S:
        if(key == 'w') break;
        key = 's';
        bugflag = true; break;
    case Qt::Key_A:
        if(key == 'd') break;
        key = 'a';
        bugflag = true; break;
    case Qt::Key_D:
        if(key == 'a') break;
        key = 'd';
        bugflag = true; break;
    case Qt::Key_Space:     //空格鍵暫停,這裡有點問題
        timer->stop(); break;
    default:                //其他鍵重啟定時器
        timer->start(timerspeed);
    }
}

//析構
Widget::~Widget()
{
    delete ui;
}

snake.h 蛇類頭文件

#ifndef SNAKE_H
#define SNAKE_H

#include <QWidget>
#include <QVector>      //Qt動態數組類

class Snake : public QWidget
{
    Q_OBJECT
private:
    //蛇坐標結構
    struct  Snakecoor
    {
        int x;
        int y;
    };

public:
    explicit Snake(QWidget *parent = nullptr);

    //蛇坐標容器
    QVector<Snakecoor> snakevec;

    //蛇移動主要函數
    void Move(char key);

    //根據key值確定蛇的移動方向
    void DeteDirMove(char key, Snakecoor &nexthead);

    //重置蛇
    void Reset();

signals:

public slots:
};

#endif // SNAKE_H

snake.cpp 蛇類函數實現

#include "snake.h"

//構造函數初始化一條蛇
Snake::Snake(QWidget *parent) : QWidget(parent)
{
    Snakecoor snakenode;            //臨時結構變量用於創建蛇
    for (int i = 5; i >= 0; i--) //從右向左創建5節蛇身,容器的第一個元素為蛇頭
    {
        snakenode.x = 25 * 8 + i * 25;//每個格子寬25像素
        snakenode.y = 25 * 8;
        snakevec.push_back(snakenode);//將蛇節點插入到數組中
    }
}

//確定蛇的移動方向,確定新蛇頭的坐標位置
inline void Snake::DeteDirMove(char key, Snakecoor &nexthead)
{
    switch (key)
    {
    case 'w':
        //新蛇頭坐標根據舊蛇頭坐標改變
        nexthead.x = snakevec.front().x;
        nexthead.y = snakevec.front().y - 25;
        break;
    case 's':
        nexthead.x = snakevec.front().x;
        nexthead.y = snakevec.front().y + 25;
        break;
    case 'a':
        nexthead.x = snakevec.front().x - 25;
        nexthead.y = snakevec.front().y;
        break;
    case 'd':
        nexthead.x = snakevec.front().x + 25;
        nexthead.y = snakevec.front().y;
    }
}

//蛇移動主要函數
void Snake::Move(char key)
{
    Snakecoor nexthead;  //創建一個新的蛇頭

    DeteDirMove(key, nexthead);  //確定新蛇頭的位置

    snakevec.insert(snakevec.begin(), nexthead);    //將新蛇頭插入到容器頭部
}

//重置蛇函數
void Snake::Reset()
{
    snakevec.clear();   //清空容器

    //初始化蛇
    Snakecoor snakenode;
    for (int i = 5; i >= 0; i--)
    {
        snakenode.x = 25 * 8 + i * 25;
        snakenode.y = 25 * 8;
        snakevec.push_back(snakenode);
    }
}

food.h 食物類頭文件

#ifndef FOOD_H
#define FOOD_H

#include <QWidget>

class Food : public QWidget
{
    Q_OBJECT
public :
    int food_x = 0;     //食物坐標
    int food_y = 0;

public:
    explicit Food(QWidget *parent = nullptr);

    void randfood();    //隨機產生食物坐標
    int getfoodX();     //返回食物坐標
    int getfoodY();

signals:

public slots:
};

#endif // FOOD_H

food.cpp 食物類函數實現

#include "food.h"
#include <ctime>

Food::Food(QWidget *parent) : QWidget(parent)
{
    randfood();     //創建對象時產生就一個隨機坐標

}

//隨機產生食物坐標
void Food::randfood()
{
    srand((unsigned)time(NULL));
    food_x = (rand() % 24) * 25;
    food_y = (rand() % 24 )* 25;
}

//獲取食物的坐標
int Food::getfoodX()
{
    return food_x;
}

int Food::getfoodY()
{
    return food_y;
}

以上就是本文的全部內容,希望對大傢的學習有所幫助,也希望大傢多多支持WalkonNet。

推薦閱讀: