Qt—贪吃蛇项目(由0到1实现贪吃蛇项目)

发布于:2024-06-28 ⋅ 阅读:(18) ⋅ 点赞:(0)

一、项目介绍

贪吃蛇是久负盛名的游戏,它也和俄罗斯⽅块,扫雷等游戏位列经典游戏的⾏列。 在编程语⾔的教学中,我们以贪吃蛇为例,从设计到代码实现来提升编程能⼒和逻辑能⼒。它通过控制蛇头⽅向吃⻝物,从⽽使得蛇变得越来越⻓。在本游戏中设置了上下左右四个⽅向键来控制蛇的移动⽅向。⻝物的产⽣是随机⽣成的,当蛇每吃⼀次⻝物 就会增加⼀节⾝体,同时游戏积分也会相应的加⼀。
在本游戏的设计中,蛇的⾝体会越吃越⻓,⾝体越⻓对应的难度就越⼤,因为⼀旦蛇头和⾝体相交游戏就会结束。
本项⽬使⽤ Qt 实现⼀款简单的贪吃蛇游戏。
项目的目标:

  1. 游戏大厅界面实现
  2. 难度选择界面实现
  3. 游戏界面实现
  4. 分数记录界面实现

二、游戏大厅界面实现

2.1完成游戏大厅的背景图。

1.背景图的渲染,我们通过QT的绘图事件完成

void gamehall::paintEvent(QPaintEvent *event)
{
 	 //实例化一个画家
    QPainter paint(this);
    //实例化一个设备
    QPixmap pix(":res/game_hall.png");
    //进行绘画
    paint.drawPixmap(0,0,this->width(),this->height(),pix);
 }

2.对我们的标题栏进行设置

 	//设置窗口大小
    this->setFixedSize(1000,800);
    //设置标题栏图标
    this->setWindowIcon(QIcon(":res/ico.png"));
    //设置标题栏标签
    this->setWindowTitle("游戏大厅");

在这里插入图片描述

2.2创建一个按钮,给它设置样式,并且可以跳转到别的页面

1.创建一个按钮,给它设置样式

  QFont font("华文行楷",25);
    //设置按钮
    QPushButton *intoBtn= new QPushButton(this);
    intoBtn->setFont(font);
    intoBtn->setText("开始游戏");
    intoBtn->setStyleSheet("QPushButton{border:0px;}");
    intoBtn->move(450,520);
    

2.创建的新页面
点击add new
在这里插入图片描述
3. 建立信号槽,使按钮被点击后跳转界面(这个跳转的界面,是新创建的文件,点击按钮会发出声音)

	//创建另一个窗口
    GameSelect *select=new GameSelect();
    connect(intoBtn,&QPushButton::clicked,[=]{
        //关闭上个窗口
        this->close();
        //设置窗口大小
        select->setGeometry(this->geometry());
        //显示窗口
        select->show();
        //发出声音
         QSound::play(":res/clicked.wav");

在这里插入图片描述
4.总代码

#include "gamesnake.h"
#include "ui_gamesnake.h"
#include "gameselect.h"
#include <QPainter>
#include <QPixmap>
#include <QIcon>
#include <QPushButton>
#include <QSound>
GameSnake::GameSnake(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::GameSnake)
{
    ui->setupUi(this);
    //设置窗口大小
    this->setFixedSize(1000,800);
    //设置窗口图标
    this->setWindowIcon(QIcon(":res/ico.png"));
    //设置窗口标签
    this->setWindowTitle("游戏大厅");
    //设置字体
    QFont font("华文行楷",25);
    //设置按钮
    QPushButton *intoBtn= new QPushButton(this);
    intoBtn->setFont(font);
    intoBtn->setText("开始游戏");
    intoBtn->setStyleSheet("QPushButton{border:0px;}");
    intoBtn->move(450,520);
    //创建另一个窗口
    GameSelect *select=new GameSelect();
    connect(intoBtn,&QPushButton::clicked,[=]{
        //关闭上个窗口
        this->close();

        select->setGeometry(this->geometry());
        select->show();
        QSound::play(":res/clicked.wav");



   });
}

GameSnake::~GameSnake()
{
    delete ui;
}

void GameSnake::paintEvent(QPaintEvent *event)
{
    //实例化一个画家
    QPainter paint(this);
    //实例化一个设备
    QPixmap pix(":res/game_hall.png");
    //进行绘画
    paint.drawPixmap(0,0,this->width(),this->height(),pix);
}

注意pro文件里的配置加上multimedai

三、难度选择界面实现

我们已经把贪吃蛇的游戏界面完成了,下面就进入关卡选择界面。
在关卡选择界⾯上设置了四个游戏模式按钮,分别是:容易模式、正常模式、困难模式、地狱模式;⼀个 “历史战绩” 按钮;⼀个返回游戏⼤厅界⾯的按钮。
1. 创建按钮,给它设置样式。

    //容易关卡
   QPushButton *briefnessBtn= new QPushButton(this);
   briefnessBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
   briefnessBtn->move(400,200);
   QFont font("华文行楷",25);
   briefnessBtn->setFont(font);
   briefnessBtn->setText("容易模式");

如图,我已经创建了5个按钮:
在这里插入图片描述

2. 为了使第二个界面可以返回到第一个界面,创建一个按钮,按下按钮触发信号,就可以返回第一个页面。(返回按钮)

//返回按钮
QPushButton *returnBtn= new QPushButton(this);
returnBtn->move(850,700);
returnBtn->setIcon(QIcon(":res/up.png"));

connect(returnBtn,&QPushButton::clicked,[=]{
    this->close();
    GameSnake *snake= new GameSnake();
    snake->show();
    QSound::play(":res/clicked.wav");
        });

在这里插入图片描述
3. 游戏选择页面跳转到游戏大厅
实现页面之间的跳转

 //实现游戏房间和选择关卡切换
    //创建另一个窗口
    GameRoom *room=new GameRoom();


    //创建按钮,给按钮设置格式
    //容易关卡
   QPushButton *briefnessBtn= new QPushButton(this);
   briefnessBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
   briefnessBtn->move(400,200);
   QFont font("华文行楷",25);
   briefnessBtn->setFont(font);
   briefnessBtn->setText("容易模式");
   connect(briefnessBtn,&QPushButton::clicked,[=]{
       //关闭上个窗口
       this->close();

       room->setGeometry(this->geometry());
       room->show();
       QSound::play(":res/clicked.wav");
  });

以第一个按钮为例,运行后点击简单模式效果如下(这是一个新的界面):
在这里插入图片描述
4. 总代码

#include "gameselect.h"
#include <QPainter>
#include <QPixmap>
#include <QPushButton>
#include "gamesnake.h"#
#include "gameroom.h"
#include <QSound>
GameSelect::GameSelect(QWidget *parent) : QWidget(parent)
{
    this->setFixedSize(1000,800);
    //设置窗口图标
    this->setWindowIcon(QIcon(":res/ico.png"));
    //设置窗口标签
    this->setWindowTitle("选择大厅");

   //实现游戏房间和选择关卡切换
    //创建另一个窗口
    GameRoom *room=new GameRoom();


    //创建按钮,给按钮设置格式
    //容易关卡
   QPushButton *briefnessBtn= new QPushButton(this);
   briefnessBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
   briefnessBtn->move(400,200);
   QFont font("华文行楷",25);
   briefnessBtn->setFont(font);
   briefnessBtn->setText("容易模式");
   connect(briefnessBtn,&QPushButton::clicked,[=]{
       //关闭上个窗口
       this->close();

       room->setGeometry(this->geometry());
       room->show();
       QSound::play(":res/clicked.wav");

  });


   //正常关卡
  QPushButton *normalBtn= new QPushButton(this);
  normalBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
  normalBtn->move(400,280);
  QFont font1("华文行楷",25);
  normalBtn->setFont(font);
  normalBtn->setText("正常模式");
  connect(normalBtn,&QPushButton::clicked,[=]{
      //关闭上个窗口
      this->close();

      room->setGeometry(this->geometry());
      room->show();
      QSound::play(":res/clicked.wav");

 });

  //困难关卡
 QPushButton *difficultyBtn= new QPushButton(this);
 difficultyBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
 difficultyBtn->move(400,360);
 QFont font2("华文行楷",25);
 difficultyBtn->setFont(font);
 difficultyBtn->setText("困难模式");
 connect(difficultyBtn,&QPushButton::clicked,[=]{
     //关闭上个窗口
     this->close();

     room->setGeometry(this->geometry());
     room->show();
     QSound::play(":res/clicked.wav");

});

 //地狱关卡
QPushButton *bhellBtn= new QPushButton(this);
bhellBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
bhellBtn->move(400,440);
QFont font3("华文行楷",25);
bhellBtn->setFont(font);
bhellBtn->setText("地狱模式");
connect(bhellBtn,&QPushButton::clicked,[=]{
    //关闭上个窗口
    this->close();

    room->setGeometry(this->geometry());
    room->show();
    QSound::play(":res/clicked.wav");

});

//排行榜
QPushButton *ranpingBtn= new QPushButton(this);
ranpingBtn->setStyleSheet("QPushButton{background-color:#0072C6;color: black; border:0px groove gray;border-radius:10px;padding:6px;}");
ranpingBtn->move(400,520);
QFont font4("华文行楷",25);
ranpingBtn->setFont(font);
ranpingBtn->setText("历史战绩");

//返回按钮
QPushButton *returnBtn= new QPushButton(this);
returnBtn->move(850,700);
returnBtn->setIcon(QIcon(":res/up.png"));

connect(returnBtn,&QPushButton::clicked,[=]{
    this->close();
    GameSnake *snake= new GameSnake();
    snake->show();
    QSound::play(":res/clicked.wav");
        });
}
void GameSelect::paintEvent(QPaintEvent *event)
{
    //设置窗口大小
    this->setFixedSize(1000,800);
    //创建绘画者
    QPainter paint(this);
    //创建画板
    QPixmap pix(":res/game_select.png");
    paint.drawPixmap(0,0,this->width(),this->height(),pix);
}

在这里插入图片描述

注意:历史战绩还没有设计

四、 游戏界面实现

前两个页面已经实现完成了,最后完成游戏界面,我们就可以玩上自己写的游戏了。

游戏房间界⾯包含下⾯⼏个部分:
• 背景图的绘制。
• 蛇的绘制、蛇的移动、判断蛇是否会撞到⾃⼰ 。
• 积分的累加和绘制 在这⾥我们要考虑⼏个⽐较核⼼的问题:

  1. 怎么让蛇动起来?
    • 我们可以⽤⼀个链表表⽰贪吃蛇,⼀个⼩⽅块表⽰蛇的⼀个节点, 我们设置蛇的默认⻓度为3。
    • 向上移动的逻辑就是在蛇的上⽅加⼊⼀个⼩⽅块, 然后把最后⼀个⼩⽅块删除即可。
    • 需要⽤到定时器Qtimer 每100 - 200ms 重新渲染。
  2. 怎么判断蛇有没有吃到⻝物?
    • 判断蛇头和⻝物的坐标是否相交,Qt 有相关的接⼝调⽤。
  3. 怎么控制蛇的移动?
    • 借助QT的实践机制实现, 重写keyPressEvent即可, 在函数中监控想要的键盘事件即可 。
    • 我们通过绘制四个按钮,使⽤信号和槽的机制控制蛇的上、下、左、右移动⽅向。

1.先要对游戏界面进行渲染,用到pixmap和painter。

void GameRoom::paintEvent(QPaintEvent *event)
{
    qDebug("开始绘画");
    //创建绘画者
    QPainter paint(this);
    //创建画板,不带有参数
    QPixmap pix;
    //load方法,加载资源或者数据
    //游戏区域
    pix.load(":res/game_room.png");
    paint.drawPixmap(0,0,800,800,pix);
    //控制区域
    pix.load(":res/bg1.png");
    paint.drawPixmap(800,0,201,900,pix);
}

2.创建一个按钮,实现页面之间的跳转

  //创建一个返回按钮
    QPushButton* returnButton = new QPushButton(this);
    //设置位置和按钮照片
    returnButton->move(900,700);
    returnButton->setIcon(QIcon(":res/up.png"));
    //创建信号槽
    connect(returnButton,&QPushButton::clicked,[=]{
    //关闭当前页面
    this->close();
    GameSelect *gameRn = new GameSelect();
    //显示上个页面
    gameRn->show();
    QSound::play(":res/clicked.wav");
    });

如图:
在这里插入图片描述
3. 对游戏房间数据结构进行封装
gameroom的头文件

#define GAMEROOM_H

#include <QWidget>
//枚举蛇的移动方向
enum class kSnakeDirect
{
    UP = 0,
    DOWN,
    LEFT,
    RIGHT
};

class GameRoom : public QWidget
{
    Q_OBJECT
public:
    explicit GameRoom(QWidget *parent = nullptr);
    //重写绘画事件
    void paintEvent(QPaintEvent *event);
private:
    //表示蛇身体结点的宽度
    const int kSnakeNodeWidth = 20;
    //表示蛇身体节点的高度
    const int kSnakeNodeHeight = 20;
    //表示蛇的速度
    const int KDefountTimeout = 100;
    //表示贪吃蛇的链表
    QList<QRectF> snakeList;
    //表示食物的结点
    QRectF foodRect;
    //表示蛇初始方向的默认值
    kSnakeDirect moveDirect = kSnakeDirect::UP;
};

4.蛇移动方向的实现

注意: 这⾥贪吃蛇不允许直接掉头, ⽐如当前是向上的, 不能直接修改为向下。

分别声明:

void moveUp();//蛇向上移动
void moveDown();//蛇向下移动
void moveLeft();//蛇向左移动
void moveRight();//蛇向右移动

在这里插入图片描述
列如蛇向上移动的定义

void GameRoom::moveUp()
{
    QPointF leftTop; // 左上⻆坐标
     QPointF rightBottom; // 右下⻆坐标
     auto snakeNode = snakeList.front();
     int headX = snakeNode.x();
     int headY = snakeNode.y();
     // 如果上⾯剩余的空间不够放⼊⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑
     if(headY-kSnakeNodeWidth < 0) {
     leftTop = QPointF(headX, this->height() - kSnakeNodeHeight);
     } else {
     leftTop = QPointF(headX, headY - kSnakeNodeHeight);
     }
     rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);
     snakeList.push_front(QRectF(leftTop, rightBottom));
}

5.判断游戏是否结束
贪吃蛇游戏的结束标志是自己碰到自己,才算结束。

bool GameRoom::checkFail()
{
    for(int i=0;i<snakeList.size();i++)
    {
        for(int j=i+1;j<snakeList.size();j++)
        {
            if(snakeList.at(i)==snakeList.at(j))
            {
                 return true;//游戏结束
            }
        }
    }
    return false;//游戏正常运行
}

6.在绘图事件里把蛇绘画出来,并初始化贪吃蛇

 //绘制蛇:蛇头+蛇身+蛇尾
    //绘制蛇头的上下左右
    if(moveDirect==kSnakeDirect::UP)
    {
        pix.load(":res/rescopy.png");//加载资源
    }
    else if(moveDirect == kSnakeDirect::DOWN)
    {
        pix.load(":res/rescopy1.png");//加载资源
    }
    else if(moveDirect==kSnakeDirect::LEFT)
    {
        pix.load(":res/rescopy 2.png");//加载资源
    }
    else
    {
        pix.load(":res/rescopy3.png");//加载资源
    }
    //获取链表的头结点
    auto snakeHeadNode=snakeList.front();
    //画家绘画,给这个结点绘制pix里面的资源
   paint.drawPixmap(snakeHeadNode.x(),snakeHeadNode.y(),snakeHeadNode.width(),snakeHeadNode.height(),pix);

   //绘制蛇身
   pix.load(":res/rescopy 5.png");
   for(int i=1;i<snakeList.size()-1;i++)
   {
       auto node=snakeList.at(i);
       paint.drawPixmap(node.x()+2,node.y(),node.width()-3,node.height(),pix);
   }
   //绘制蛇尾
   auto TailNode = snakeList.back();
   paint.drawPixmap(TailNode.x()+2,TailNode.y(),TailNode.width()-3,TailNode.height(),pix);

在构造函数里,初始化贪吃蛇

  // 初始化贪吃蛇
     snakeList.push_back(QRectF(this->width() * 0.5, this->height() * 0.5,
    kSnakeNodeWidth, kSnakeNodeHeight));
     moveUp();
     moveUp();

7. 定义贪吃蛇的食物

//创建随机的食物
void GameRoom::creatNewFood()
{
    foodRect= QRectF(qrand()%(800/kSnakeNodeWidth)*kSnakeNodeWidth,qrand()%(800/kSnakeNodeHeight)*kSnakeNodeHeight,kSnakeNodeWidth,kSnakeNodeHeight);
}
//在绘图事件里,绘制食物
  //绘制食物
   pix.load(":res/food.bmp");
   paint.drawPixmap(foodRect.x(),foodRect.y(),foodRect.width(),foodRect.height(),pix);

8.让贪吃蛇移动起来

定时器的是为了实现每隔⼀段时间能处理移动的逻辑并且更新绘图事件。
• ⾸先, 需要判断蛇头和⻝物节点坐标是否相交 ◦ 如果相交, 需要创建新的⻝物节点, 并且需要更新蛇的⻓度, 所以 cnt 需要 +1 ;
• 如果不相交, 那么直接处理蛇的移动即可。
• 根据蛇移动⽅向 moveDirect 来处理蛇的移动, 处理⽅法是在前⽅加⼀个, 并且删除后⽅节点;
• 重新触发绘图事件, 更新渲染。

//在构造函数里编写
timer = new QTimer(this);
 connect(timer, &QTimer::timeout, this, [=](){
 int cnt = 1;
 // 判断是否贪吃蛇和⻝物是否相交
 if (snakeList.front().intersects(foodRect)) {
 createNewFood();
 ++cnt;
 //蛇吃食物的声音
 QSound::play(":res/eatfood.wav");
 }
 while(cnt--) {
 // 处理蛇的移动
 switch (moveDirect) {
 case SnakeDirect::UP:
 moveUp();
 break;
 case SnakeDirect::DOWN:
 moveDown();
 break;
 case SnakeDirect::LEFT:
 moveLeft();
 break;
 case SnakeDirect::RIGHT:
 moveRight();
 break;
 default:
 qDebug() << "⾮法移动⽅向";
 break;
 }
 }
 // 删除最后⼀个节点
 snakeList.pop_back();
 update();
 });

9.设置游戏开始和游戏暂停按钮

在刚进⼊游戏房间界⾯时,⼀定不能点击 “退出” 按钮,如果点击 “退出” 按钮,那么程 序就会异常退出。点击的顺序⼀定是:先点击 “开始” 按钮,最后才能点击 “退出” 。

    //设置开始暂停按钮
    QPushButton *startbtn = new QPushButton(this);
    QPushButton *stopbtn = new QPushButton(this);
    QFont ft("楷体", 20);
    //设置按钮的位置
    startbtn->move(860,150);
    stopbtn->move(860,200);
    //设置按钮⽂本
    startbtn->setText("开始");
    stopbtn->setText("暂停");
    //设置按钮样式
    startbtn->setStyleSheet("QPushButton{border:0px;}");
    stopbtn->setStyleSheet("QPushButton{border:0px;}");
    //设置按钮字体格式
    startbtn->setFont(ft);
    stopbtn->setFont(ft);
    //蛇移动
    connect(startbtn,&QPushButton::clicked,this,[=](){
        isGameStart = true;
        timer->start(kDefountTimeout);
        sound = new QSound(":res/Trepak.wav"); //声⾳路径
             sound->play(); //播放
             sound->setLoops(-1); //循环播放
    });
    //蛇暂停
    connect(stopbtn,&QPushButton::clicked,this,[=](){
        isGameStart = false;
        timer->stop();//停止工作
        sound->stop();//停止播放
    });

10. 设置蛇的方向控制

 //设置蛇2的移动方向
    QPushButton *Up = new QPushButton(this);
    QPushButton *Down = new QPushButton(this);
    QPushButton *Left = new QPushButton(this);
    QPushButton *Right = new QPushButton(this);
    Up->move(880,400);
    Down->move(880,480);
    Left->move(840,440);
    Right->move(920,440);
    Up->setStyleSheet("QPushButton{border:0px;}");
    Up->setFont(font);
    Up->setText("↑");
    Down->setStyleSheet("QPushButton{border:0px;}");
    Down->setFont(font);
    Down->setText("↓");
    Left->setStyleSheet("QPushButton{border:0px;}");
    Left->setFont(font);
    Left->setText("←");
    Right->setStyleSheet("QPushButton{border:0px;}");
    Right->setFont(font);
    Right->setText("→");
    connect(Up,&QPushButton::clicked,this,[=](){
        if(moveDirect != kSnakeDirect::DOWN)
            moveDirect = kSnakeDirect::UP;
    });
    connect(Down,&QPushButton::clicked,this,[=](){
        if(moveDirect != kSnakeDirect::UP)
            moveDirect = kSnakeDirect::DOWN;
    });
    connect(Left,&QPushButton::clicked,this,[=](){
        if(moveDirect != kSnakeDirect::RIGHT)
            moveDirect = kSnakeDirect::LEFT;
    });
    connect(Right,&QPushButton::clicked,this,[=](){
        if(moveDirect != kSnakeDirect::LEFT)
            moveDirect = kSnakeDirect::RIGHT;
        });

我们也可以用键盘来控制蛇的方向

void GameRoom::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_W :
    case Qt::Key_Up :
        if(moveDirect != kSnakeDirect::DOWN)
        moveDirect = kSnakeDirect::UP;
        break;
    case Qt::Key_S :
    case Qt::Key_Down :
        if(moveDirect != kSnakeDirect::UP)
        moveDirect=kSnakeDirect::DOWN;
        break;
    case Qt::Key_D :
    case Qt::Key_Right :
        if(moveDirect != kSnakeDirect::LEFT)
        moveDirect=kSnakeDirect::RIGHT;
        break;
    case Qt::Key_A :
    case Qt::Key_Left :
        if(moveDirect != kSnakeDirect::RIGHT)
        moveDirect=kSnakeDirect::LEFT;
        break;
        //开始
     case Qt::Key_F :
        isGameStart = true;
        timer->start(moveDefount);
        sound = new QSound(":res/Trepak.wav"); //声⾳路S径
        sound->play(); //播放
        sound->setLoops(-1); //循环播放
        break;
//      //暂停
     case Qt::Key_G :
        isGameStart = false;
        timer->stop();//停止工作
        sound->stop();//停止播放
        break;
    }
}

11.设置游戏退出按钮,并设置一个消息盒子。

    //设置退出按钮,并给他设置样式
    QPushButton *exitBtn=new QPushButton(this);
    exitBtn->setText("退出");
    exitBtn->move(870,750);
    exitBtn->setFont(font);
    //设置消息盒子
    QMessageBox *msg=new QMessageBox(this);
    QPushButton *okBtn =new QPushButton("OK");
    QPushButton *cancelBtn =new QPushButton("cancel");
    msg->addButton(okBtn,QMessageBox::AcceptRole);
    msg->addButton(cancelBtn,QMessageBox::RejectRole);
    msg->setWindowTitle("退出游戏");
    msg->setText("确认退出游戏吗?");
    connect(exitBtn,&QPushButton::clicked,[=]{
        //显示消息盒子
        msg->show();
         QSound::play(":res/clicked.wav");
        //事件轮询
        msg->exec();
        if(msg->clickedButton()==okBtn)
        {
            this->close();//关闭程序
        }
        else
        {
            msg->close();//关闭信息盒子
        }
    });

12. 分数的绘制

 //绘制分数
   pix.load(":res/sorce_bg.png");
   paint.drawPixmap(this->width()*0.85,this->height()*0.06,90,40,pix);
   QPen pen;
   pen.setColor(Qt::black);
   QFont font("方正舒体",22);
   paint.setPen(pen);
   paint.setFont(font);
   paint.drawText(this->width()*0.9,this->height()*0.1,QString("%1").arg(snakeList.size()));

13.游戏失败的绘制

   //绘制游戏失败效果
   if(checkFail())
   {
       pen.setColor(Qt::red);
       paint.setPen(pen);
       QFont font("方正舒体",50);
       paint.setFont(font);
       paint.drawText(this->width()*0.25,this->height()*0.5,QString("GAME OVER!"));
       timer->stop();
       QSound::play((":res/gameover.wav"));
       sound->stop();
   }

在这里插入图片描述
14.实现4种不同游戏模式
在这里插入图片描述
先对各个模式的时间进行设置,然后设置房间的定时器时间,从而改变游戏的模式。

room->setTimer(200);//改变各个模式的速度默认值

通过默认值去改变时间,从而设置游戏模式

在这里插入图片描述

五、在文件中写入历史战绩

通过文件的方式用来显示历史战绩

5.1 从文件里提取分数

读⽂件:读取写⼊⽂件中蛇的⻓度

connect(ranpingBtn,&QPushButton::clicked,[=]{
    //创建一个窗口控件
    QWidget *wGet=new QWidget();
    wGet->setWindowTitle("历史战绩");
    wGet->setFixedSize(500,300);
    //创建一个多行输入框
    QTextEdit *edit=new QTextEdit(wGet);

    edit->setFont(font);
    edit->setFixedSize(500,300);
    //打开文件,并且只能读,文件要自己创建
    QFile file("D:/QT_creator_3_25/greedy_snake1/Snake/res/1.txt");
    file.open(QIODevice::ReadOnly);
    //文本流对象
    QTextStream in(&file);
    //in.表示文件,把文件里的数据按行读取,并且转化为整数
    int data=in.readLine().toInt();
     edit->append("得分为:");
    //这里需要的是QString类型,把整形转化为字符串
    edit->append(QString::number(data));
    //显示窗口
    wGet->show();
    QSound::play(":res/clicked.wav");
    file.close();
});

在这里插入图片描述

5.2 把贪吃蛇的长度存入文件

写⽂件:往⽂件中写⼊蛇的⻓度

 //把分数存入文件
   int c=snakeList.size();
   QFile file("D:/QT_creator_3_25/greedy_snake1/Snake/res/1.txt");
   if(file.open(QIODevice::Text|QIODevice::WriteOnly))
   {
       //输入文本和只写
   QTextStream out(&file);
   out<<c;
       //关闭文件
    file.close();
   }

在这里插入图片描述
在这里插入图片描述

最后通过黑盒测试的方法,解决程序的一些问题,确保程序正常运行。(解决了按左右键,游戏就失败的问题;解决了蛇不在窗口跑的问题)

六、总结

做完这个项目,收获非常大。对qt代码的理解也更加深刻,同时也了解了一点额外的知识点
注意:

  1. QPixmap中的load函数是将图片加载到缓冲区,即QPixmapCache中存放图片,当把第一张图片加载到缓冲区会显示图片1,当加载第二张图片时缓冲区的图片1并没有被覆盖,实际是缓冲区存了两张图片,并显示图片2,当再次加载图片1到缓冲区时,这时QPixmapCache认为已经有了图片1,会直接返回true,缓冲区的图片排列顺序并没有发生变化,固依旧显示图片2。
    解决办法:
    (1)QPixmap直接定义局部变量,这样每次会开辟新的缓冲区
    (2)每次加载图片时先清空缓冲区,调用QPixmapCache::clear()
  2. QPointF就是Float QPoint,用法的话QPointF在浮点精度上表示平面上的点,绝大部分操作都是与QPoint相类似的,细微的差别在于运算符重载以及提供了QPoint与QPointF的相互转换。
  3. 在Qt中QList和QVector一般获取元素都是通过 at(index) 来获取得,但是at获取的元素返回值是一个const & 常引用,也就是元素不支持改变。
  4. snakeList.front().intersects(foodRect),判断两个矩形会不会相交。
  5. QTextStream文本流对象
    QTextStream out(&文件指针)
    QTextStream in(&文件指针)