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

news2024/11/24 20:41:42

用Qt实现一个贪吃蛇项目

  • 一、项目介绍
  • 二、游戏大厅界面实现
    • 2.1完成游戏大厅的背景图。
    • 2.2创建一个按钮,给它设置样式,并且可以跳转到别的页面
  • 三、难度选择界面实现
  • 四、 游戏界面实现
  • 五、在文件中写入历史战绩
    • 5.1 从文件里提取分数
    • 5.2 把贪吃蛇的长度存入文件
  • 六、总结

一、项目介绍

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1873850.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

华为OceanStor磁盘阵列存储恢复出厂设置命令 LUN不处于在线状态,不能执行此操作解决方案

环境 OceanStor S2600T V2老版本 客户现场有一台Oceanstor 2600 V2的存储&#xff0c;因和另一台磁盘扩展框做了跨设备LUN需要进行配置清除&#xff0c;配置结束后需要重新划分存储空间并对接服务器&#xff0c;保证业务能够正常上线&#xff01;在清除配置回退的过程中&#…

Firefox 编译指南2024 Windows10篇- 编译Firefox(三)

1.引言 在成功获取了Firefox源码之后&#xff0c;下一步就是将这些源码编译成一个可执行的浏览器。编译是开发流程中的关键环节&#xff0c;通过编译&#xff0c;我们可以将源代码转换为可执行的程序&#xff0c;测试其功能&#xff0c;并进行必要的优化和调试。 对于像Firef…

Milvus【部署 01】向量数据库Milvus在Linux环境下的在线+离线安装

向量数据库Milvus在Linux环境下的在线离线安装 1.千问简介2.在线安装2.离线安装 1.千问简介 Milvus 是一款专为处理高维向量数据设计的开源云原生数据库&#xff0c;旨在满足海量向量数据的实时召回需求。它由 Zilliz 公司开发并维护&#xff0c;基于Apache许可证2.0版本发布。…

选择适合你的8款原型设计工具

随着互联网的飞速发展&#xff0c;设计行业逐渐成为近年来的热门职业。设计师们需要的掌握的技能也越来越多&#xff0c;例如海报设计、名片设计、产品设计、网页设计等。产品原型设计就是产品设计中非常重要的一个阶段&#xff0c;主要目的是帮助用户更容易了解产品设计的思路…

YouTube广告投放指南:如何投放 YouTube视频广告

在海外广告投放中&#xff0c;YOutube是重要的渠道之一。这篇文章Maskfog将为你介绍Youtube广告类型以及广告投放流程&#xff0c;继续看下去&#xff01; YouTube 视频广告的类型 1.信息流视频广告 信息流视频广告显示在 YouTube 主页、搜索结果页面上&#xff0c;并作为 Yo…

【pytorch10】统计属性

常见统计属性 norm&#xff08;范数&#xff09;mean,sumprodmax&#xff0c;min&#xff0c;argmin&#xff0c;argmaxkthvalue&#xff0c;topk kthvalue求第几个的位置和第几个的值 topk求top几的这样的一个数值 norm范数 这里的norm表达的是范数的意思&#xff0c;norma…

wait/notify 的原理

目录 一、wait/notify 的原理 二、虚假唤醒&#xff08;错误唤醒&#xff09; 三、使用 wait/notify 的正确姿势 一、wait/notify 的原理 1. owner 线程发现有条件不满足&#xff0c;调用 wait() 进入 Monitor 的 WaitSet 等待&#xff0c;切换为 Waiting 状态 2. 问题&…

老生常谈问题之什么是缓存穿透、缓存击穿、缓存雪崩?举个例子你就彻底懂了!!

老生常谈问题之什么是缓存穿透、缓存击穿、缓存雪崩&#xff1f;举个例子你就彻底懂了&#xff01;&#xff01; 缓存穿透发生场景解决方案 缓存击穿解决方案 缓存雪崩发生场景解决方案 总结三者区分三者原因三者解决方案 想象一下&#xff0c;你开了一家便利店&#xff0c;店里…

Springboot Mybatis 多数据源配置以及使用

在Spring Boot中配置MyBatis的多数据源是一个常见需求&#xff0c;尤其是在需要连接多个数据库时&#xff0c;下面是详细的步骤指南。 引入依赖 首先&#xff0c;在你的pom.xml文件中添加Spring Boot、MyBatis和数据库连接的相关依赖。例如&#xff0c;如果你使用的是MySQL数…

Boosted Trees 介绍

Boosted Trees 介绍 文章目录 Boosted Trees 介绍监督学习要素模型和参数目标函数&#xff1a;训练损失 正则化为什么介绍一般原则&#xff1f; 决策树集成Tree BoostingAdditive TrainingModel ComplexityThe Structure ScoreLearn the tree structure 关于 XGBoost 的最后话…

Kotlin vs Java:深入解析两者之间的最新差异与优劣(全面指南)

文章目录 1. 概述2. 语法简洁性3. 空安全4. 扩展函数5. 协程6. 数据类7. 智能类型转换8. 默认参数与命名参数9. 无 checked exceptions10. 单例模式总结 &#x1f389;欢迎来到Java学习路线专栏~探索Java中的静态变量与实例变量 ☆* o(≧▽≦)o *☆嗨~我是IT陈寒&#x1f379;✨…

某麦网自动刷新抢票脚本——手机端(高级版)

某麦网自动刷新抢票脚本——电脑端 小白操作-抵制黄牛–需要更好用更高级关注获取 如何用Python自动抢大麦网演出票&#xff1f; 在数字化时代&#xff0c;购票已经成为我们生活的一部分&#xff0c;无论是音乐会、话剧、体育赛事还是各种展览&#xff0c;抢票几乎成了一项“…

【隐私计算】对SIMD编码的粗浅理解

首先需要知道&#xff0c;同态加密是在多项式上进行的&#xff0c;基于RLEW的整体流程如下&#xff1a; 将单个数编码到一个N阶&#xff08;N项&#xff09;多项式中&#xff0c;多项式系数的利用率极低。而在神经网络中&#xff0c;我们需要计算的东西往往是一个很大的矩阵/te…

使用 nvm 管理 Node 版本及 pnpm 安装

文章目录 GithubWindows 环境Mac/Linux 使用脚本进行安装或更新Mac/Linux 环境变量nvm 常用命令npm 常用命令npm 安装 pnpmNode 历史版本 Github https://github.com/nvm-sh/nvm Windows 环境 https://nvm.uihtm.com/nvm.html Mac/Linux 使用脚本进行安装或更新 curl -o- …

可用的微信小程序抓包方式(Charles + bp)

扫码领资料 获网安教程 前言 接到对公司小程序进行渗透的任务&#xff0c;尝试了网上几种对小程序抓包的方式&#xff08;BurpProxifier、Burp安卓模拟器等&#xff09;都无法完成抓包&#xff0c;可能已经失效&#xff0c;结合不同的文章尝试了bpCharles&#xff0c;成功抓包…

支持内嵌H5页面,谈谈微信小程序的应用场景

目录 一、微信小程序介绍二、什么是内嵌H5页面三、小程序内嵌H5页面的如何交互四、微信小程序的应用场景 一、微信小程序介绍 微信小程序是一种基于微信平台的轻量级应用&#xff0c;它无需下载安装&#xff0c;用户只需通过微信扫一扫或搜索即可快速打开使用。与传统的APP相比…

新火种AI|苹果要将苹果智能做成AI时代的APP Store?

作者&#xff1a;一号 编辑&#xff1a;美美 苹果还是想要自己做AI时代的“APP Store”。 自从去年开始落了队&#xff0c;苹果现在AI上开始高歌猛进。今年WWDC上展示的AI产品和与OpenAI的合作只是开始。有消息称&#xff0c;苹果正与Meta等AI巨头展开深入合作&#xff0c;这…

二、安装虚拟机

本篇来源&#xff1a;山海同行 本篇地址&#xff1a;https://shanhaigo.cn/courseDetail/1805875642621952000 本篇资源&#xff1a;以整理到-山海同行 一、官网下载centos7 1. 进入CentOS 官方网站 官方网站&#xff1a;https://www.centos.org/download/ 2. 选择iso 点击下…

数据库自动备份到gitee上,实现数据自动化备份

本人有个不太好的习惯&#xff0c;每次项目的数据库都是在线上创建&#xff0c;Navicat 连接线上数据库进行处理&#xff0c;最近有一个项目需要二次升级&#xff0c;发现老项目部署的服务器到期了&#xff0c;完蛋&#xff0c;数据库咩了&#xff01;&#xff01;&#xff01;…

IP地址查询和代理服务器:双重保护隐私

随着网络应用的日益普及&#xff0c;我们的个人信息和数据安全面临前所未有的挑战。在此背景下&#xff0c;IP地址查询和代理服务器成为保护个人隐私和网络安全的两大关键工具。本文将从IP地址查询的原理和应用出发&#xff0c;深入剖析代理服务器在网络隐私保护中的作用&#…