使用C++的QT框架实现贪吃蛇

news2024/11/25 1:04:56

最近刷抖音经常看到别人使用类似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);

47ebe6630c42442dbfe5d3d2353e8824.png

可能不是那么美观,主要是实现功能,之后的话都可以改良

 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_NAMESPACE

class 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

如上所示,蛇的移动是一个定时器,使用定时器进行绘画蛇这个数组,定时器每次到对应的时间会首先增加一个新元素到蛇前面,将最后一个元素上升,如图

d5e55f937d734f8a89a4938236d73718.png

使用绘画室事件画蛇,画蛇的话只需要在这个框内蛇的坐标,要么是左上角加上宽高,或者左上角坐标,右下角坐标等

应为我这边使用的蛇的身躯是一些小正方形组成的,所以可以直接使用左上角坐标加上宽高为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这个值就会一直会朝向对于的方向,如下

513a9fa0f56a4ce79be65d11d1f1a26c.png

移动一下 

63c4518284d04b88a6017d0ad099be8a.png

停是应为我在键盘事件中设置了如果为空格那么 

 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 坐标和窗口的高度 - 20
        zuoshang = QPointF(she[0].x(), this->height() - 20);
        // 右下角坐标设置为左上角坐标的 x 坐标 + 20 和窗口的高度
        youxia = QPointF(she[0].x() + 20, this->height());
    }
    else {
        // 如果蛇头未超出上边界,将左上角坐标设置为当前位置的 x 和 y 坐标,但 y 坐标减去 20
        zuoshang = 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 坐标和窗口高度减去 20
        zuoshang = QPointF(she[0].x(), this->height() - 20);
        // 右下角坐标设置为左上角坐标的 x 坐标加上 20 和窗口的高度
        youxia = QPointF(she[0].x() + 20, this->height());
    } else {
        // 如果蛇头未超出上边界,将左上角坐标设置为蛇头的 x 坐标和 y 坐标减去 20
        zuoshang = 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 坐标和 0
        zuoshang = 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);         // 设置矩形宽度为 20
    this->kuang.setHeight(20);        // 设置矩形高度为 20

    kuang.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();
}

25be32b166fc41c18010822c3b5d3cf5.png

碰撞之后执行函数

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

18193f1262814f7288e66b43e24e1ba9.png

随机刷新到一个地方

这样就简单的实现了这个贪吃蛇的游戏

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

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

相关文章

MySQL(6):多表查询

多表查询&#xff0c;也称为关联查询&#xff0c;指两个或更多个表一起完成查询操作。 前提条件&#xff1a; 这些一起查询的表之间是有关系的&#xff08;一对一、一对多&#xff09;&#xff0c;它们之间一定是有关联字段&#xff0c;这个关联字段可能建立了外键&#xff0c;…

医院小程序解决方案:让医疗服务触手可及

随着移动互联网的发展&#xff0c;小程序已经成为各行各业进行营销和提供服务的重要工具。医疗陪诊行业也不例外。本教程将带领大家了解如何快速掌握医疗陪诊小程序的搭建技巧&#xff0c;帮助大家轻松搭建自己的医疗陪诊小程序。 步骤一&#xff1a;登录乔拓云平台进入商城后台…

分类预测 | Matlab实现SMA-KELM黏菌优化算法优化核极限学习机分类预测

分类预测 | Matlab实现SMA-KELM黏菌优化算法优化核极限学习机分类预测 目录 分类预测 | Matlab实现SMA-KELM黏菌优化算法优化核极限学习机分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现SMA-KELM黏菌优化算法优化核极限学习机分类预测(完整源码和数…

进程空间管理:用户态和内核态

用户态虚拟空间里面有几类数据&#xff0c;例如代码、全局变量、堆、栈、内存映射区等。在 struct mm_struct 里面&#xff0c;有下面这些变量定义了这些区域的统计信息和位置。 unsigned long mmap_base; /* base of mmap area */ unsigned long total_vm; /* Total page…

sql根据同一字段不同值时间升序将序

SELECT ID,T_STATUS,T_TIME FROMs order by CASE WHEN T_STATUS 0 THEN 100 ELSE 1000 END,case when T_STATUS 0 then T_TIME end ,case when T_STATUS ! 0 then T_TIME end desc;

Java规则引擎2.1.8版本新增功能说明

规则引擎更新功能 新增: 1.决策结束节点新增结果导出excel功能&#xff1b; 在决策流程的结束节点&#xff0c;可以将决策结果导出为Excel文件。这个功能为用户提供了更多的灵活性和便利性&#xff0c;使他们能够轻松地将决策结果数据进行进一步的分析和处理。 2.新增公有变…

【教3妹学编程-算法题】2913. 子数组不同元素数目的平方和 I

-----------------第二天------------------------ 面试官 : 好的&#xff0c; 我们再来做个算法题吧。平时工作中会尝试用算法吗&#xff0c; 用到了什么数据结构&#xff1f; 3妹 : 有用到&#xff0c; 用到了 bla bla… 面试官 : 好的&#xff0c; 题目是这样的&#xff1…

kali搭建Cobalt strike挂马

APT攻击是高级可持续化攻击&#xff0c;一般是团队分工合作 Cobalt strike红队用的渗透工具。多协议&#xff0c;端口&#xff0c;插件 服务端运行 cobaltstrike4提示java异常&#xff0c;改用cobaltstrike 3.14版本 服务端搭建&#xff1a;./teamserver 服务器IP地址 密码 …

2G-GCN:Multi-person HOI Recognition in Videos

Geometric Features Informed Multi-person Human-object Interaction Recognition in Videos解读 摘要简介 2. Related Work2.1 图像中的HOI检测2.2 视频中的HOI识别2.3 HOI识别数据集2.4 几何特征为HOI分析提供信息 3. 多人HOI数据集(MPHOI-72)4. Two-level Geometric Featur…

MySQL -- 复合查询及内外连接

MySQL – 复合查询及内外连接 文章目录 MySQL -- 复合查询及内外连接一、基本查询回顾1.单表查询 二、多表查询1.表的组合2.案例 三、自连接1.案例四、子查询1.单行子查询2.多行子查询3.多列子查询4.在from子句中使用子查询5.合并查询 五、内连接六、外连接1.左外连接2.右外连接…

C语言程序设计(第五版)谭浩强 第三章课后题答案

第三章 1、假如我国国民生产总值的年增长率为7%&#xff0c; 计算10年后我国国民生产总值与现在相比增长多少百分比。计算公式为 ,其中r为年增长率&#xff0c;n为年数&#xff0c;p为与现在相比的倍数。 #include<stdio.h> #include<math.h>int main(){float r,…

基于QT的简易计算器(一)

目录 0 简介1.设计原理1.1界面设计1.1.1界面基本布局1.1.2 界面调整和美化1.1.2 控件重命名 1.2 连接信号和槽1.3 软件逻辑1.3.1四则运算1.3.2 连续运算&#xff08;不完全&#xff09;的原理1.3.3 清屏1.3.4 退格1.3.5 等于1.3.6 小数点 2.总结与拓展 0 简介 最近在学QT&…

数字化浪潮下,AI数字人融入多元化应用场景

随着AI数字人技术的发展&#xff0c;各个行业都在不断挖掘数字人更多的潜力&#xff0c;VR全景中的AI数字人功能逐渐成为了一种新颖的用户交互方式。AI数字人将企业的文化、品牌价值、商业服务等充分结合为一体&#xff0c;为企业提供了全新的机会&#xff0c;从客户互动到营销…

VueX中的getters配置项

一、配置getters属性 当我们想对VueX中的state中的数据进行处理&#xff0c;我们就可以使用getter配置项。 就像是组件中的数据和计算属性之间的关系。 const getters { 属性名 (state) { return 处理结果; } } 我们能够直接拿到state进行操作&#xff0c;并返回操作结果。 …

LeetCode——数组 移除元素(Java)

移除元素 简介[简单] 27. 移除元素[简单] 26. 删除有序数组中的重复项[简单] 283. 移动零[简单] 844. 比较含退格的字符串[简单] 977. 有序数组的平方 简介 记录一下自己刷题的历程以及代码。写题过程中参考了 代码随想录。会附上一些个人的思路&#xff0c;如果有错误&#x…

红黑树——插入底层实现【C++】面试重灾区!!

目录 前言 一&#xff0c;概念 定义 二&#xff0c;insert 情况一&#xff1a; 情况二&#xff1a; 情况三&#xff1a; insert代码 三&#xff0c; 红黑树验证(面试题) 产生随机数验证 每日一图区&#xff1a; 前言 AVL树是一棵绝对平衡的二叉搜索树&#xff0c;其…

IM 系统通信系统是什么

IM&#xff08;Instant Messaging&#xff09;系统是一种实时通信系统&#xff0c;允许用户通过互联网或内部网络即时发送文本消息、文件、音频、视频和其他类型的数据给其他用户或群组。这类系统通常支持在线状态显示、消息确认、消息历史记录和多端同步等功能&#xff0c;以提…

WEB3 在 React搭建的Dapp中通过redux全局获取并存储用户ETH与自定义token与交易所存储数量

上文 web3 在React dapp中全局管理web3当前登录用户/智能合约等信息中 我们简单操作&#xff0c;将web3的公共信息都存在了window对象上 然后 我们先来启动一下环境 终端输入 ganache -d打开项目 终端输入 truffle migrate --reset在区块链上发布一下智能合约 然后 我们在…

FedAT:分层机制更新的联邦学习

文章链接&#xff1a;FedAT: A Communication-Efficient Federated Learning Method with Asynchronous Tiers under Non-IID Data 发表会议: SC’21 (International Conference for High Performance Computing, Networking, Storage, and Analysis) 高性能计算&#xff0c;体…

Redis安装与常用命令

目录 一、Reids简介 二、Redis安装 2.1 Linux安装 2.2 Windows安装 三、Redis常用命令 3.1 Redis字符串 3.2 Redis哈希(Hash) 3.3 Redis列表&#xff08;List&#xff09; 3.4 Redis集合&#xff08;Set&#xff09; 一、Reids简介 Redis&#xff08;Remote Dictiona…