贪吃蛇(Qt版)

目录

一、项目介绍

界面一:游戏大厅界面

界面二:关卡选择界面

界面三:游戏界面

最终游戏效果:

二、项目创建与资源配置

1. 创建项目

2. 添加项目资源文件

三、项目实现

1. 游戏大厅界面

2. 关卡选择界面

3. 游戏房间界面

3.1 封装贪吃蛇数据结构

3.2 初始化游戏房间界面

3.3 蛇的移动

3.4 初始化贪吃蛇本体和食物节点

3.5 实现定时器的超时槽函数

3.6 实现各个方向的移动

3.7 重写绘图事件函数进行渲染

3.8 检查是否自己会撞到自己

3.9 设置游戏开始和游戏暂停按钮

3.10 设置退出游戏按钮

3.11 获取历史战绩

四、项目源码


一、项目介绍

        贪吃蛇游戏是一款休闲益智类游戏。它通过控制蛇头方向吃食物,从而使得蛇变得越来越长。

        在本游戏中设置了上下左右四个方向键来控制蛇的移动方向。食物的产生是随机生成的,当蛇每吃一次食物就会增加一节身体,同时游戏积分也会相应的加一。

        在本游戏的设计中,蛇的身体会越吃越长,身体越长对应的难度就越大,因为一旦蛇头和身体相交游戏就会结束。


本项目使用 Qt 实现一款简单的贪吃蛇游戏。

主要界面如下:

界面一:游戏大厅界面

界面二:关卡选择界面

界面三:游戏界面


最终游戏效果:


二、项目创建与资源配置

1. 创建项目

🌴游戏大厅界面:

  • 打开Qt-Creator 创建项目。 注意项目名中不能包含空格、回车、中文等特殊字符

  • 选择基类为 QWidget, 类名为 GameHall 代表游戏大厅界面

 

  • 选择 MinGW 64-bit 编译套件


🌴关卡选择界面: 

  • 添加新文件 

  •  继承自 QWidget, 类名为 GameSelect 代表游戏关卡界面

  • 点击完成 


🌴游戏房间界面: 

  • 添加新文件 

  • 继承自 QWidget, 类名为 GameRoom 代表游戏房间界面

  • 点击完成 


2. 添加项目资源文件

  • 拷贝资源文件到项目文件中

  • 在项目中添加资源文件

  • 给资源文件起名为:res, 点击下一步

  • 点击完成 

  • 添加前缀 

  • 添加资源文件 

三、项目实现

1. 游戏大厅界面

游戏大厅界面比较简单, 只有一个背景图和一个按钮。

  • 背景图的渲染,我们通过QT的绘图事件完成。
#include <QPainter>void GameHall::paintEvent(QPaintEvent *event)
{(void)event;// 去掉警告小技巧QPainter painter(this);// 实例化画家对象QPixmap pix(":res/game_hall.png");// 实例化绘画设备painter.drawPixmap(0, 0, this->width(), this->height(), pix);// 绘画
}
  • 按钮的响应我们通过QT的信号和槽机制完成。
#include "gameselect.h"
#include <QIcon>
#include <QPushButton>
#include <QFont>
#include <QSound>GameHall::GameHall(QWidget *parent): QWidget(parent), ui(new Ui::GameHall)
{ui->setupUi(this);// 设置窗口信息this->setFixedSize(1000, 800);this->setWindowTitle("贪吃蛇游戏");this->setWindowIcon(QIcon(":res/ico.png"));// 设置开始游戏按钮QPushButton* startBtn = new QPushButton(this);startBtn->setText("开始游戏");startBtn->move(this->width() * 0.4, this->height() * 0.7);startBtn->setStyleSheet("QPushButton { border : 0px; }");QFont font("华文行楷", 23, QFont::ExtraLight, false);startBtn->setFont(font);// 点击按钮就会进入游戏关卡界面GameSelect* select = new GameSelect;connect(startBtn, &QPushButton::clicked, [=](){select->setGeometry(this->geometry());// 设置窗口固定this->close();// 关闭游戏大厅界面select->show();// 显示游戏关卡界面// 注意:使用 QSound 类时, 需要添加模块:multimediaQSound::play(":res/clicked.wav");// 添加按钮点击音效});
}

 效果示例:

2. 关卡选择界面

        关卡选择界面包含一个背景图和五个按钮,背景图的绘制和游戏大厅背景图绘制⼀样,同样使用的是Qt中的绘图事件。

  • 背景图的渲染,我们通过QT的绘图事件完成。
#include <QPainter>void GameSelect::paintEvent(QPaintEvent *event)
{(void)event;QPainter painter(this);// 实例化画家对象QPixmap pix(":res/game_select.png");// 实例化绘画设备painter.drawPixmap(0, 0, this->width(), this->height(), pix);// 绘画
}
  •  按钮的响应我们通过QT的信号和槽机制完成。
#include "gameselect.h"
#include "gamehall.h"
#include "gameroom.h"
#include <QIcon>
#include <QFont>
#include <QPushButton>
#include <QSound>
#include <QFile>
#include <QTextStream>
#include <QTextEdit>
#include <QDateTime>GameSelect::GameSelect(QWidget *parent) : QWidget(parent)
{// 设置游戏关卡界面this->setFixedSize(1000, 800);this->setWindowTitle("关卡选择");this->setWindowIcon(QIcon(":res/ico.png"));// 设置字体格式QFont font("华文行楷", 20, QFont::ExtraLight, false);// 创建返回按钮QPushButton* backBtn = new QPushButton(this);QPixmap pix(":res/back.png");backBtn->setIcon(pix);backBtn->move(this->width() * 0.9, this->height() * 0.9);connect(backBtn, &QPushButton::clicked, [=](){emit this->backGameHall();// emit 关键字触发信号QSound::play(":res/clicked.wav");});// 返回游戏大厅界面connect(this, &GameSelect::backGameHall, [=](){this->hide();GameHall* gameHall = new GameHall;gameHall->show();});GameRoom* gameRoom = new GameRoom();// 设置简单模式按钮QPushButton* simpleBtn = new QPushButton(this);simpleBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");simpleBtn->setFont(font);simpleBtn->setText("简单模式");simpleBtn->move(395, 140);connect(simpleBtn, &QPushButton::clicked, [=](){gameRoom->setTimeout(300);gameRoom->setGeometry(this->geometry());// 设置窗口固定this->hide();gameRoom->show();QSound::play(":res/clicked.wav");});// 设置正常按钮模式QPushButton* normalBtn = new QPushButton(this);normalBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");normalBtn->setFont(font);normalBtn->setText("正常模式");normalBtn->move(395, 260);connect(normalBtn, &QPushButton::clicked, [=](){gameRoom->setTimeout(200);gameRoom->setGeometry(this->geometry());// 设置窗口固定this->hide();gameRoom->show();QSound::play(":res/clicked.wav");});// 设置困难模式按钮QPushButton* hardBtn = new QPushButton(this);hardBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");hardBtn->setFont(font);hardBtn->setText("困难模式");hardBtn->move(395, 380);connect(hardBtn, &QPushButton::clicked, [=](){gameRoom->setTimeout(100);gameRoom->setGeometry(this->geometry());// 设置窗口固定this->hide();gameRoom->show();QSound::play(":res/clicked.wav");});// 获取历史战绩QPushButton* hisBtn = new QPushButton(this);hisBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");hisBtn->setFont(font);hisBtn->setText("历史战绩");hisBtn->move(395, 500);connect(hisBtn, &QPushButton::clicked, [=](){QSound::play(":res/clicked.wav");QFile file("D:/code/qt/Snake/1.txt");file.open(QIODevice::ReadOnly);QTextStream in(&file);// 创建文本流对象int data = in.readLine().toInt();// 读取第一行作为整形数据QWidget* newWidget = new QWidget;// 一定不能添加到对象书上newWidget->setWindowTitle("历史战绩");newWidget->setFixedSize(500, 300);QTextEdit* edit = new QTextEdit(newWidget);// 将编辑框置于新窗口上edit->setFont(font);edit->setFixedSize(500, 300);// 获取系统时间QDateTime currentTime = QDateTime::currentDateTime();QString time = currentTime.toString("yyyy-MM-dd hh:mm:ss");edit->setText("时间:");edit->append(time);edit->setText("得分为:");edit->append(QString::number(data));newWidget->show();});}

效果示例:

3. 游戏房间界面

游戏房间界面包含下面几个部分:

  1. 背景图的绘制。
  2. 蛇的绘制、蛇的移动、判断蛇是否会撞到自己。
  3. 积分的累加和绘制。

在这里我们要考虑几个比较核心的问题:

1. 怎么让蛇动起来?

  • 我们可以用一个链表表示贪吃蛇,一个小方块表示蛇的一个节点, 我们设置蛇的默认长度为3;
  • 向上移动的逻辑就是在蛇的上方加入一个小方块, 然后把最后一个小方块删除即可;
  • 需要用到定时器 Qtimer 每100 - 200ms 重新渲染。

2. 怎么判断蛇有没有吃到食物?

  • 判断蛇头和食物的坐标是否相交,Qt 有相关的接口调用。

3. 怎么控制蛇的移动?

  • 借助QT的实践机制实现, 重写keyPressEvent即可, 在函数中监控想要的键盘事件即可。
  • 我们通过绘制四个按钮,使用信号和槽的机制控制蛇的上、下、左、右移动方向。

3.1 封装贪吃蛇数据结构

#include <QSound>// 枚举蛇的移动方向
enum class snakeDirect{UP = 0,DOWN,LEFT,RIGHT
};class GameRoom : public QWidget
{Q_OBJECT
public:explicit GameRoom(QWidget *parent = nullptr);// 重写绘图事件,实现贪吃蛇游戏游戏背景效果void paintEvent(QPaintEvent *event);void startGame();void setTimeout(int count) {moveTimeout = count;}void createNewFood();// 生成食物void moveUp();// 蛇向上移动void moveDown();// 蛇向下移动void moveLeft();// 蛇向左移动void moveRight();// 蛇向右移动bool checkFail();// 判断游戏是否结束private:snakeDirect moveDirect = snakeDirect::UP;// 定义蛇的移动方向,默认朝上bool isGameStart = false;// 表示是否开始游戏QTimer* timer;// 定时器QList<QRectF> snakeList;// 表示贪吃蛇链表QRectF foodRect;// 表示食物节点const int kDefaultTimeout = 200;// 表示贪吃蛇默认移动时间const int kSnakeNodeWidth = 20;// 表示蛇身体节点的宽度const int kSnakeNodeHeight = 20;// 表示蛇身体节点的高度int moveTimeout = kDefaultTimeout;QSound* sound;// 音频
};

3.2 初始化游戏房间界面

  • 设置窗口大小、标题、图标等

#include <QIcon>GameRoom::GameRoom(QWidget *parent) : QWidget(parent)
{// 初始化窗口设置this->setFixedSize(1000, 800);this->setWindowTitle("贪吃蛇游戏");this->setWindowIcon(QIcon(":res/ico.png"));
}

3.3 蛇的移动

  • 蛇的移动方向为:上、下、左、右。通过在游戏房间中布局四个按钮来控制蛇的移动方向。
  • 注意: 这里贪吃蛇不允许直接掉头, 比如当前是向上的, 不能直接修改为向下。
    // 方向控制QPushButton* Up = new QPushButton(this);QPushButton* Down = new QPushButton(this);QPushButton* Left = new QPushButton(this);QPushButton* Right = new QPushButton(this);QString buttonStyle ="QPushButton {""    border: 2px solid #8f8f8f;""    border-radius: 10px;""    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f0f0f0, stop: 1 #bbbbbb);""    color: #000000;""}""QPushButton:hover {""    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffffff, stop: 1 #8f8f8f);""}""QPushButton:pressed {""    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dadada, stop: 1 #949494);""}";Up->move(880, 400);Down->move(880, 480);Left->move(840, 440);Right->move(920, 440);QFont font("楷体", 24, QFont::ExtraLight, false);Up->setStyleSheet(buttonStyle);Up->setFont(font);Up->setText("↑");Down->setStyleSheet(buttonStyle);Down->setFont(font);Down->setText("↓");Left->setStyleSheet(buttonStyle);Left->setFont(font);Left->setText("←");Right->setStyleSheet(buttonStyle);Right->setFont(font);Right->setText("→");connect(Up, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::DOWN){moveDirect = snakeDirect::UP;}});connect(Down, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::UP){moveDirect = snakeDirect::DOWN;}});connect(Left, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::RIGHT){moveDirect = snakeDirect::LEFT;}});connect(Right, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::LEFT){moveDirect = snakeDirect::RIGHT;}});

3.4 初始化贪吃蛇本体和食物节点

GameRoom::GameRoom(QWidget *parent) : QWidget(parent)
{// 初始化贪吃蛇snakeList.push_back(QRectF(this->width() * 0.5, this->height() * 0.5, kSnakeNodeWidth, kSnakeNodeHeight));moveUp();moveUp();createNewFood();// 初始化食物
}
  • moveUp() 的功能是将蛇向上移动一次, 即在上方新增一个节点, 但不删除尾部节点。
  • createNewFood() 方法的功能是随机创建一个食物节点:
void GameRoom::createNewFood()
{foodRect = QRectF(qrand() % (this->width() / kSnakeNodeWidth) * kSnakeNodeWidth,qrand() % (this->height() / kSnakeNodeHeight) *kSnakeNodeHeight,kSnakeNodeWidth,kSnakeNodeHeight);                
}

3.5 实现定时器的超时槽函数

定时器是为了实现每隔一段时间能处理移动的逻辑并且更新绘图事件。

1. 首先, 需要判断蛇头和食物节点坐标是否相交

  • 如果相交, 需要创建新的食物节点, 并且需要更新蛇的长度, 所以 count 需要 +1 ;
  • 如果不相交, 那么直接处理蛇的移动即可。

2. 根据蛇移动方向 moveDirect 来处理蛇的移动, 处理方法是在前方加一个, 并且删除后方节点;

3. 重新触发绘图事件, 更新渲染。

GameRoom::GameRoom(QWidget *parent) : QWidget(parent)
{    timer = new QTimer(this);connect(timer, &QTimer::timeout, [=](){int count = 1;if (snakeList.front().intersects(foodRect)){createNewFood();++count;QSound::play(":res/eatfood.wav");}while (count--) {// 处理蛇的移动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();});
}

3.6 实现各个方向的移动

        各个方向的移动主要在于更新矩形节点的坐标, 要注意的是一定要处理边界的情况, 当边界不够存储一个新的节点时, 我们需要处理穿墙逻辑。

  • 实现向上移动
void GameRoom::moveUp()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果上面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headY < 0){leftTop = QPointF(headX, this->height() - kSnakeNodeHeight);}else {leftTop = QPointF(headX, headY - kSnakeNodeHeight);}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}
  • 实现向下移动
void GameRoom::moveDown()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果下面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headY > this->height()){leftTop = QPointF(headX, 0);}else {leftTop = snakeNode.bottomLeft();// 返回矩形左下角的坐标}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}
  • 实现向左移动
void GameRoom::moveLeft()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果左面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headX < 0){leftTop = QPointF(800 - kSnakeNodeWidth, headY);}else {leftTop = QPointF(headX - kSnakeNodeWidth, headY);}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}
  • 实现向右移动
void GameRoom::moveRight()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果右面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headX > 760){leftTop = QPointF(0, headY);}else {leftTop = snakeNode.topRight();// 返回矩形右上角的坐标}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}

3.7 重写绘图事件函数进行渲染

重写基类的 paintEvent() 方法进行渲染:

  • 渲染背景图
  • 渲染蛇头
  • 渲染蛇身体
  • 渲染蛇尾巴
  • 渲染右边游戏控制区域
  • 渲染食物节点
  • 渲染当前分数
  • 游戏结束渲染 game over!
void GameRoom::paintEvent(QPaintEvent *event)
{QPainter painter(this);QPixmap pix;pix.load(":res/game_room.png");painter.drawPixmap(0, 0, 800, 800, pix);painter.setRenderHint(QPainter::Antialiasing);// 设置抗锯齿// 渲染蛇头if (moveDirect == snakeDirect::UP){pix.load(":res/up.png");}else if (moveDirect == snakeDirect::DOWN){pix.load(":res/down.png");}else if (moveDirect == snakeDirect::LEFT){pix.load(":res/left.png");}else {pix.load(":res/right.png");}auto snakeHead = snakeList.front();painter.drawPixmap(snakeHead.x(), snakeHead.y(), snakeHead.width(), snakeHead.height(), pix);// 渲染蛇身体pix.load(":res/Bd.png");for (int i = 1; i < snakeList.size() - 1; i++){auto node = snakeList.at(i);painter.drawPixmap(node.x(), node.y(), node.width(), node.height(), pix);}// 渲染蛇尾巴auto snakeTail = snakeList.back();painter.drawPixmap(snakeTail.x(), snakeTail.y(), snakeTail.width(), snakeTail.height(), pix);// 渲染食物pix.load(":res/food.bmp");painter.drawPixmap(foodRect.x(), foodRect.y(), foodRect.width(), foodRect.height(), pix);// 渲染右边区域pix.load(":res/bg1.png");painter.drawPixmap(800, 0, 200, 1000, pix);// 渲染分数pix.load(":res/sorce_bg.png");painter.drawPixmap(this->width() * 0.85, this->height() * 0.02, 90, 40, pix);QPen pen;pen.setColor(Qt::black);painter.setPen(pen);QFont font("楷体", 22, QFont::ExtraLight, false);painter.setFont(font);painter.drawText(this->width() * 0.9, this->height() * 0.06, QString("%1").arg(snakeList.size()));// 如果失败,渲染 GAME OVERif (checkFail()){pen.setColor(Qt::red);painter.setPen(pen);QFont font("楷体", 50, QFont::ExtraLight, false);painter.setFont(font);painter.drawText(this->width() * 0.5 - 250, this->height() * 0.5, QString("GAME OVER"));timer->stop();QSound::play(":res/gameover.wav");sound->stop();}
}

3.8 检查是否自己会撞到自己

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;
}

3.9 设置游戏开始和游戏暂停按钮

    // 开始游戏 & 暂停游戏QPushButton* startBtn = new QPushButton(this);QPushButton* stopBtn = new QPushButton(this);QFont ft("楷体", 20, QFont::ExtraLight, false);// 设置按钮的位置startBtn->move(860, 150);stopBtn->move(860, 200);// 设置按钮文本startBtn->setText("开始");stopBtn->setText("暂停");// 设置按钮样式startBtn->setStyleSheet("QPushButton {""background-color: white;"// 按钮背景颜色"color: #4CAF50;" // 文字颜色"border: 2px solid #4CAF50;" // 边框"border-radius: 8px;" // 边框圆角"padding: 10px 20px;" // 内边距"}");stopBtn->setStyleSheet("QPushButton {""background-color: white;"// 按钮背景颜色"color: #4CAF50;" // 文字颜色"border: 2px solid #4CAF50;" // 边框"border-radius: 8px;" // 边框圆角"padding: 10px 20px;" // 内边距"}");// 设置按钮字体格式startBtn->setFont(ft);stopBtn->setFont(ft);connect(startBtn, &QPushButton::clicked, [=](){sound = new QSound(":res/Trepak.wav");sound->play();sound->setLoops(-1);// 循环播放isGameStart = true;timer->start(moveTimeout);});connect(stopBtn, &QPushButton::clicked, [=](){sound->stop();isGameStart = false;timer->stop();});

3.10 设置退出游戏按钮

当我们点击退出游戏按钮时,当前游戏房间窗口不会立即退出,而是会弹窗提示,提示我们是否要退出游戏,效果如下图示:

这个弹窗提示我们是通过 Qt 中的消息盒子来实现的,具体实现过程如下:

  // 退出游戏按钮QPushButton* exitGame = new QPushButton(this);exitGame->setStyleSheet("QPushButton {""background-color: white;" // 按钮背景颜色"color: #4CAF50;" // 文字颜色"border: 2px solid #4CAF50;" // 边框"border-radius: 8px;" // 边框圆角"padding: 10px 20px;" // 内边距"}");exitGame->move(825, 730);exitGame->setText("退出");exitGame->setFont(font);// 消息提示QMessageBox* messageBox = new QMessageBox(this);QPushButton* okbtn = new QPushButton("ok");QPushButton* cancelbtn = new QPushButton("cancel");messageBox->setWindowTitle("退出游戏");// 设置消息对话框的标题messageBox->setText("确认退出游戏吗?");// 设置消息对话框内容messageBox->setIcon(QMessageBox::Question); //设置消息对话框类型messageBox->addButton(okbtn, QMessageBox::AcceptRole);//Accept Role:接受的角色messageBox->addButton(cancelbtn, QMessageBox::RejectRole);//Reject Role:排斥作用connect(exitGame, &QPushButton::clicked, [=](){messageBox->show();// 消息提示QSound::play(":res/clicked.wav");messageBox->exec();// 阻塞等待用户输入GameSelect* s = new GameSelect;if (messageBox->clickedButton() == okbtn){this->close();//如果点击了 Ok 按钮,那么就会跳转到游戏关卡界⾯s->show();s->setGeometry(this->geometry());QSound::play(":res/clicked.wav");}else{messageBox->close();QSound::play(":res/clicked.wav");}});

3.11 获取历史战绩

对于历史战绩的获取我们是通过 Qt 中的读写文件操作来实现的。具体实现过程如下:

  • 写文件:往文件中写入蛇的长度
 int c = snakeList.size();QFile file("D:/code/qt/Snake/1.txt");if (file.open(QIODevice::WriteOnly | QIODevice::Text)){QTextStream out(&file);int num = c;out << num;// 将int类型数据写入文件file.close();}
  • 读文件:读取写入文件中蛇的长度
QFile file("D:/code/qt/Snake/1.txt");
file.open(QIODevice::ReadOnly);QTextStream in(&file);// 创建文本流对象
int data = in.readLine().toInt();// 读取第一行作为整形数据

 效果示例:

四、项目源码

🌵gamehall.h

#ifndef GAMEHALL_H
#define GAMEHALL_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class GameHall; }
QT_END_NAMESPACEclass GameHall : public QWidget
{Q_OBJECTpublic:GameHall(QWidget *parent = nullptr);~GameHall();//重写绘图事件,绘制游戏大厅界面void paintEvent(QPaintEvent *event);private:Ui::GameHall *ui;
};
#endif // GAMEHALL_H

🌵gamehall.cpp

#include "gamehall.h"
#include "ui_gamehall.h"
#include "gameselect.h"
#include <QPainter>
#include <QIcon>
#include <QPushButton>
#include <QFont>
#include <QSound>GameHall::GameHall(QWidget *parent): QWidget(parent), ui(new Ui::GameHall)
{ui->setupUi(this);// 设置窗口信息this->setFixedSize(1000, 800);this->setWindowTitle("贪吃蛇游戏");this->setWindowIcon(QIcon(":res/ico.png"));// 设置开始游戏按钮QPushButton* startBtn = new QPushButton(this);startBtn->setText("开始游戏");startBtn->move(this->width() * 0.4, this->height() * 0.7);startBtn->setStyleSheet("QPushButton { border : 0px; }");QFont font("华文行楷", 23, QFont::ExtraLight, false);startBtn->setFont(font);// 点击按钮就会进入游戏关卡界面GameSelect* select = new GameSelect;connect(startBtn, &QPushButton::clicked, [=](){select->setGeometry(this->geometry());// 设置窗口固定this->close();// 关闭游戏大厅界面select->show();// 显示游戏关卡界面// 注意:使用 QSound 类时, 需要添加模块:multimediaQSound::play(":res/clicked.wav");// 添加按钮点击音效});
}GameHall::~GameHall()
{delete ui;
}void GameHall::paintEvent(QPaintEvent *event)
{(void)event;// 去掉警告小技巧QPainter painter(this);// 实例化画家对象QPixmap pix(":res/game_hall.png");// 实例化绘画设备painter.drawPixmap(0, 0, this->width(), this->height(), pix);// 绘画
}

 🍇gameselect.h

#ifndef GAMESELECT_H
#define GAMESELECT_H#include <QWidget>class GameSelect : public QWidget
{Q_OBJECT
public:explicit GameSelect(QWidget *parent = nullptr);//重写绘图事件,绘制游戏关卡选择界面void paintEvent(QPaintEvent *event);signals:void backGameHall();// 信号声明};#endif // GAMESELECT_H

 🍇gameselect.cpp 

#include "gameselect.h"
#include "gamehall.h"
#include "gameroom.h"
#include <QPainter>
#include <QIcon>
#include <QFont>
#include <QPushButton>
#include <QSound>
#include <QFile>
#include <QTextStream>
#include <QTextEdit>
#include <QDateTime>GameSelect::GameSelect(QWidget *parent) : QWidget(parent)
{// 设置游戏关卡界面this->setFixedSize(1000, 800);this->setWindowTitle("关卡选择");this->setWindowIcon(QIcon(":res/ico.png"));// 设置字体格式QFont font("华文行楷", 20, QFont::ExtraLight, false);// 创建返回按钮QPushButton* backBtn = new QPushButton(this);QPixmap pix(":res/back.png");backBtn->setIcon(pix);backBtn->move(this->width() * 0.9, this->height() * 0.9);connect(backBtn, &QPushButton::clicked, [=](){emit this->backGameHall();// emit 关键字触发信号QSound::play(":res/clicked.wav");});// 返回游戏大厅界面connect(this, &GameSelect::backGameHall, [=](){this->hide();GameHall* gameHall = new GameHall;gameHall->show();});GameRoom* gameRoom = new GameRoom();// 设置简单模式按钮QPushButton* simpleBtn = new QPushButton(this);simpleBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");simpleBtn->setFont(font);simpleBtn->setText("简单模式");simpleBtn->move(395, 140);connect(simpleBtn, &QPushButton::clicked, [=](){gameRoom->setTimeout(300);gameRoom->setGeometry(this->geometry());// 设置窗口固定this->hide();gameRoom->show();QSound::play(":res/clicked.wav");});// 设置正常按钮模式QPushButton* normalBtn = new QPushButton(this);normalBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");normalBtn->setFont(font);normalBtn->setText("正常模式");normalBtn->move(395, 260);connect(normalBtn, &QPushButton::clicked, [=](){gameRoom->setTimeout(200);gameRoom->setGeometry(this->geometry());// 设置窗口固定this->hide();gameRoom->show();QSound::play(":res/clicked.wav");});// 设置困难模式QPushButton* hardBtn = new QPushButton(this);hardBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");hardBtn->setFont(font);hardBtn->setText("困难模式");hardBtn->move(395, 380);connect(hardBtn, &QPushButton::clicked, [=](){gameRoom->setTimeout(100);gameRoom->setGeometry(this->geometry());// 设置窗口固定this->hide();gameRoom->show();QSound::play(":res/clicked.wav");});// 获取历史战绩QPushButton* hisBtn = new QPushButton(this);hisBtn->setStyleSheet("QPushButton { background-color:#0072C6; color:white; ""border:2px groove gray; border-radius:10px; padding:6px; }");hisBtn->setFont(font);hisBtn->setWindowIcon(QIcon(":res/ico.png"));hisBtn->setText("历史战绩");hisBtn->move(395, 500);connect(hisBtn, &QPushButton::clicked, [=](){QSound::play(":res/clicked.wav");QFile file("D:/code/qt/Snake/1.txt");file.open(QIODevice::ReadOnly);QTextStream in(&file);// 创建文本流对象int data = in.readLine().toInt();// 读取第一行作为整形数据QWidget* newWidget = new QWidget;// 一定不能添加到对象书上newWidget->setWindowTitle("历史战绩");newWidget->setWindowIcon(QIcon(":res/ico.png"));newWidget->setFixedSize(500, 300);QTextEdit* edit = new QTextEdit(newWidget);// 将编辑框置于新窗口上edit->setFont(font);edit->setFixedSize(500, 300);// 获取系统时间QDateTime currentTime = QDateTime::currentDateTime();QString time = currentTime.toString("yyyy-MM-dd hh:mm:ss");edit->setText("时间:");edit->append(time);edit->setText("得分为:");edit->append(QString::number(data));newWidget->show();});}//重写绘图事件,绘制游戏关卡选择界面
void GameSelect::paintEvent(QPaintEvent *event)
{(void)event;QPainter painter(this);// 实例化画家对象QPixmap pix(":res/game_select.png");// 实例化绘画设备painter.drawPixmap(0, 0, this->width(), this->height(), pix);// 绘画
}

🌴gameroom.h

#ifndef GAMEROOM_H
#define GAMEROOM_H#include <QWidget>
#include <QSound>// 枚举蛇的移动方向
enum class snakeDirect{UP = 0,DOWN,LEFT,RIGHT
};class GameRoom : public QWidget
{Q_OBJECT
public:explicit GameRoom(QWidget *parent = nullptr);// 重写绘图事件,实现贪吃蛇游戏游戏背景效果void paintEvent(QPaintEvent *event);void startGame();void setTimeout(int count) {moveTimeout = count;}void createNewFood();// 生成食物void moveUp();// 蛇向上移动void moveDown();// 蛇向下移动void moveLeft();// 蛇向左移动void moveRight();// 蛇向右移动bool checkFail();// 判断游戏是否结束private:snakeDirect moveDirect = snakeDirect::UP;// 定义蛇的移动方向,默认朝上bool isGameStart = false;// 表示是否开始游戏QTimer* timer;// 定时器QList<QRectF> snakeList;// 表示贪吃蛇链表QRectF foodRect;// 表示食物节点const int kDefaultTimeout = 200;// 表示贪吃蛇默认移动时间const int kSnakeNodeWidth = 20;// 表示蛇身体节点的宽度const int kSnakeNodeHeight = 20;// 表示蛇身体节点的高度int moveTimeout = kDefaultTimeout;QSound* sound;// 音频
};#endif // GAMEROOM_H

🌴gameroom.cpp 

#include "gameroom.h"
#include "gameselect.h"
#include <QIcon>
#include <QPixmap>
#include <QPainter>
#include <QTimer>
#include <QDebug>
#include <QFile>
#include <QPushButton>
#include <QMessageBox>
#include <QTextEdit>
#include <QSound>GameRoom::GameRoom(QWidget *parent) : QWidget(parent)
{// 初始化窗口设置this->setFixedSize(1000, 800);this->setWindowTitle("贪吃蛇游戏");this->setWindowIcon(QIcon(":res/ico.png"));// 初始化贪吃蛇snakeList.push_back(QRectF(this->width() * 0.5, this->height() * 0.5, kSnakeNodeWidth, kSnakeNodeHeight));moveUp();moveUp();createNewFood();// 初始化食物timer = new QTimer(this);connect(timer, &QTimer::timeout, [=](){int count = 1;if (snakeList.front().intersects(foodRect)){createNewFood();++count;QSound::play(":res/eatfood.wav");}while (count--) {// 处理蛇的移动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();});// 开始游戏 & 暂停游戏QPushButton* startBtn = new QPushButton(this);QPushButton* stopBtn = new QPushButton(this);QFont ft("楷体", 15, QFont::ExtraLight, false);// 设置按钮的位置startBtn->move(840, 150);stopBtn->move(840, 220);// 设置按钮文本startBtn->setText("开始");stopBtn->setText("暂停");// 设置按钮样式startBtn->setStyleSheet("QPushButton {""background-color: white;"// 按钮背景颜色"color: #4CAF50;" // 文字颜色"border: 2px solid #4CAF50;" // 边框"border-radius: 8px;" // 边框圆角"padding: 10px 20px;" // 内边距"}");stopBtn->setStyleSheet("QPushButton {""background-color: white;"// 按钮背景颜色"color: #4CAF50;" // 文字颜色"border: 2px solid #4CAF50;" // 边框"border-radius: 8px;" // 边框圆角"padding: 10px 20px;" // 内边距"}");// 设置按钮字体格式startBtn->setFont(ft);stopBtn->setFont(ft);connect(startBtn, &QPushButton::clicked, [=](){sound = new QSound(":res/Trepak.wav");sound->play();sound->setLoops(-1);// 循环播放isGameStart = true;timer->start(moveTimeout);});connect(stopBtn, &QPushButton::clicked, [=](){sound->stop();isGameStart = false;timer->stop();});// 方向控制QPushButton* Up = new QPushButton(this);QPushButton* Down = new QPushButton(this);QPushButton* Left = new QPushButton(this);QPushButton* Right = new QPushButton(this);QString buttonStyle ="QPushButton {""    border: 2px solid #8f8f8f;""    border-radius: 10px;""    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #f0f0f0, stop: 1 #bbbbbb);""    color: #000000;""}""QPushButton:hover {""    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #ffffff, stop: 1 #8f8f8f);""}""QPushButton:pressed {""    background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 #dadada, stop: 1 #949494);""}";Up->move(880, 400);Down->move(880, 480);Left->move(840, 440);Right->move(920, 440);QFont font("楷体", 15, QFont::ExtraLight, false);Up->setStyleSheet(buttonStyle);Up->setFont(font);Up->setText("↑");Down->setStyleSheet(buttonStyle);Down->setFont(font);Down->setText("↓");Left->setStyleSheet(buttonStyle);Left->setFont(font);Left->setText("←");Right->setStyleSheet(buttonStyle);Right->setFont(font);Right->setText("→");connect(Up, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::DOWN){moveDirect = snakeDirect::UP;}});connect(Down, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::UP){moveDirect = snakeDirect::DOWN;}});connect(Left, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::RIGHT){moveDirect = snakeDirect::LEFT;}});connect(Right, &QPushButton::clicked, [=](){if (moveDirect != snakeDirect::LEFT){moveDirect = snakeDirect::RIGHT;}});// 退出游戏按钮QPushButton* exitGame = new QPushButton(this);exitGame->setStyleSheet("QPushButton {""background-color: white;" // 按钮背景颜色"color: #4CAF50;" // 文字颜色"border: 2px solid #4CAF50;" // 边框"border-radius: 8px;" // 边框圆角"padding: 10px 20px;" // 内边距"}");exitGame->move(840, 725);exitGame->setText("退出");exitGame->setFont(font);// 消息提示QMessageBox* messageBox = new QMessageBox(this);QPushButton* okbtn = new QPushButton("ok");QPushButton* cancelbtn = new QPushButton("cancel");messageBox->setWindowIcon(QIcon(":res/ico.png"));// 设置消息对话框的图标messageBox->setWindowTitle("退出游戏");// 设置消息对话框的标题messageBox->setText("确认退出游戏吗?");// 设置消息对话框内容messageBox->setIcon(QMessageBox::Question); //设置消息对话框类型messageBox->addButton(okbtn, QMessageBox::AcceptRole);//Accept Role:接受的角色messageBox->addButton(cancelbtn, QMessageBox::RejectRole);//Reject Role:排斥作用connect(exitGame, &QPushButton::clicked, [=](){messageBox->show();// 消息提示QSound::play(":res/clicked.wav");messageBox->exec();// 阻塞等待用户输入GameSelect* s = new GameSelect;if (messageBox->clickedButton() == okbtn){this->close();//如果点击了 Ok 按钮,那么就会跳转到游戏关卡界⾯s->show();s->setGeometry(this->geometry());QSound::play(":res/clicked.wav");}else{messageBox->close();QSound::play(":res/clicked.wav");}});
}// 重写绘图事件,实现贪吃蛇游戏游戏背景效果
void GameRoom::paintEvent(QPaintEvent *event)
{QPainter painter(this);QPixmap pix;pix.load(":res/game_room.png");painter.drawPixmap(0, 0, 800, 800, pix);painter.setRenderHint(QPainter::Antialiasing);// 设置抗锯齿// 蛇 = 蛇头 + 蛇身体 + 蛇尾巴// 渲染蛇头if (moveDirect == snakeDirect::UP){pix.load(":res/up.png");}else if (moveDirect == snakeDirect::DOWN){pix.load(":res/down.png");}else if (moveDirect == snakeDirect::LEFT){pix.load(":res/left.png");}else {pix.load(":res/right.png");}auto snakeHead = snakeList.front();painter.drawPixmap(snakeHead.x(), snakeHead.y(), snakeHead.width(), snakeHead.height(), pix);// 渲染蛇身体pix.load(":res/Bd.png");for (int i = 1; i < snakeList.size() - 1; i++){auto node = snakeList.at(i);painter.drawPixmap(node.x(), node.y(), node.width(), node.height(), pix);}// 渲染蛇尾巴auto snakeTail = snakeList.back();painter.drawPixmap(snakeTail.x(), snakeTail.y(), snakeTail.width(), snakeTail.height(), pix);// 渲染食物pix.load(":res/food.bmp");painter.drawPixmap(foodRect.x(), foodRect.y(), foodRect.width(), foodRect.height(), pix);// 渲染右边区域pix.load(":res/bg1.png");painter.drawPixmap(800, 0, 200, 1000, pix);// 渲染分数pix.load(":res/sorce_bg.png");painter.drawPixmap(this->width() * 0.85, this->height() * 0.02, 100, 50, pix);QPen pen;pen.setColor(Qt::black);painter.setPen(pen);QFont font("楷体", 15, QFont::ExtraLight, false);painter.setFont(font);painter.drawText(this->width() * 0.9, this->height() * 0.075, QString("%1").arg(snakeList.size()));// 往文件中写入得分int c = snakeList.size();QFile file("D:/code/qt/Snake/1.txt");if (file.open(QIODevice::WriteOnly | QIODevice::Text)){QTextStream out(&file);int num = c;out << num;// 将int类型数据写入文件file.close();}// 如果失败,渲染 GAME OVERif (checkFail()){pen.setColor(Qt::red);painter.setPen(pen);QFont font("微软雅黑", 40, QFont::ExtraLight, false);painter.setFont(font);painter.drawText(this->width() * 0.1, this->height() * 0.5, QString("GAME OVER"));timer->stop();QSound::play(":res/gameover.wav");sound->stop();}
}// 创建食物
void GameRoom::createNewFood()
{foodRect = QRectF(qrand() % (this->width() / kSnakeNodeWidth) * kSnakeNodeWidth,qrand() % (this->height() / kSnakeNodeHeight) *kSnakeNodeHeight,kSnakeNodeWidth,kSnakeNodeHeight);
}// 实现向上移动
void GameRoom::moveUp()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果上面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headY < 0){leftTop = QPointF(headX, this->height() - kSnakeNodeHeight);}else {leftTop = QPointF(headX, headY - kSnakeNodeHeight);}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}// 实现向下移动
void GameRoom::moveDown()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果下面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headY > this->height()){leftTop = QPointF(headX, 0);}else {leftTop = snakeNode.bottomLeft();// 返回矩形左下角的坐标}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}// 实现向左移动
void GameRoom::moveLeft()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果左面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headX < 0){leftTop = QPointF(800 - kSnakeNodeWidth, headY);}else {leftTop = QPointF(headX - kSnakeNodeWidth, headY);}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}// 实现向右移动
void GameRoom::moveRight()
{QPointF leftTop;// 左上角坐标QPointF rightBottom;// 右下角坐标auto snakeNode = snakeList.front();int headX = snakeNode.x();int headY = snakeNode.y();// 如果右面剩余的空间不够放入⼀个新的节点, 相当于到墙边了, 要处理穿墙逻辑if (headX > 760){leftTop = QPointF(0, headY);}else {leftTop = snakeNode.topRight();// 返回矩形右上角的坐标}rightBottom = leftTop + QPointF(kSnakeNodeWidth, kSnakeNodeHeight);snakeList.push_front(QRectF(leftTop, rightBottom));
}// 判断游戏是否结束
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;
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

TCP 粘包问题

TCP是一个面向字节流的传输层协议。“流” 意味着 TCP 所传输的数据是没有边界的。这不同于 UDP 协议提供的是面向消息的传输服务&#xff0c;其传输的数据是有边界的。TCP 的发送方无法保证对方每次收到的都是一个完整的数据包。于是就有了粘包、拆包问题的出现。粘包、拆包问…

进程的创建、终止

目录 前言1. 进程创建2. 进程终止3. exit && _exit 的异同3.1 相同点3.2 不同点 前言 紧接着进程地址空间之后&#xff0c;我们这篇文章开始谈论进程控制相关的内容&#xff0c;其中包括进程是如何创建的&#xff0c;进程终止的几种情况&#xff0c;以及进程异常终止的…

Android低内存设备系统优化

切记,所有的优化都遵循一条准则: 空间换时间,时间换空间。 一、前言 我们为什么会觉得卡顿、不流畅? 卡顿等性能问题的最主要根源都是因为渲染性能,Android系统很有可能无法及时完成那些复杂的界面渲染操作。Android系统每隔16ms发出信号,触发对UI进行渲染,如果每次渲染…

Thinkphp6 反序列化漏洞分析

本文来自无问社区&#xff0c;更多实战内容可前往查看http://wwlib.cn/index.php/artread/artid/10431.html 版本&#xff1a;Thinkphp6&PHP7.3.4 TP 环境搭建利用 composer 命令进行&#xff0c;同时本次分析在 windows 环境下进行 composer create-project topthink/t…

Android 架构模式之 MVP

目录 架构设计的目的对 MVP 的理解代码ModelViewPresenter Android 中 MVP 的问题试吃个小李子ModelViewPresenter效果展示 大家好&#xff01; 作为 Android 程序猿&#xff0c;你有研究过 MVP 架构吗&#xff1f;在开始接触 Android 那一刻起&#xff0c;我们就开始接触 MVC…

高频变压器无功补偿怎么做

高频变压器的无功补偿主要是为了提高功率因数、减小无功损耗、提高电源利用率。在高频电路中&#xff0c;由于频率较高&#xff0c;传统的无功补偿方法需要进行一定的调整和优化。以下是高频变压器无功补偿的一些方法和建议&#xff1a; 1、无功补偿电容器 高频电容器选择&…

具有手势识别的动捕设备——mHand Pro VR数据手套

数据手套是指通过手套内置的传感器&#xff0c;实时采集手部运动数据的动捕设备&#xff0c;通常被应用于虚拟仿真、虚拟现实vr交互、动画制作等领域。其中&#xff0c;基于惯性动作捕捉技术研发的数据手套&#xff0c;凭借其高性价比的优势&#xff0c;在市面上的应用更为广泛…

STM32G474按钮输入和点灯

在获取到工程模板后&#xff0c;学习某个CPU的第一步通常都是IO口操作。因此按钮输入和点灯&#xff0c;就是本次学习的第一个程序。先从简单入手。 和GPIO操作有关的函数如下: __HAL_RCC_GPIOA_CLK_ENABLE();//使能GPIOA时钟 __HAL_RCC_GPIOB_CLK_ENABLE();//使能GPIOB时钟 _…

深度理解指针(2)

hello各位小伙伴们&#xff0c;关于指针的了解我们断更了好久了&#xff0c;接下来这几天我会带领大家继续我们指针的学习。 目录 数组名的理解 使用指针访问一维数组 一维数组传参的本质 二级指针 指针数组 使用指针数组来模仿二维数组 数组名的理解 我们首先来看一段…

【开源社区】Elasticsearch(ES)中 exists 查询空值字段的坑

文章目录 1、概述2、使用 null_value 处理空值3、使用 exists 函数查询值为空的文档3.1 使用场景3.2 ES 中常见的空值查询方式3.3 常见误区3.4 使用 bool 查询函数查询空值字段3.5 exists 函数详解3.5.1 bool 查询的不足3.5.3 exists 的基本使用 3.6 完美方案 1、概述 本文主要…

单例模式 详解

单例模式 简介: 让类只初始化一次, 然后不同的地方都能获取到同一个实例 这是非常常用的一种模式, 系统稍微大一点基本上都会用到. 在系统中, 不同模块的总管理类都已单例模式居多 这里我们不仅使用c实现单例模式, 也会用python2实现一遍 python代码 想要看更详细的python单…

手动下载Sentinel-1卫星精密轨道数据

轨道信息对于InSAR&#xff08;干涉合成孔径雷达&#xff09;数据处理至关重要&#xff0c;因为它影响从初始图像配准到最终形变图像生成的整个过程。不准确的轨道信息会导致基线误差&#xff0c;这些误差会以残差条纹的形式出现在干涉图中。为了消除由轨道误差引起的系统性误差…

Swift 6.0 如何更优雅的抛出和处理特定类型的错误

概述 从 Swift 语言诞生那天儿起&#xff0c;它就不厌其烦一遍又一遍地向秃头码农们诉说着自己的类型安全和高雅品味。 不过遗憾的是&#xff0c;作为 Swift 语言中错误处理这最为重要的一环却时常让小伙伴们不得要领、满腹狐疑。 在本篇博文中&#xff0c;您将学到如下内容&…

基于网格尺度的上海市人口分布空间聚集特征分析与冷热点识别

在上篇文章提到了同一研究空间在不同尺度下的观察可能会带来不同的见解和发现&#xff0c;这次我们把尺度缩放到网格&#xff0c;来看网格尺度下的空间自相关性、高/低聚类&#xff0c;这些&#xff0c;因为尺度缩放到网格尺度了&#xff0c;全国这个行政区范围就显的太大了&am…

基于Shader实现的UGUI描边解决方案遇到的bug

原文链接&#xff1a;https://www.cnblogs.com/GuyaWeiren/p/9665106.html 使用这边文章介绍的描边解决方案时遇到了一些问题&#xff0c;就是文字的描边经常会变粗&#xff0c;虽然有的时候也可以正常显示描边&#xff0c;但是运行一会儿描边就不正常了&#xff0c;而且不正常…

UDP+TCP

一、UDP协议 1.recvfrom:recvform(int sockfd,void *buf,size_t len,int flags,struct sockaddr *src_addr,socklen_t *addrlen); 参数&#xff1a;socket的fd; 保存数据的空间地址 &#xff1b; 空间大小&#xff1b; 默认接收方式&#xff08;默认阻塞&#xf…

【案例56】安全设备导致请求被拦截

问题现象 访问相关报表 第二次访问发现有相关的连接问题 问题分析 服务器访问相关节点&#xff0c;发现相关节点无此问题。从客户的客户端访问缺有问题。在nclog中发现如下日志&#xff0c;链接被重置。 直接访问服务器无丢包现象。客户端未开防火墙。装了杀毒软件已经卸载。…

简单记录:两台服务器如何超快速互传文件/文件夹

在服务器间传输文件和文件夹是一个常见的任务&#xff0c;尤其是在需要同步数据或进行备份时。以下是使用 scp 命令在两台服务器之间进行文件传输的基本步骤。 服务器A 至 服务器B&#xff1a;文件传输指南 前提条件 确保服务器A和服务器B之间网络互通。确认您有权限访问目标…

C语言 之 整数在内存中的存储、大小端字节序和字节序的判断

文章目录 整数在内存中的存储大小端字节序和字节序判断大小端有大小端的原因高位和地位怎么区分&#xff1f;图例判断机器大端还是小端的例题 整数在内存中的存储 整数的2进制表示方法有三种&#xff0c;即 原码、反码和补码 三种表示方法均有符号位和数值位两部分&#xff0c…

微信小程序获取当前位置并自定义浮窗

1、在腾讯地图api申请key&#xff08;添加微信小程序的appid&#xff09;。 每个Key每日可以免费使用100次&#xff0c;超过次数后会导致地图不显示。可以多申请几个Key解决。WebService API | 腾讯位置服务腾讯地图开放平台为各类应用厂商和开发者提供基于腾讯地图的地理位置…