【QT】贪吃蛇小游戏 -- 童年回忆

news2024/12/25 2:03:10

成品展示

项目分析:
🐍基本元素如下

🐍小蛇的设计,初始大小蛇头占一个方块,蛇身占两个方块。
🐍关于小蛇的移动,采用蛇头前进方向增加一个方块,蛇尾减掉一个方块的实现方法。
🐍在移动过程中不能立即向前进反方向移动(如正在向左前进的小蛇,按下向右的按键应当无效果),当小蛇吃到食物后,蛇身自增,并记录得分。
🐍当小蛇碰到边界处 或 自身时游戏结束。
🐍食物随机生成。

工程创建

游戏背景图片准备

小游戏的设计采用的是“穿越方案”,到达边界后会从另一边出现。并未设置墙壁碰撞检测。下图墙壁仅为图片,在代码实现过程中可根据需求添加碰撞检测。

主体框架预览

主要函数分析

按键处理

void Widget::keyPressEvent(QKeyEvent* event)
{
    switch(event->key())
    {
    case Qt::Key_Up:
        if(this->moveFlag != DIR_DOWN)
        {
            this->moveFlag = DIR_UP;
        }
        break;
    case Qt::Key_Down:
        if(this->moveFlag != DIR_UP)
        {
            this->moveFlag = DIR_DOWN;
        }
        break;
    case Qt::Key_Right:
        if(this->moveFlag != DIR_LEFT)
        {
            this->moveFlag = DIR_RIGHT;
        }
        break;
    case Qt::Key_Left:
        if(this->moveFlag != DIR_RIGHT)
        {
            this->moveFlag = DIR_LEFT;
        }
        break;
    case Qt::Key_Space:
        if(this->gameStart == false)
        {
            this->gameStart = true;
            //游戏开始,启动定时器
            this->timer->start(TIME);
        }
        else
        {
            this->gameStart = false;
            this->timer->stop();
        }
        break;
    default:
        break;
    }
}

渲染绘图

//渲染
void Widget::paintEvent(QPaintEvent* et)
{
    QPainter painter(this);
    QPen pen;
    QBrush brush;
   //背景图片
    QPixmap pix;
    pix.load("D:/snake/snake_b1.png");
    painter.drawPixmap(0,0,820,460,pix);


    //画蛇
    pen.setColor(Qt::black);
    brush.setColor(Qt::darkMagenta);
    brush.setStyle(Qt::SolidPattern);

    painter.setPen(pen);
    painter.setBrush(brush);


    for(int i = 0; i<snake.length();++i)
    {
        painter.drawRect(snake[i]);
    }
    //画 食物
    pen.setColor(Qt::red);
    brush.setColor(Qt::red);
    brush.setStyle(Qt::SolidPattern);

    painter.setPen(pen);
    painter.setBrush(brush);

    painter.drawEllipse(this->rewardNode);

    //绘制得分
    QFont font("方正舒体",17,QFont::ExtraLight,false);
    painter.setFont(font);
    painter.drawText((this->width() - 75),this->height()- 427, QString::number(this->score));

    if(checkContact())
    {
        painter.drawText((this->width() - 300)/2,(this->height()-30)/2,QString("小蛇不小心咬到了自己,游戏结束!!!"));
        this->timer->stop();
    }



    QWidget::paintEvent(et);
}

槽函数

void Widget::timeout()
{
    int count = 1;
    //蛇头是否 触碰到食物
    if(snake[0].intersects(this->rewardNode))
    {
        addNewReword();
        score++;
        count++;
    }
    while(count--)
    {
        switch(this->moveFlag)
        {
        case DIR_UP:
            //蛇上方加方格
            addTop();
            break;
        case DIR_DOWN:
            //蛇下方加方格
            addDown();
            break;
        case DIR_LEFT:
            //蛇左边加空格
            addLeft();
            break;
        case DIR_RIGHT:
            //蛇右边加空格
            addRight();
            break;
        default:
            break;
        }
    }
    //删除尾部方块
    deleteLast();
    update();
}

蛇运动

以向上运动为例:

情况1:蛇头运动越界,从另一端“穿越”回游戏界面。

情况2:正常运动

运动实现原理:

向上增加方块,代码实现:

void Widget::addTop()
{
    QPointF leftTop;//左上
    QPointF rightBotom;//右下

    //方案1:如果越界(蛇头碰到墙体) -- 游戏失败
    //方案2:穿墙(上越界,从下边界出来)
    if(snake[0].y() - NODEHEIGHT < 0)
    {
        //方案1:游戏直接结束

        //方案2:穿墙
        leftTop = QPoint(snake[0].x(),this->height() - NODEHEIGHT);
        rightBotom = QPoint(snake[0].x()+NODEHEIGHT,this->height());

    }
    else
    {
        //正常增加方块
        leftTop = QPointF(snake[0].x(),snake[0].y()-NODEHEIGHT);
        rightBotom = snake[0].topRight();
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}

整体代码   

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QList>
#include <Qtime>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE

typedef enum DIR
{
    DIR_LEFT,
    DIR_RIGHT,
    DIR_UP,
    DIR_DOWN
}dir_t;

//超时时间 单位毫秒
#define TIME 100
#define NODEHEIGHT 20

//y轴边界
#define BORDER_X 300
//x轴边界
#define BORDER_Y 500

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
 //槽函数
protected slots:
    //定时器启动 触发该槽函数
    void timeout();
protected:
    //上 -- 蛇身增长
    void addTop();
    //下 -- 蛇身增长
    void addDown();
    //左 -- 蛇身增长
    void addRight();
    //右 -- 蛇身增长
    void addLeft();
    //删除蛇尾方块
    void deleteLast();
    //随机生成食物
    void addNewReword();
    //渲染
    void paintEvent(QPaintEvent* event);
    //蛇头碰到蛇身判断
    bool checkContact();
    //按键处理 -- 重写
    void keyPressEvent(QKeyEvent* event);
private:
    Ui::Widget *ui;
    //记录当前头部朝向
    int moveFlag = DIR_UP;
    //游戏是否开始
    bool gameStart = false;
    //记录蛇身体
    QList<QRectF> snake;
    //定时器
    QTimer* timer;
    //食物
    QRectF rewardNode;
    //积分
    int score;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QKeyEvent>
#include <QTimer>
#include <QPainter>
#include <QPaintEvent>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //初始化定时器
    this->timer = new QTimer();
    //信号和槽
    connect(timer,SIGNAL( timeout() ),this,SLOT( timeout() ));

    //调整窗口带下
    this->resize(820,460);

    //初始化蛇身
    QPointF leftTop = QPointF(200,200);
    QPointF rightBotom = QPointF(220,220);
    snake.insert(0,QRectF(leftTop,rightBotom));

    //初始化食物
    addNewReword();
    //初始化积分
    this->score = 0;
    addTop();
    addTop();
}

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


void Widget::keyPressEvent(QKeyEvent* event)
{
    switch(event->key())
    {
    case Qt::Key_Up:
        if(this->moveFlag != DIR_DOWN)
        {
            this->moveFlag = DIR_UP;
        }
        break;
    case Qt::Key_Down:
        if(this->moveFlag != DIR_UP)
        {
            this->moveFlag = DIR_DOWN;
        }
        break;
    case Qt::Key_Right:
        if(this->moveFlag != DIR_LEFT)
        {
            this->moveFlag = DIR_RIGHT;
        }
        break;
    case Qt::Key_Left:
        if(this->moveFlag != DIR_RIGHT)
        {
            this->moveFlag = DIR_LEFT;
        }
        break;
    case Qt::Key_Space:
        if(this->gameStart == false)
        {
            this->gameStart = true;
            //游戏开始,启动定时器
            this->timer->start(TIME);
        }
        else
        {
            this->gameStart = false;
            this->timer->stop();
        }
        break;
    default:
        break;
    }
}

//渲染
void Widget::paintEvent(QPaintEvent* et)
{
    QPainter painter(this);
    QPen pen;
    QBrush brush;
   //背景图片
    QPixmap pix;
    pix.load("D:/snake/snake_b1.png");
    painter.drawPixmap(0,0,820,460,pix);


    //画蛇
    pen.setColor(Qt::black);
    brush.setColor(Qt::darkMagenta);
    brush.setStyle(Qt::SolidPattern);

    painter.setPen(pen);
    painter.setBrush(brush);


    for(int i = 0; i<snake.length();++i)
    {
        painter.drawRect(snake[i]);
    }
    //画 食物
    pen.setColor(Qt::red);
    brush.setColor(Qt::red);
    brush.setStyle(Qt::SolidPattern);

    painter.setPen(pen);
    painter.setBrush(brush);

    painter.drawEllipse(this->rewardNode);

    //绘制得分
    QFont font("方正舒体",17,QFont::ExtraLight,false);
    painter.setFont(font);
    painter.drawText((this->width() - 75),this->height()- 427, QString::number(this->score));

    if(checkContact())
    {
        painter.drawText((this->width() - 300)/2,(this->height()-30)/2,QString("小蛇不小心咬到了自己,游戏结束!!!"));
        this->timer->stop();
    }



    QWidget::paintEvent(et);
}

void Widget::addTop()
{
    QPointF leftTop;//左上
    QPointF rightBotom;//右下

    //方案1:如果越界(蛇头碰到墙体) -- 游戏失败
    //方案2:穿墙(上越界,从下边界出来)
    if(snake[0].y() - NODEHEIGHT < 0)
    {
        //方案1:游戏直接结束

        //方案2:穿墙
        leftTop = QPoint(snake[0].x(),this->height() - NODEHEIGHT);
        rightBotom = QPoint(snake[0].x()+NODEHEIGHT,this->height());

    }
    else
    {
        //正常增加方块
        leftTop = QPointF(snake[0].x(),snake[0].y()-NODEHEIGHT);
        rightBotom = snake[0].topRight();
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}
//向下增加方块
void Widget::addDown()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].y() + NODEHEIGHT*2 > this->height())//越界
    {
        //修正坐标
        leftTop = QPointF(snake[0].x(),0);
        rightBotom = QPointF(snake[0].x()+NODEHEIGHT,NODEHEIGHT);
    }
    else
    {
        //
        leftTop = snake[0].bottomLeft();
        rightBotom = snake[0].bottomRight() + QPointF(0,NODEHEIGHT);
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addLeft()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].x() - NODEHEIGHT < 0)//越界
    {
        //修正坐标
        leftTop = QPointF(this->width()-NODEHEIGHT,snake[0].y());
    }
    else
    {
        leftTop = snake[0].topLeft() - QPointF(NODEHEIGHT,0);
    }
    rightBotom = leftTop + QPointF(NODEHEIGHT,NODEHEIGHT);

    snake.insert(0,QRectF(leftTop,rightBotom));
}
void Widget::addRight()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].x() + NODEHEIGHT*2 > this->width())//越界
    {
        //修正坐标
        leftTop = QPointF(0,snake[0].y());
    }
    else
    {
        //
        leftTop = snake[0].topRight();
    }
    rightBotom = leftTop + QPointF(NODEHEIGHT,NODEHEIGHT);
    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::deleteLast()
{
    snake.removeLast();
}

void Widget::timeout()
{
    int count = 1;
    //蛇头是否 触碰到食物
    if(snake[0].intersects(this->rewardNode))
    {
        addNewReword();
        score++;
        count++;
    }
    while(count--)
    {
        switch(this->moveFlag)
        {
        case DIR_UP:
            //蛇上方加方格
            addTop();
            break;
        case DIR_DOWN:
            //蛇下方加方格
            addDown();
            break;
        case DIR_LEFT:
            //蛇左边加空格
            addLeft();
            break;
        case DIR_RIGHT:
            //蛇右边加空格
            addRight();
            break;
        default:
            break;
        }
    }
    //删除尾部方块
    deleteLast();
    update();
}
//随机生成食物
void Widget::addNewReword()
{
    rewardNode = QRectF(
        qrand()%(this->width()/20) * 20,
        qrand()% (this->height()/20) * 20,
        NODEHEIGHT,
        NODEHEIGHT
        );
}

bool Widget::checkContact()
{
    for(int i = 0;i<snake.length();++i)
    {
        for(int j = i+1;j<snake.length();++j)
        {
            if(snake[i] == snake[j])
            {
                return true;
            }
        }
    }
    return false;
}

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

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

相关文章

算法学习——华为机考题库3(HJ21 - HJ25)

算法学习——华为机考题库3&#xff08;HJ21 - HJ30&#xff09; HJ21 简单密码 描述 现在有一种密码变换算法。 九键手机键盘上的数字与字母的对应&#xff1a; 1–1&#xff0c; abc–2, def–3, ghi–4, jkl–5, mno–6, pqrs–7, tuv–8 wxyz–9, 0–0&#xff0c;把密码…

2023年计算机视觉领域突破性研究有哪些?

B站&#xff1a;啥都会一点的研究生公众号&#xff1a;啥都会一点的研究生 ​回顾2023年&#xff0c;计算机视觉领域有哪些较为突出的研究成果&#xff1f;一起看看吧 SAM(Segment Anything Model) SAM 由 Meta AI 开发&#xff0c;是 CV 中分割任务的基础模型&#xff0c;彻…

富文本编辑器CKEditor4简单使用-05(开发自定义插件入门)

富文本编辑器CKEditor4简单使用-05&#xff08;开发自定义插件入门&#xff09; 1. CKEditor4插件入门1.1 关于CKEditor4插件的简单安装与使用1.2 参考 2. 开发自定义插件——当前时间插件2.1 创建插件文件目录结构2.2 编写插件原代码2.2.1 编写代码框架2.2.2 创建编辑器命令2.…

【blender插件】(1)快速开始

特性 blender的python API有如下特性: 编辑用户界面可以编辑的任意数据(场景,网格,粒子等)。修改用户首选项、键映射和主题。运行自己的配置运行工具。创建用户界面元素,如菜单、标题和面板。创建新的工具。场景交互式工具。创建与Blender集成的新渲染引擎。修改模型的数据…

迪文串口屏数据的隐藏功能

一、概述 由于项目中在使用迪文屏显示数据的时候&#xff0c;需要在数据为0的时候不显示0&#xff0c;而迪文屏默认的数据变量在无值的时候显示为0&#xff0c;此时可以使用数据的隐藏功能指令 二、具体实现方法 1、可以使用描述指针地址来实现数据的隐藏&#xff0c;查看应用…

「效果图渲染」效果图与3D影视动画渲染平台

效果图渲染和3D影视动画渲染都是视觉图像渲染的领域应用。效果图渲染主要服务于建筑、室内设计和产品设计等行业&#xff0c;这些领域通常对视觉呈现的精度和细节有较高要求。与之相比&#xff0c;3D影视动画渲染则普遍应用于电影、电视、视频游戏和广告等媒体领域&#xff0c;…

2024年第四届工业自动化、机器人与控制工程国际会议 | Ei、Scopus双检索

会议简介 Brief Introduction 2024年第四届工业自动化、机器人与控制工程国际会议&#xff08;IARCE 2024&#xff09; 会议时间&#xff1a;2024年7月5 -7日 召开地点&#xff1a;中国成都 大会官网&#xff1a;www.iarce.org 2024年第四届工业自动化、机器人与控制工程国际会…

零基础学Python之核心基础知识

1.Python入门简介 &#xff08;1&#xff09;什么是Python Life is short, you need Python&#xff01;人生苦短&#xff0c;我用Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性&#xff0c;相比其他语言…

【数据库】创建索引的注意事项

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;数据库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 结语 我的其他博客 前言 在数据库设计和优化中&#xff0c;索引的合理使用是提高查询性能和加速数据检索的关键因素之一。通过选…

Facebook未来展望:社交媒体的下一个篇章

社交媒体一直是连接人与人之间的纽带&#xff0c;而Facebook则一直在推动这一领域的发展。随着科技不断演进和社会需求的不断变迁&#xff0c;Facebook正积极筹谋社交媒体的下一个篇章。本文将深入剖析Facebook的未来展望&#xff0c;探讨其在社交媒体领域所迎接的新时代。 1. …

数字化管理成为了企业数字化转型的重要支撑

随着科技的不断发展&#xff0c;数字化转型已经成为企业发展的重要趋势。数字化转型是指企业利用数字化技术&#xff0c;如云计算、大数据、人工智能等&#xff0c;来提升自身业务水平、提高效率、降低成本&#xff0c;从而实现可持续发展。在这个过程中&#xff0c;数字化管理…

Linux笔记之bash和expect脚本中的$和$argv参数匹配

Linux笔记之bash和expect脚本中的 和 和 和argv参数匹配 —— 杭州 2024-02-03 code review! 文章目录 Linux笔记之bash和expect脚本中的 和 和 和argv参数匹配1.bash脚本2.expect脚本 1.bash脚本 在Linux的bash脚本中&#xff0c;参数和脚本文件名的变量使用方式与你提供的稍…

2024美赛B题详细技术文档22页+配套每小问代码+数据集汇总去+参考论文

2024美赛B题 &#xff08;完整版在文末&#xff09; Maritime Cruises Mini-Submarines (MCMS)是一家总部位于希腊的公司&#xff0c;专门制造能够携 带人类到达海洋最深处的潜水艇。潜水艇是由一艘主船运输和支持的&#xff0c;可以在水下自由 活动。MCMS 现在希望利用他们的潜…

【Vue3实战】TypeScript前端实战基础

【Vue3实战】TypeScript前端实战基础 前言一、TypeScript的由来二、什么是TypeScript?三、静态类型检查四、类型注解和类型推导五、可选参数和默认参数六、接口和类型别名接口接口的可选设置类型 七、类和继承接口的继承交叉类型模拟继承 八、泛型什么是泛型泛型接口泛型函数泛…

使用MATLAB驱动USRP-N320实现OFDM自收自发

文章目录 前言一、收发代码二、截取一帧 OFDM三、执行主函数四、运行结果五、资源自取 前言 本文作为实验结果记录及测试&#xff0c;方便后面回顾所做的工作。本文基于一台电脑和一台 USRP 设备实现了 OFDM 自发和自收功能 一、收发代码 ofdm_tx_rx_test.m 核心代码&#x…

使用 IDEA 开发一个简单易用的 SDK

目录 一、什么是 SDK 二、为什么要开发 SDK 三、开发 SDK 的详细步骤 四、导入 SDK 进行测试 附&#xff1a;ConfigurationProperties 注解的介绍及使用 一、什么是 SDK 1. 定义&#xff1a;软件开发工具包 Software Development Kit 2. 用于开发特定软件或应用程序的工…

大数据应用开发3-Scala笔记1

一、编程框架 Scala语言是在JVM上运行的&#xff0c;兼容Java语法 区分大小写 - Scala是大小写敏感的&#xff0c;这意味着标识Hello 和 hello在Scala中会有不同的含义。 类名 - 对于所有的类名的第一个字母要大写。 如果需要使用几个单词来构成一个类的名称&#xff0c;每个…

单片机学习笔记---定时器/计数器(简述版!)

目录 定时器的介绍 定时计数器的定时原理 定时计数器的内部结构 两种控制寄存器 &#xff08;1&#xff09;工作方式寄存器TMOD &#xff08;2&#xff09;控制寄存器TCON 定时计数器的工作方式 方式0 方式1 方式2 方式3 定时器的配置步骤 第一步&#xff0c;对…

嵌入式软件中常见的 8 种数据结构

数据结构是一种特殊的组织和存储数据的方式&#xff0c;可以使我们可以更高效地对存储的数据执行操作。数据结构在计算机科学和软件工程领域具有广泛而多样的用途。 几乎所有已开发的程序或软件系统都使用数据结构。此外&#xff0c;数据结构属于计算机科学和软件工程的基础。当…

el-table点击某一行选中改变背景色且执行方法

elementUI table表格点击某一行选中并且改变背景色 使用:row-style"rowStyle"及row-click“selectRow”&#xff1a; 其中 selectRow 方法中&#xff1a; row 输出&#xff1a;当前行的内容 column 输出&#xff1a;当前列的信息 event 输出&#xff1a;当前事件 …