Qt6_贪吃蛇Greedy Snake

news2024/12/27 14:19:00

贪吃蛇Greedy Snake

1分析

首先这是一个贪吃蛇界面,由一个长方形边框和一只贪吃蛇组成

image-20230905161147724

默认开局时,贪吃蛇身体只有3个小方块,使用画笔画出

1.1如何移动

对于蛇的移动,有2种方法

  1. 在一定时间范围内(定时器),未对游戏做出操作(前后左右移动),则整个身体向面朝方向移动一格
  2. 在一定时间范围内(定时器),未对游戏做出操作(前后左右移动),则身体最前面加上一格方块,身体最后面减去一个方块

这里使用第2种方法

如图,有4个坐标,AC分别是原蛇的头部方格的两个坐标

则新产生的方格的B点为(x1,y1-方格高度);D点(x3,y3-方格高度)

image-20230905180018710

注意

向某个方向增加不能无限增加下去,当触碰到顶部时,我们可以设置条件:

  1. 死亡
  2. 穿过从另一个方向开始

这里我们采用第2种方式

1.2向上

如图,此时BD点已经超过x轴,即高度为0的位置

image-20230905180927106

因此我们可以判断:当蛇头部的方格A/C的高度 - 1个方格的高度 < 0 时,则从底部开始进入

image-20230905231105665

  • 移动前:x3=x1+蛇宽度;y3=y1

  • 移动后:x2=x1,y2=窗口高度-蛇宽度;x4=x2+蛇宽度,y4=y2

1.3向下

同理,向下走只要最下面增加,最上面减少即可

image-20230905231120101

  • 移动前:x4=x2+蛇宽度;y2=y4

  • 移动后:x1=x2,y1=0;x3=x1+蛇宽度,y3=蛇高度

1.4向左

  • 移动前:x3=x2+蛇宽;y3=y2+蛇高

  • 移动后:x1=x2-蛇宽,y1=y2;x4=x2,y4=y3

image-20230905235603773

穿越

  • x2=窗口宽度-蛇宽,y2=y1
  • x3=窗口宽度,y3=y4

image-20230907123806011

1.5向右

  • 移动前:x4=x1+蛇宽;y4=y1+蛇高

  • 移动后:x2=x1+蛇宽,y2=y1;x3=x4+蛇宽,y3=y4

image-20230906000046738

穿越

  • x2=0,y2=y1;
  • x3=蛇宽,y3=y4

image-20230907123143492

2如何成长

在界面的不同地方会默认刷新出一个球,蛇吃了这个球身体就会增加一格,同时球消失刷新出现在其他地方

image-20230905162217248

判断球是否被吃掉:即判断蛇的身体是否与球重合,如果重合,增加身体1,同时刷新球出现的位置

3代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#define OUTTIME 100 //超时时间
#define SNAKE_WIDTH 20  //蛇的一个方格宽度
#define SNAKE_HEIGHT 20 //蛇的一个方格高度
#include <QWidget>
#include<QKeyEvent>
#include<QDebug>
#include<QTimer>
#include<QPixmap>
#include<QPainter>
#include<random>
#include<QTime>
#include <QRandomGenerator>

/*
    0.创建蛇
        要求:可以实现前面删除后面增加,可以用LIST链表
    1.按键处理
    2.使用定时器
       2.1关联信号槽
       2.2启动定时器
       2.3实现对应的超时处理(timeOut)函数
    3.渲染图像
*/

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
//定义方向
enum Direction
{
    LEFT,
    RIGHT,
    UP,
    DOWN
};
class Widget : public QWidget
{
    Q_OBJECT
public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
protected:
    //按键处理
    void keyPressEvent(QKeyEvent *event);
    //添加头部
    void addTOP();
    void addBUTOM();
    void addLEFT();
    void addRIGHT();
    //绘制界面
    void paintEvent(QPaintEvent *event);
    //删除尾部
    void deleLAST();
    //初始化食物
    void initFood();
    //判断是否碰撞
    bool isIntersect();
private:
    Ui::Widget *ui;
    int moveFlag = UP;  //方向标志
    bool gameStatu = false;//游戏状态
    //bool isIntersect = false;
    //定时器
    QTimer *timer;
    //事物
    QRectF food;
    //创建蛇
    QList<QRectF> snake;
protected slots:
    void timeOut();//超时函数
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(450,300);
    //使用定时器
    timer = new QTimer();
    //发出信号
    //连接信号
    connect(timer,SIGNAL(timeout()),this,SLOT(timeOut()));

    //初始化蛇
    QRectF rect(this->width()/2,this->height()/2,SNAKE_WIDTH,SNAKE_HEIGHT);
    snake.append(rect); //蛇身+1
    addTOP();   //蛇身+1
    addTOP();

    //初始化食物
    initFood();
}

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

//按键绑定:控制方向
void Widget::keyPressEvent(QKeyEvent *event)
{
    switch(event->key())
    {
    case Qt::Key_Up:
        if(moveFlag != DOWN)
        {
            moveFlag = UP;
        }
        break;
    case Qt::Key_Down:
        if(moveFlag != UP)
        {
            moveFlag = DOWN;
        }
        break;
    case Qt::Key_Left:
        if(moveFlag != RIGHT)
        {
            moveFlag = LEFT;
        }
        break;
    case Qt::Key_Right:
        if(moveFlag != LEFT)
        {
            moveFlag = RIGHT;
        }
        break;
    case Qt::Key_Space:
        if(!gameStatu)
        {
            gameStatu = true;
            //启动定时器
            timer->start(OUTTIME);
        }
        else
        {
            gameStatu = false;
            timer->stop();
        }
        break;
    default:
        return;
        break;
    }
}

//顶部增加
void Widget::addTOP()
{
    QPointF leftTop;    //左上点
    QPointF rightBotom; //右下点

    if(snake[0].y() - SNAKE_HEIGHT < 0 )    //如果超过顶部
    {
        leftTop = QPoint(snake[0].x(),this->height() - SNAKE_HEIGHT);
        rightBotom = QPoint(snake[0].x() + SNAKE_WIDTH,this->height());
    }
    else    //未超过顶部
    {
        leftTop = QPointF(snake[0].x(),snake[0].y() - SNAKE_HEIGHT);
        rightBotom = snake[0].topRight();
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addBUTOM()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].y() + 2*SNAKE_HEIGHT > this->height() )    //如果超过底部
    {
        leftTop = QPointF(snake[0].topLeft().x(),0);
              //                    原x           0
        rightBotom = QPointF(snake[0].bottomRight().x(),SNAKE_HEIGHT);
                    //          右下x+蛇宽               蛇高
    }
    else    //未超过底部
    {
        leftTop = snake[0].bottomLeft();
        rightBotom = snake[0].bottomRight()+QPointF(0,SNAKE_HEIGHT);
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addLEFT()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].x()-SNAKE_WIDTH < 0 )    //超过边缘
    {
        leftTop = QPointF(this->width()-SNAKE_WIDTH,snake[0].topLeft().y());
            //              窗口宽-蛇宽                  左上原y不变
        rightBotom = QPointF(this->width(),snake[0].bottomRight().y());
            //              窗口宽                     右下原y不变
    }
    else    //未超过
    {
        leftTop = QPointF(snake[0].topLeft().x()-SNAKE_WIDTH,snake[0].topLeft().y());
               //               左上x-蛇宽                          左上原y不变
        rightBotom = QPointF(snake[0].bottomRight().x()-SNAKE_WIDTH,snake[0].bottomRight().y());
                //                  右下x-蛇宽                      右下原y不变
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addRIGHT()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].x()+SNAKE_WIDTH*2 > this->width() )    //超过边缘
    {
        leftTop = QPointF(0,snake[0].topLeft().y());
            //           0      左上原y不变
        rightBotom = QPointF(SNAKE_WIDTH,snake[0].bottomRight().y());
            //              蛇宽                     右下原y不变
    }
    else    //未超过边缘
    {
        leftTop = QPointF(snake[0].topLeft().x()+SNAKE_WIDTH,snake[0].topLeft().y());
            //               左上x-蛇宽                          左上原y不变
        rightBotom = QPointF(snake[0].bottomRight().x()+SNAKE_WIDTH,snake[0].bottomRight().y());
            //                  右下x-蛇宽                      右下原y不变
    }
    snake.insert(0,QRectF(leftTop,rightBotom));
}

//绘制界面
void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QPen pen;//画笔
    QBrush brush;//画刷

    //加载背景图片
    QPixmap pix;
    pix.load(":/background/bg_1.jpg");
    painter.drawPixmap(0,0,pix.width(),pix.height(),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]);//画家进行绘画
    }

    //绘制食物
    brush.setColor(Qt::yellow);//画刷颜色
    brush.setStyle(Qt::SolidPattern);//画刷风格
    painter.setPen(pen);    //画家配置画笔
    painter.setBrush(brush);//画家配置画刷
    painter.drawEllipse(food);

    //绘制结束面板
    if(isIntersect())
    {
        QFont font("微软雅黑",30,QFont::ExtraLight,false);
        painter.setFont(font);
        painter.drawText(this->width()/2-120,
                 this->height()/2,
                 QString("GAME OVER!"));
        timer->stop();
    }
}

//去尾
void Widget::deleLAST()
{
    snake.removeLast();
}

// 初始化食物
void Widget::initFood()
{								//QRandomGenerator::global()->generate()随机生成数字
    food = QRectF(QRandomGenerator::global()->generate()%(this->width()/20)*20,
                  QRandomGenerator::global()->generate()%(this->height()/20)*20,
                  SNAKE_WIDTH,SNAKE_HEIGHT);
}

//是否碰撞
bool Widget::isIntersect()
{
    for(int i=1;i<snake.length();i++)
    {
        if(snake[0] == snake[i])
        {
            return true;
        }
    }
    return false;
}

//超时函数
void Widget::timeOut()
{
    int count =1;
    if(snake[0].intersects(food))
    {
        count++;
        //重新初始化食物,即令其刷新位置
        initFood();
    }
    while(count--)
    {
        switch (moveFlag)
        {
    case UP:
        addTOP();
        break;
    case DOWN:
        addBUTOM();
        break;
    case LEFT:
        addLEFT();
        break;
    case RIGHT:
        addRIGHT();
        break;
    default:
        return;
        break;
        }
    }
    deleLAST();
    update();   //重新绘制,即更新图像
}

4效果

//重新初始化食物,即令其刷新位置
initFood();
}
while(count–)
{
switch (moveFlag)
{
case UP:
addTOP();
break;
case DOWN:
addBUTOM();
break;
case LEFT:
addLEFT();
break;
case RIGHT:
addRIGHT();
break;
default:
return;
break;
}
}
deleLAST();
update(); //重新绘制,即更新图像
}


## 4效果

![greedy_snake](https://img-blog.csdnimg.cn/img_convert/709a852d84a10ece80eabe1982fbfda0.gif)

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

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

相关文章

谷歌 reCAPTCHA 人机验证

一、问题 smogon 论坛注册不成功。输入账号、密码、邮箱后显示 You did not complete the CAPTCHA verification properly. Please try again. 即未通过 CAPTCHA 验证。 二、原因 使用的 CAPTCHA 验证是谷歌的 reCAPTCHA 人机验证&#xff0c;国内无法访问谷歌服务。 三、解决…

分页查询实现

目录 1.实体类 2.Mapper层 2.1.xxxMapper接口 2.2.xxxMapper.xml文件 3.Service层 3.1.xxxService接口 3.2.xxxServiceImpl层 4.xxxController层 5.调用接口 6.总结 1.实体类 与数据库交互和与前端交互的实体类 这个Model是与数据库交互的实体类&#xff0c;其中的…

python调用GPT实现:智能用例生成工具

工具作用&#xff1a; 根据输入的功能点&#xff0c;生成通用测试点 实现步骤 工具实现主要分2个步骤&#xff1a; 1.https请求调用Gpt,将返回响应结果保存为.md文件 2.用python实现 将 .md文件转换成.xmind文件 3.写个简单的前端页面&#xff0c;调用上述步骤接口 详细代…

7个数据科学Python库将为您节省大量时间

7个数据科学Python库将为您节省大量时间 在进行数据科学时&#xff0c;您可能会花费大量时间编写代码并等待计算机运行某些操作。我挑选了一些可以在这两种情况下节省您时间的Python库。即使您只将其中一个库纳入您的工具库&#xff0c;您仍然可以在下次项目工作时节省宝贵的时…

揭示OLED透明屏数据:探索未来显示技术的潜力

OLED透明屏作为一项颇具吸引力的显示技术&#xff0c;以其独特的特点和卓越的画质在市场上引起了广泛关注。 在这篇文章中&#xff0c;尼伽将和大家一起深入探索OLED透明屏的数据&#xff0c;通过具体的市场趋势分析、技术指标解析、应用领域探讨和未来前景展望&#xff0c;为…

【C++基础】实现日期类

​&#x1f47b;内容专栏&#xff1a; C/C编程 &#x1f428;本文概括&#xff1a; C实现日期类。 &#x1f43c;本文作者&#xff1a; 阿四啊 &#x1f438;发布时间&#xff1a;2023.9.7 对于类的成员函数的声明和定义&#xff0c;我们在类和对象上讲到过&#xff0c;需要进行…

mac 查看端口占用

sudo lsof -i tcp:port # 示例 sudo lsof -i tcp:8080 杀死进程 sudo kill -9 PID # 示例 sudo kill -9 8080

“搞事情”?OpenAl将于11月召开其首届开发者大会

摘要&#xff1a;OpenAI也要召开它的第一届开发者大会了。这次活动&#xff0c;或许标志着OpenAI向其下一阶段的商业开发迈出了关键一步。 昨天&#xff0c;OpenAI宣布将于11月6日举办其首次开发者大会。在这场名为“OpenAI DevDay”的活动中&#xff0c;OpenAI的技术人员将进行…

欧科云链与HashKey Exchange达成合作,助力香港虚拟资产合规化

继8月10日 欧科云链 与 华为云 达成合作之后&#xff0c; 今天&#xff0c;欧科云链 又与 Hashkey Exchange 共同宣布正式达成合作&#xff01; 这次与Hashkey达成合作&#xff0c;双方又将在Web3行业中谱写怎样的故事&#xff1f; 9月6日&#xff0c;欧科云链控股有限公司&…

2023 年高教社杯全国大学生数学建模竞赛题目 C 题 蔬菜类商品的自动定价与补货决策

C 题 蔬菜类商品的自动定价与补货决策 在生鲜商超中&#xff0c;一般蔬菜类商品的保鲜期都比较短&#xff0c;且品相随销售时间的增加而变差&#xff0c; 大部分品种如当日未售出&#xff0c;隔日就无法再售。因此&#xff0c;商超通常会根据各商品的历史销售和需求情况每天进…

生物通路数据库收录1600+整合的经典通路

生物通路数据库为科学家提供了关于生物通路的大量信息和资源&#xff0c;特别是在数据整合、信息检索、数据可视化分析、数据交互、生物学研究等方面&#xff0c;积极推动了生物学研究和科学的发展。 世界各地正在创建各种类型的通路数据库&#xff0c;每个数据库都反映了其创…

快递批量查询高手必备的实用工具

在网购日益普及的今天&#xff0c;我们经常需要查询快递的物流信息。但是&#xff0c;传统的查询方式一个一个地输入快递单号&#xff0c;不仅费时费力&#xff0c;还容易出错。有没有一种方法可以批量查询多个快递单号呢&#xff1f;答案是肯定的&#xff0c;今天我们就来介绍…

2140. 解决智力问题;1401. 圆和矩形是否有重叠;901. 股票价格跨度

2140. 解决智力问题 核心思想:动态规划。dp[i]表示解决i-n-1的问题所能获得的最高分数&#xff0c;注意需要倒叙遍历&#xff0c;因为i的状态由后面的状态转移过来的。 1401. 圆和矩形是否有重叠 核心思想&#xff1a;分情况讨论&#xff0c;圆心情况。借用别人一张图说明。 …

JWT-Token升级方案

1. 介绍 JWT是JSON Web Token的缩写&#xff0c;即JSON Web令牌&#xff0c;是一种自包含令牌。 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息&#xff0c;以便于从资源服务器获取资…

ATFX汇市:美联储褐皮书透露就业市场新动向,美元指数中期多头趋势延续

ATFX汇市&#xff1a;今日2:00&#xff0c;美联储发布褐皮书&#xff0c;关于就业市场&#xff0c;其中提到&#xff1a;全国就业增长乏力&#xff0c;大多数地区的劳动力成本压力增长加剧&#xff0c;企业预计工资增长将在短期内普遍放缓。从7月、8月的非农就业报告当中&#…

数据结构和算法(2):向量

抽象数据类型 数组到向量 C/C 中&#xff0c;数组A[]中的元素与[0,n)内的编号一一对应&#xff0c;A[0],A[1],...,A[n-1]&#xff1b;反之&#xff0c;每个元素均由&#xff08;非负&#xff09;编号唯一指代&#xff0c;并可直接访问A[i] 的物理地址 Ai s&#xff0c;s 为单…

vue-elementPlus自动按需导入和主题定制

elementPlus自动按需导入 装包 -> 配置 1. 装包&#xff08;主包和两个插件包&#xff09; $ npm install element-plus --save npm install -D unplugin-vue-components unplugin-auto-import 2. 配置 在vite.config.js文件中配置&#xff0c;配置完重启&#xff08;n…

SM5202 是一款完整的采用恒定电流/恒定电压的单节锂电池线性充电器

简介&#xff1a; SM5202 是一款完整的采用恒定电流/恒定电压的单节锂电池线性充电器&#xff0c;并带有锂电池正负极反接保护功能&#xff0c;可以保护芯片和用户安全。由于采用了内部 PMOSFET 架构&#xff0c;加上防倒充电路&#xff0c;所以不需要外部检测电阻和隔离二极管…

Java之包装类的算法小题的练习

算法小题 练习一&#xff1a; 需求&#xff1a; 键盘录入一些1~10日之间的整数&#xff0c;并添加到集合中。直到集合中所有数据和超过200为止。 代码示例&#xff1a; public class Test1 {public static void main(String[] args) {/*键盘录入一些1~10日之间的整数&…