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

成品展示

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

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

工程创建

游戏背景图片准备

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

主体框架预览

主要函数分析

按键处理

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

迷你洗衣机哪个牌子好又实惠?最好用的迷你洗衣机分享

随着大家工作的压力越来越大&#xff0c;下了班之后只能想躺平&#xff0c;在洗完澡之后看着还需要手洗的内衣裤真的很头疼。有些小伙伴还有会攒几天再丢进去洗衣机里面一起&#xff0c;而且这样子是非常不好的&#xff0c;用过的内衣裤长时间不清洗容易滋生细菌&#xff0c;而…

Vue3_基础使用

vue2的选项式与vue3的组合式区别&#xff1a; 选项式&#xff1a;vue2中数据与方法计算属性等等&#xff0c;针对一个数据的处理在不同的配置中&#xff0c;当业务复杂时很难维护&#xff0c;修改起来也不好查找。 vue3的组合式&#xff1a;将针对数据的方法计算属性等等放在一…

10.网桥是什么?网桥和路由器及交换机的区别?以太网和令牌环网,nat,查公网ip等

网桥是什么&#xff1f;有什么作用&#xff1f; 网桥是一种网络设备&#xff0c;它可以在数据链路层&#xff08;第二层&#xff09;上连接不同的局域网&#xff08;LAN&#xff09;&#xff0c;并根据MAC地址转发数据帧。网桥的作用是&#xff1a; 隔离碰撞域&#xff0c;提…

QML自定义ComboBox组件,支持动态筛选

QtQuick.Controls提供了ComboBox组件&#xff0c;该组件能够满足日常的下拉选择框的需求&#xff0c;但当需要用户在ComboBox中通过输入关键字进行自动匹配时&#xff0c;原生的组件虽然提供了editable属性用于输入关键字&#xff0c;但是匹配内容不弹出下拉框&#xff0c;无法…

2024年美赛数学建模D题思路分析 - 大湖区水资源问题

# 1 赛题 问题D&#xff1a;大湖区水资源问题 背景 美国和加拿大的五大湖是世界上最大的淡水湖群。这五个湖泊和连接的水道构成了一个巨大的流域&#xff0c;其中包含了这两个国家的许多大城市地区&#xff0c;气候和局部天气条件不同。 这些湖泊的水被用于许多用途&#xff…

《Pandas 简易速速上手小册》第8章:Pandas 高级数据分析技巧(2024 最新版)

文章目录 8.1 使用 apply 和 map 函数8.1.1 基础知识8.1.2 重点案例&#xff1a;客户数据清洗和转换8.1.3 拓展案例一&#xff1a;产品评分调整8.1.4 拓展案例二&#xff1a;地址格式化 8.2 性能优化技巧8.2.1 基础知识8.2.2 重点案例&#xff1a;大型销售数据分析8.2.3 拓展案…

Python之数据分析

【案例】 某公司有2份数据文件&#xff0c;现在需要对其进行数据分析&#xff0c;计算每日的销售额并以柱状图表的形式进行展现。 数据如下&#xff1a; 一月份数据&#xff1a; 二月份数据&#xff1a; 需求分析 根据题目要求我们要得到每日销售额&#xff0c;分析文本数据可以…

一些你可能用到的头文件和函数

1. gets 函数和 fgets 函数。 两者功能相似&#xff0c;都是输入 char 型 字符&#xff0c;但是格式和稳定性有所差别。前者gets稳定性较弱&#xff0c;但是用法简单&#xff0c;格式如下&#xff1a; 现在一些工程都用 fgets 函数&#xff0c;因为它的强大的稳定性&#xff0…

玩转全新nova12系列熄屏显示,做最潮nova星人!

熄屏显示一直是大家非常喜欢的一项功能&#xff0c;可以让我们在不影响他人的情况下随时随时地查看消息提醒。华为nova12全系列机型均支持熄屏显示功能&#xff0c;且在系列上更是有重磅升级&#xff0c;熄屏显示不再只局限于一小块区域&#xff0c;整个屏幕都可以作为显示空间…

【2024美赛C题】网球大佬带你无背景压力分析解题思路!

2024美赛数学建模c题思路分享 加群可以享受定制等更多服务&#xff0c;或者搜索B站&#xff1a;数模洛凌寺 联络组织企鹅&#xff1a;936670395 以下是C题老师的解题思路&#xff08;企鹅内还会随时更新文档&#xff09;&#xff1a; 1背景介绍 2024MCM问题C&#xff1a;网…

基于Python3的OneDrive多网盘挂载程序,带会员/同步等功能,附带系统搭建教程

搭建教程 虚拟主机用户&#xff0c; Apache构架的配置如下&#xff0c;Nginx的我不知道 根目录创建一个.htaccess文件&#xff0c;内容如下&#xff1a; <IfModule mod_rewrite.c> RewriteEngine on RewriteCond %{REQUEST_URI} !^public RewriteRule ^(…

Git介绍与常用命令总结

Git介绍与其常用命令总结 1、Git介绍2、Git的使用3、Git常用命令3.1 初始化仓库3.2 克隆仓库3.3 配置用户信息3.4 提交代码(Commit)3.5 推送代码(Push)3.6 拉取代码(Pull)3.7 分支(Branch)3.8 远程仓库(Remote)3.9 撤销回退本地改动3.10 更新本地仓库与远程仓库 1、Git介绍 Gi…

2024年美国大学生数学建模竞赛F题思路分析

题目 非法野生动物贸易对环境造成了负面影响&#xff0c;并威胁全球生物多样性。据估计&#xff0c;其涉及高达265亿美元的年交易额&#xff0c;被认为是全球所有非法交易中的第四大。[1] 你需要开发一个基于数据驱动的5年项目&#xff0c;旨在显著减少非法野生动物贸易。你的…

npm 和 yarn 的使用

安装 yarn npm i yarn -g查看版本 npm -v yarn --version切换 npm/yarn 的下包镜像源 // 查看当前的镜像源 npm config get registry// 切换淘宝镜像源 // 新的淘宝源&#xff0c;旧的淘宝源已于2022年05月31日零时起停止服务 npm config set registry https://registry.…

鸿蒙ArkUI日期选择组件

鸿蒙ArkUI日期选择组件&#xff0c;基于基础组件进行的二次封装的日期选择组件&#xff0c;快速实现日期选择。 /*** 日期*/ Component export default struct DiygwDate{//绑定的值Link Watch(onValue) value:string;// 隐藏值State valueField: string value;// 显示值Sta…

【靶场实战】Pikachu靶场不安全的文件下载漏洞关卡详解

Nx01 系统介绍 Pikachu是一个带有漏洞的Web应用系统&#xff0c;在这里包含了常见的web安全漏洞。 如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习&#xff0c;那么Pikachu可能正合你意。 Nx02 不安全的文件下载漏洞概述 文件下载功能在很多web系统上都…

移动机器人激光SLAM导航(二):运动控制与传感器篇

参考引用 机器人工匠阿杰wpr_simulation 1. 机器人运动控制 1.1 测试环境安装 wpr_simulation 安装$ mkdir -p catkin_ws/src $ cd catkin_ws/src $ git clone https://github.com/6-robot/wpr_simulation.git $ cd wpr_simulation/scripts/ $ ./install_for_melodic.sh # 自…

Elasticsearch-内存结构

ElasticSearch的内存从大的结构可以分堆内存&#xff08;On Heap&#xff09;和堆外内存&#xff08;Off Heap&#xff09;。Off Heap部分由Lucene进行管理。On Heap部分存在可GC部分和不可GC部分&#xff0c;可GC部分通过GC回收垃圾对象&#xff0c;从而释放内存。不可GC部分不…

【项目日记(七)】第三层: 页缓存的具体实现(上)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:项目日记-高并发内存池⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你做项目   &#x1f51d;&#x1f51d; 开发环境: Visual Studio 2022 项目日…

three.js CSS3DRenderer、CSS3DObject渲染HTML标签

有空的老铁关注一下我的抖音&#xff1a; 效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red;position: relative;"><…