使用C++的QT框架实现贪吃蛇
最近刷抖音经常看到别人使用类似chatGPT的al工具实现这个贪吃蛇游戏,正好我之前也写过,那么今天看看怎么去实现这个简单的游戏
我这边使用的是C++的QT框架,当然用哪些框架都可以,主要是逻辑思路
1.生成画布,开始是一些框的配置
// 构造函数,初始化 Widget 类
Widget::Widget(QWidget *parent): QWidget(parent) // 调用 QWidget 的构造函数来初始化父类, ui(new Ui::Widget) // 创建 Widget 类的私有成员 ui,用于用户界面
{// 在用户界面上设置布局ui->setupUi(this);// 创建一个 qiu 对象并将其赋给 yuan 指针this->yuan = new qiu(this);// 创建一个定时器对象,并设置其间隔为 100 毫秒time->setInterval(100);// 设置窗口大小为 600x368 像素this->setFixedSize(QSize(600, 368));// 设置窗口标题为 "贪吃蛇"this->setWindowTitle("贪吃蛇");
}
还需要画这个框的背景,代码如下
QPainter huajia(this); // 创建一个 QPainter 对象 huajia,并将其绑定到当前窗口或绘图设备
QPixmap p1 = QPixmap(":/C:/Users/Administrator/Pictures/tp2.png"); // 创建一个 QPixmap 对象 p1,加载指定路径下的图片// 使用 QPainter 绘制图片到指定区域
// 参数解释: (0, 0) 是绘制的起始位置,this->width() 是绘制的宽度,this->height() 是绘制的高度,p1 是要绘制的图片
huajia.drawPixmap(0, 0, this->width(), this->height(), p1);

可能不是那么美观,主要是实现功能,之后的话都可以改良
2.贪吃蛇主要还是蛇,蛇的话我是使用一个QRectF链表来表示蛇
为什么使用链表呢,因为我的蛇移动是删除最后一个元素新增为第一个元素,而链表本身比较适合元素的移动,可以插入第一个,删除最后一个元素,当我删除一个元素后后面的元素会扑上来,实现动态的蛇的移动
QWidget头文件如下
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTimer>
#include <QRect>
#include "qiu.h"QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECT// 声明一个枚举类型,用于表示蛇的移动方向enum fangxiang { shang, xia, zuo, you, ting, kai };public:// 构造函数,可以接受一个父窗口对象作为参数Widget(QWidget *parent = nullptr);// 析构函数~Widget();// 更新蛇的位置void gengxin();// 保存蛇的各个部分的矩形区域QList<QRectF> she;// 当前蛇的移动方向fangxiang zhujue = shang;// 指向 qiu 对象的指针qiu* yuan;// 键盘事件处理函数,用于捕捉键盘输入void keyPressEvent(QKeyEvent *event);// 绘制事件处理函数,用于绘制蛇和其他图形void paintEvent(QPaintEvent *event);// 向上移动蛇的头部void addshang();// 向下移动蛇的头部void addxia();// 向左移动蛇的头部void addzuo();// 向右移动蛇的头部void addyou();// 停止蛇的移动void tingzhi();// 开始蛇的移动void kaishi();// 保存小球的矩形区域QRect xiaoqiu;public slots:
private:Ui::Widget *ui;
};#endif // WIDGET_H
如上所示,蛇的移动是一个定时器,使用定时器进行绘画蛇这个数组,定时器每次到对应的时间会首先增加一个新元素到蛇前面,将最后一个元素上升,如图

使用绘画室事件画蛇,画蛇的话只需要在这个框内蛇的坐标,要么是左上角加上宽高,或者左上角坐标,右下角坐标等
应为我这边使用的蛇的身躯是一些小正方形组成的,所以可以直接使用左上角坐标加上宽高为20
//使用画家对象绘画蛇的方块
huajia.drawPixmap(yuan->x,yuan->y,20,20,yuan->p);
3.定时器,定时器主要的作用是控制蛇的移动,主要方式为蛇的数组进行删除最后一个元素,新增一个元素到首部,只要定时循环,那么就会呈现蛇的移动
新增槽函数
// 使用 Qt 的信号与槽机制,在定时器时间间隔内触发 gengxin 函数
connect(time, &QTimer::timeout, this, [=]() {this->gengxin();
});
槽函数内部为蛇的枚举,我这边,上下左右等,
void Widget::gengxin() {// 根据当前的蛇的移动方向执行相应的移动函数switch (zhujue) {case shang:addshang();break;case xia:addxia();break;case zuo:addzuo();break;case you:addyou();break;case ting:// 如果蛇的状态是 "ting",则不进行移动break;case kai:// 如果蛇的状态是 "kai",则不进行移动break;}// 请求重新绘制界面,以更新蛇的位置update();// 移除蛇的尾部,相当于模拟蛇在前进时的效果she.removeLast();
}
4.设置键盘事件,我这边使用的是键盘右边的那四个方向,代码如下
void Widget::keyPressEvent(QKeyEvent *event) {// 响应用户的键盘按键事件switch (event->key()) {case Qt::Key_Up:// 如果用户按下向上箭头键,且当前蛇的方向不是向下,则将蛇的方向设置为向上if (zhujue != xia) {zhujue = shang;}break;case Qt::Key_Down:// 如果用户按下向下箭头键,且当前蛇的方向不是向上,则将蛇的方向设置为向下if (zhujue != shang) {zhujue = xia;}break;case Qt::Key_Left:// 如果用户按下向左箭头键,且当前蛇的方向不是向右,则将蛇的方向设置为向左if (zhujue != you) {zhujue = zuo;}break;case Qt::Key_Right:// 如果用户按下向右箭头键,且当前蛇的方向不是向左,则将蛇的方向设置为向右if (zhujue != zuo) {zhujue = you;}break;case Qt::Key_Space:// 如果用户按下空格键,切换蛇的状态为 "ting"(停止)或 "kai"(开始)if (zhujue == ting) {zhujue = kai;time->start(); // 启动定时器,继续游戏}else {time->stop(); // 停止定时器,暂停游戏zhujue = ting; // 将蛇的状态设置为 "ting"(停止)}break;default:break;}
}
这样的话比如我点一下上,zhujue这个值就会一直会朝向对于的方向,如下

移动一下

停是应为我在键盘事件中设置了如果为空格那么
case Qt::Key_Space:// 如果用户按下空格键,切换蛇的状态为 "ting"(停止)或 "kai"(开始)if (zhujue == ting) {zhujue = kai;time->start(); // 启动定时器,继续游戏}else {time->stop(); // 停止定时器,暂停游戏zhujue = ting; // 将蛇的状态设置为 "ting"(停止)}break;
反之本来就是停止就会继续
5.最后是怎么根据我现在的方向移动蛇呢
比如我现在zhujue=上方,那么会一直执行
void Widget::gengxin(){switch (zhujue) {// 按下上键那么会一直执行case shang:addshang();break;
}
就会一直执行addshang()这个函数,那么
void Widget::addshang()
{// 定义两个 QPointF 类型的变量,用于存储矩形的左上角和右下角坐标QPointF zuoshang; // 左上角坐标QPointF youxia; // 右下角坐标// 检查蛇头是否超出窗口上边界if (she[0].y() - 20 <= 0) {// 如果蛇头超出上边界,将左上角坐标设置为当前位置的 x 坐标和窗口的高度 - 20zuoshang = QPointF(she[0].x(), this->height() - 20);// 右下角坐标设置为左上角坐标的 x 坐标 + 20 和窗口的高度youxia = QPointF(she[0].x() + 20, this->height());}else {// 如果蛇头未超出上边界,将左上角坐标设置为当前位置的 x 和 y 坐标,但 y 坐标减去 20zuoshang = QPointF(she[0].x(), she[0].y() - 20);// 右下角坐标设置为蛇头矩形的右上角坐标youxia = QPointF(she[0].topRight());}// 在蛇的头部插入一个新的矩形,使用左上角和右下角坐标创建矩形she.insert(0, QRectF(zuoshang, youxia));
}
这段代码是当向上移动时,那么对于蛇的每一个元素会将坐上坐标的x减去20,实现蛇的移动,如果出现she[0].y()的距离快到边框时,那么会重置到下面,详细代码如下
void Widget::addshang()
{// 定义左上角和右下角的 QPointF 类型变量,用于表示新矩形的坐标QPointF zuoshang; // 左上角坐标QPointF youxia; // 右下角坐标// 检查蛇头是否超出窗口的上边界if (she[0].y() - 20 <= 0) {// 如果蛇头超出上边界,将左上角坐标设置为蛇头的 x 坐标和窗口高度减去 20zuoshang = QPointF(she[0].x(), this->height() - 20);// 右下角坐标设置为左上角坐标的 x 坐标加上 20 和窗口的高度youxia = QPointF(she[0].x() + 20, this->height());} else {// 如果蛇头未超出上边界,将左上角坐标设置为蛇头的 x 坐标和 y 坐标减去 20zuoshang = QPointF(she[0].x(), she[0].y() - 20);// 右下角坐标设置为蛇头矩形的右上角坐标youxia = QPointF(she[0].topRight());}// 在蛇的头部插入一个新的矩形,使用左上角和右下角坐标创建矩形she.insert(0, QRectF(zuoshang, youxia));
}void Widget::addxia()
{// 定义左上角和右下角的 QPointF 类型变量,用于表示新矩形的坐标QPointF zuoshang; // 左上角坐标QPointF youxia; // 右下角坐标// 检查蛇头是否超出窗口的下边界if (she[0].y() + 40 > this->height()) {// 如果蛇头超出下边界,将左上角坐标设置为蛇头的 x 坐标和 0zuoshang = QPointF(she[0].x(), 0);} else {// 如果蛇头未超出下边界,将左上角坐标设置为蛇头底部左侧的坐标zuoshang = she[0].bottomLeft();}// 右下角坐标设置为左上角坐标加上 (20, 20) 的偏移youxia = zuoshang + QPointF(20, 20);// 在蛇的头部插入一个新的矩形,使用左上角和右下角坐标创建矩形she.insert(0, QRectF(zuoshang, youxia));
}void Widget::addzuo()
{// 定义左上角和右下角的 QPointF 类型变量,用于表示新矩形的坐标QPointF zuoshang; // 左上角坐标QPointF youxia; // 右下角坐标// 检查蛇头是否超出窗口的左边界if (she[0].x() - 20 < 0) {// 如果蛇头超出左边界,将左上角坐标设置为窗口宽度减去 20 和蛇头的 y 坐标zuoshang = QPointF(this->width() - 20, she[0].y());} else {// 如果蛇头未超出左边界,将左上角坐标设置为蛇头左上角坐标减去 (20, 0)zuoshang = she[0].topLeft() - QPointF(20, 0);}// 右下角坐标设置为左上角坐标加上 (20, 20) 的偏移youxia = zuoshang + QPointF(20, 20);// 在蛇的头部插入一个新的矩形,使用左上角和右下角坐标创建矩形she.insert(0, QRectF(zuoshang, youxia));
}void Widget::addyou()
{// 定义左上角和右下角的 QPointF 类型变量,用于表示新矩形的坐标QPointF zuoshang; // 左上角坐标QPointF youxia; // 右下角坐标// 检查蛇头是否超出窗口的右边界if (she[0].x() + 20 > this->width()) {// 如果蛇头超出右边界,将左上角坐标设置为 0 和蛇头的 y 坐标zuoshang = QPointF(0, she[0].y());} else {// 如果蛇头未超出右边界,将左上角坐标设置为蛇头右上角坐标zuoshang = QPointF(she[0].topRight());}// 右下角坐标设置为左上角坐标加上 (20, 20) 的偏移youxia = zuoshang + QPointF(20, 20);// 在蛇的头部插入一个新的矩形,使用左上角和右下角坐标创建矩形she.insert(0, QRectF(zuoshang, youxia));
}
这样的话就实现了蛇的移动,但是蛇需要加分,所以需要小球
6.创建小球类,如下
头文件如下
#ifndef QIU_H // 防止头文件重复包含的预处理指令
#define QIU_H#include<QPixmap> // 包含 QPixmap 类的头文件,用于处理图片
#include <QObject> // 包含 QObject 类的头文件,用于定义对象
#include<QRect> // 包含 QRect 类的头文件,用于定义矩形区域class qiu : public QObject // 定义一个名为 qiu 的类,继承自 QObject
{Q_OBJECT // 使用 Qt 的宏,标记这个类为 Qt 对象public:explicit qiu(QObject *parent = nullptr); // 构造函数,可以接受一个父对象指针QPixmap p; // 用于存储蛇的图片int x; // 蛇的 x 坐标int y; // 蛇的 y 坐标QRect kuang; // 蛇的判定框,用于碰撞检测void gengxin(); // 用于更新蛇的移动和坐标位置的函数signals: // Qt 信号声明部分};#endif // QIU_H // 结束头文件的条件编译指令
源文件cpp为
#include "qiu.h" // 包含自定义头文件 "qiu.h"
#include <ctime> // 包含时间头文件,用于获取随机种子qiu::qiu(QObject *parent) : QObject(parent)
{p.load(":/C:/Users/Administrator/Pictures/tp4.png"); // 加载图片文件srand((unsigned int)time(NULL)); // 使用当前时间作为随机数种子this->x = rand() % (600 - 20); // 生成 x 坐标的随机值this->y = rand() % (368 - 20); // 生成 y 坐标的随机值this->kuang.setWidth(20); // 设置矩形宽度为 20this->kuang.setHeight(20); // 设置矩形高度为 20kuang.moveTo(x, y); // 移动矩形到指定的 (x, y) 坐标
}void qiu::gengxin()
{this->x = rand() % (600 - 20); // 生成新的 x 坐标的随机值this->y = rand() % (368 - 20); // 生成新的 y 坐标的随机值kuang.moveTo(x, y); // 移动矩形到新的 (x, y) 坐标
}
球的属性为:x坐标,y坐标,判定框,背景图片
球的行为为:瞬移,在被蛇吃掉时瞬移
7.最后是判定如下,在定时检查遍历蛇链表是否和小球的判定框相撞
if(she[0].intersects(yuan->kuang)){//判断蛇头元素是否和小球的框碰撞,碰撞执行gengxin()函数yuan->gengxin();
}

碰撞之后执行函数
void qiu::gengxin()
{this->x=rand()%(600-20); //随机框内x点this->y=rand()%(368-20); //随机框内y点kuang.moveTo(x,y); //随机移动
}

随机刷新到一个地方
这样就简单的实现了这个贪吃蛇的游戏
相关文章:
使用C++的QT框架实现贪吃蛇
最近刷抖音经常看到别人使用类似chatGPT的al工具实现这个贪吃蛇游戏,正好我之前也写过,那么今天看看怎么去实现这个简单的游戏 我这边使用的是C的QT框架,当然用哪些框架都可以,主要是逻辑思路 1.生成画布,开始是一些…...
如何发布自己的golang库
如何发布自己的golang库 1、在 github/gitee 上创建一个 public 仓库,仓库名与 go 库名一致,然后将该仓库 clone 到本地。 本文这里使用 gitee。 $ git clone https://gitee.com/zsx242030/goutil.git2、进入项目文件夹,进行初始化。 $ go…...
梳理自动驾驶中的各类坐标系
目录 自动驾驶中的坐标系定义 关于坐标系的定义 几大常用坐标系 世界坐标系 自车坐标系 传感器坐标系 激光雷达坐标系 相机坐标系 如何理解坐标转换 机器人基础中的坐标转换概念 左乘右乘的概念 对左乘右乘的理解 再谈自动驾驶中的坐标转换 本节参考文献 自动驾驶…...
一个可以自动把微信聊天收到的二维码图片实时提取出来并分类的软件
10-1 如果你有需要实时地、自动地把微信各个群收到的二维码图片提取出来的需求,那本文章适合你,本文章的主要内容是教你如何实现自动提取微信收到的二维码图片,助你快速扫码,永远比别人领先一步。 首先需要准备好的材料…...
02-React组件与模块
组件与模块 前期准备 安装React官方浏览器调试工具,浏览器扩展搜索即可 比如红色的React就是本地开发模式 开启一个用React写的网站,比如美团 此时开发状态就变成了蓝色 组件也能解析出来 何为组件&模块 模块,简单来说就是JS代…...
项目实战:新增@RequestMapping和@GetMapping和@PostMapping三个注解
1、RequestMapping package com.csdn.mymvc.annotation; import java.lang.annotation.*; Target(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Inherited public interface RequestMapping {String value(); }2、PostMapping package com.csdn.mymvc.annotation; im…...
基于AOSP源码Android-10.0.0_r41分支编译,framework开发,修改系统默认字体大小
文章目录 基于AOSP源码Android-10.0.0_r41分支编译,framework开发,修改系统默认字体大小 基于AOSP源码Android-10.0.0_r41分支编译,framework开发,修改系统默认字体大小 主要修改一个地方就行 代码源码路径 frameworks/base/co…...
如何再kali中下载iwebsec靶场
这个靶场有三种搭建方法: 第一种是在线靶场:http://www.iwebsec.com:81/ 第二种是虚拟机版本的,直接下载到本地搭建 官网地址下载:http://www.iwebsec.com/ 而第三种就是利用docker搭建这个靶场,我这里是用kali进行…...
Spring Boot 使用断言抛出自定义异常,优化异常处理机制
文章目录 什么是断言?什么是异常?基于断言实现的异常处理机制创建自定义异常类创建全局异常处理器创建自定义断言类创建响应码类创建工具类测试效果 什么是断言? 实际上,断言(Assertion)是在Java 1.4 版本…...
vue基于ElementUI/Plus自定义的一些组件
vue3-my-ElementPlus 源码请到GitHub下载使用MyTable、MySelect、MyPagination 置顶|Top | 使用案例: 1.0 定义表格数据(测试使用) data() {return {tableData: [],value:[],valueList: [],}; },// 构造表格测试数据// 1 第一行…...
leetcode刷题日记:69.sqrt(x)
给出一个非负的整数x,返回x的平方根向下取整的结果,这个被返回的数也应该是一个非负的值。 对我们的要求是不能使用任何内置的指数函数与操作,官方还给了我们例子: 在C种不能使用pow(x, 0.5) 在python不能使用 x**0.5 既然官方已经…...
[尚硅谷React笔记]——第9章 ReactRouter6
目录: 课程说明一级路由重定向NavLink高亮useRoutes路由表嵌套路由路由的params参数路由的search参数路由的state参数编程式路由导航useRouterContextuseNavigationTypeuseOutletuseResolvedPath()总结项目地址 1.课程说明 概述 React Router以三个不同的包发布…...
强大的pdf编辑软件:Acrobat Pro DC 2023中文
Acrobat Pro DC 2023是一款强大的PDF编辑和管理软件,它提供了广泛的功能,使用户能够轻松创建、编辑、转换和共享PDF文档。通过直观的界面和先进的工具,用户可以快速进行文本编辑、图像调整、页面管理等操作,同时支持OCR技术&#…...
玩一下Spring Boot
文章目录 1 开发环境1.1 JDK1.2 IntelliJ IDEA2 Spring Boot2.1 创建项目2.2 创建模板页面2.3 创建控制器2.4 启动项目2.5 访问页面1 开发环境 1.1 JDK 安装JDK21 配置环境变量 在命令行查看JDK版本 玩一玩jshell...
一个高性能类型安全的.NET枚举实用开源库
从零构建.Net前后端分离项目 枚举应该是我们编程中,必不可少的了,今天推荐一个.NET枚举实用开源库,它提供许多方便的扩展方法,方便开发者使用开发。 01 项目简介 Enums.NET是一个.NET枚举实用程序库,专注于为枚举提…...
c#字符串格式化
字符串格式化是一种将变量的值插入到字符串中的方法。它允许我们创建动态的字符串,其中包含变量的值。 string.Format 通过在字符串中使用占位符{0},{1}等,我们可以指定要插入的变量的位置。然后,通过在string.Format方法的参数…...
AMD老电脑超频及性能提升方案及实施
收拾电子元件的时候找到了若干古董的CPU 其中有一个X3 440 是原来同学主板烧了之后给我的,我从网上配了AM2 昂达主板,然后又买了AMD兼容内存,组成了win7 64位电脑,用起来非常不错,我把硬件配置和升级过程说明下&#x…...
Github 自动化部署到GitHub Pages
1.准备工作 新建仓库 新建项目 配置 vite.config.ts base: ./,部署应用包时的基本URL,例:vue-cli 5.x 配置 publicPath 推送到远程仓库 2.配置 GitHub Token 点击 Settings -> Actions -> General 找到 Workflow permissions,选中第…...
Golang 串口通信
简介 串口通信是一种常见的硬件通信方式,用于在计算机和外部设备之间传输数据。Golang(Go语言)作为一种高效、可靠的编程语言,提供了丰富的库和工具用于串口通信。本文将介绍如何使用Golang进行串口通信,包括串口配置…...
项目管理之如何识别并应对项目风险
项目风险管理是项目管理中不可忽视的环节,如何识别并应对项目的风险对于项目的成功实施至关重要。本文将介绍风险管理的流程、风险分解结构、定性及定量风险评估方法,以及消极和积极的风险应对策略,旨在帮助读者更好地理解和应对项目风险。 …...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...
R语言速释制剂QBD解决方案之三
本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
Python Ovito统计金刚石结构数量
大家好,我是小马老师。 本文介绍python ovito方法统计金刚石结构的方法。 Ovito Identify diamond structure命令可以识别和统计金刚石结构,但是无法直接输出结构的变化情况。 本文使用python调用ovito包的方法,可以持续统计各步的金刚石结构,具体代码如下: from ovito…...
Java数值运算常见陷阱与规避方法
整数除法中的舍入问题 问题现象 当开发者预期进行浮点除法却误用整数除法时,会出现小数部分被截断的情况。典型错误模式如下: void process(int value) {double half = value / 2; // 整数除法导致截断// 使用half变量 }此时...
Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)
引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
深入理解Optional:处理空指针异常
1. 使用Optional处理可能为空的集合 在Java开发中,集合判空是一个常见但容易出错的场景。传统方式虽然可行,但存在一些潜在问题: // 传统判空方式 if (!CollectionUtils.isEmpty(userInfoList)) {for (UserInfo userInfo : userInfoList) {…...
