基于C++的QT实现贪吃蛇小游戏

文章目录:

一:效果演示

二:实现思路

三:代码实现 

widget.h

widget.cpp

main.cpp


一:效果演示

 效果图◕‿◕✌✌✌ 

代码下载

二:实现思路

 通过按键控制蛇的移动,每吃一个商品蛇身就会加长,如果蛇身头尾相碰就结束游戏

声明渲染绘图:画笔画刷进行相关的渲染背景蛇和奖品	


按键处理机制:方便后面的键盘操作
	定时器:到时就会触发
	按下空格:代表开始
	上下左右键控制蛇的移动方向:DIR_DOWN DIR_UP DIR_LEFT DIR_RIGHT

蛇:
	蛇的表示:矩形的两点确定小方块,3个小方块为初始蛇身
	判断蛇身蛇头:如果蛇头蛇尾接触就结束游戏
	蛇身变动:相交就吃掉奖品,吃一个加一个小方块
	

奖品
	奖品的表示:小红点
	奖品的添加:蛇吃完一个奖品就马上随机添加一个奖品

蛇吃奖品:
	吃一个奖品会加一个方块:上面、下面、左面、右面

三:代码实现 

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
//按键
#include <QKeyEvent>
//定时器
#include <QTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE



//定义方向
enum Dirct{DIR_LEFT,DIR_RIGHT,DIR_DOWN,DIR_UP};


class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    //声明渲染绘图
    void paintEvent(QPaintEvent *event);

    //声明按键处理
    void keyPressEvent(QKeyEvent *event);

    //声明上面加一个函数
    void addTop();
    //声明下面加一个函数
    void addDown();
    //声明左面加一个函数
    void addLeft();
    //声明右面加一个函数
    void addRight();

    //声明减去一个模块
    void deleteLast();

    //声明添加奖品函数
    void addNewReword();

    //声明判断蛇头蛇身
    bool checkContact();

private:
    Ui::Widget *ui;
    //初始化定义方向
    int moveFlag = DIR_UP;
    //定义启动的开始
    bool gameStart = false;

    //定义定时器
    QTimer *timer;
    int time = 100;//超时事件间隔(毫秒)

    //蛇的表示  矩形两点确定(容器)
    QList <QRectF> snake;

    //小方块表示
    int nodeWidth = 20;
    int nodeHeight = 20;

    //奖品的表示
    QRectF rewardNode;

protected slots:
    //定义超时函数
    void timeout();
};


#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <qmediaplayer.h>

//画家
#include <QPainter>
//文字
#include <QRandomGenerator>
//背景音乐
#include <QtMultimedia/QtMultimedia>
#include <QtMultimediaWidgets/QVideoWidget>
#include <QApplication>
#include <QMediaPlayer>
#include <QUrl>
#include <QFileDialog>


Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    //定义窗口大小
        //resize(800,600);
        this->setFixedSize(800,600);

    QMediaPlayer *player = new QMediaPlayer;
    player->setSource(QUrl::fromLocalFile("F:/QT/Snake/images/mu.mp3"));
    player->play();

    //实现定时器 可以让蛇动起来
        //创建定时器
        timer = new QTimer();
        //信号槽   timeout超时
        connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));

    //初始化蛇身
        QRectF rect(400,300,nodeWidth,nodeHeight);
        //小方块加到蛇身上去
        snake.append(rect);
        //再加两端
        addTop();
        addTop();

    //初始化奖品
        addNewReword();


    //背景音乐
//        //创建一个显示视频的控件
//                QVideoWidget* videowin = new QVideoWidget(this);
//                videowin->resize(400,300);
//                //添加一个播放器
//                QMediaPlayer *player = new QMediaPlayer(this);
//                player->setVideoOutput(videowin);
//                //音乐
//                player->setMedia(QMediaContent(QUrl::fromLocalFile("F:/QT/Snake/images/game.mp3")));
//                //player->setVolume(50);//音量
//                //开始播放
//                player->play();






}

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

//实现按键函数    控制方向
void Widget::keyPressEvent(QKeyEvent *event){
    switch (event->key()) {
    case Qt::Key_Up:
        if(moveFlag != DIR_DOWN){moveFlag = DIR_UP;}
        break;
    case Qt::Key_Down:
        if(moveFlag != DIR_UP){moveFlag = DIR_DOWN;}
        break;
    case Qt::Key_Right:
        if(moveFlag != DIR_LEFT){moveFlag = DIR_RIGHT;}
        break;
    case Qt::Key_Left:
        if(moveFlag != DIR_RIGHT){moveFlag = DIR_LEFT;}
        break;
    case Qt::Key_Space:
        if(gameStart == false){
            gameStart = true;
            //启动定时器
            timer->start(time);
        }
        else{
            gameStart = false;
            //停止定时器
            timer->stop();
        }
        break;
    default:
        break;
    }
}


//实现超时函数
void Widget::timeout(){//实现逻辑要么都上移,要么底部加一个,顶部取消一个
    //判断有没有重合
    int count = 1;
    if(snake[0].intersects(rewardNode)){
        //相交就吃掉奖品
        count++;
        //吃掉添加新的奖品
        addNewReword();
    }

    while (count--) {
        //蛇的移动
        switch (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::addTop(){
    QPointF leftTop;
    QPointF rightBotom;

    //超出边界(顶端) 加到最下面
    if(snake[0].y()-nodeHeight < 0){
        //this->height窗口
        leftTop = QPoint(snake[0].x(),this->height()-nodeHeight);
        rightBotom = QPointF(snake[0].x()+nodeWidth,this->height());
    }else{
    //没有超出的情况
        //在蛇的上面(蛇和矩形)
        //左上角坐标     nodeHeight小方块矩形
        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()+nodeWidth,nodeHeight);
    }else{
        //没有超出的情况
        //在蛇的上面(蛇和矩形)
        //左上角坐标     nodeHeight小方块矩形
        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()-nodeWidth < 0){
        leftTop = QPointF(this->width()-nodeWidth,snake[0].y());
    }else{
        //没有超出的情况
        //在蛇的上面(蛇和矩形)
        //左上角坐标     nodeHeight小方块矩形
        leftTop = snake[0].topLeft()-QPointF(nodeWidth,0);
    }

    rightBotom = leftTop+QPointF(nodeWidth,nodeHeight);
    //右下角坐标
    //加一个小方块
    snake.insert(0,QRectF(leftTop,rightBotom));
}


//实现右面加一个函数
void Widget::addRight(){
    QPointF leftTop;
    QPointF rightBotom;

    //超出边界(顶端) 加到最下面
    if(snake[0].x()-nodeWidth*2 > this->width()){
        leftTop = QPointF(0,snake[0].y());
    }else{
        //没有超出的情况
        //在蛇的上面(蛇和矩形)
        //左上角坐标     nodeHeight小方块矩形
        leftTop = snake[0].topRight();
    }

    rightBotom = leftTop+QPointF(nodeWidth,nodeHeight);
    //右下角坐标
    //加一个小方块
    snake.insert(0,QRectF(leftTop,rightBotom));
}





//实现绘图
void Widget::paintEvent(QPaintEvent *event){
    //画家
    QPainter painter(this);
    //画笔
    QPen pen;
    //画刷
    QBrush brush;

    //背景图片
    QPixmap pix;
    pix.load("F:/QT/Snake/images/bk.png");
    //    QPixmap pixmap(":/images/bk.png");
    painter.drawPixmap(0,0,800,600,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.drawRect(rewardNode);//奖品
        painter.drawEllipse(rewardNode);

    //判断蛇头是否碰到蛇身
        if(checkContact()){
            QFont font("方块输出",30,QFont::ExtraLight,false);
            painter.setFont(font);
            painter.drawText(
                    (this->width()-300)/2,
                    (this->height()-30)/2,
                    QString("完成")
                );
            timer->stop();
        }

    //调用一下父类
    QWidget::paintEvent(event);
}


//实现减去一个方块
void Widget::deleteLast(){
    snake.removeLast();
}


//实现添加奖品函数
void Widget::addNewReword(){
    //奖品
    int width = this->width();
    int a = QRandomGenerator::global()->bounded(width/20*20);
    int height = this->height();
    int b = QRandomGenerator::global()->bounded(height/20*20);
    rewardNode = QRectF(
            //qrand()%(this->width()/20)*20,
            //qrand()%(this->height()/20)*20,
            //QT6用上面方法会报错:'qrand' was not declared in this scope; did you mean 'srand'?
            a,
            b,
            nodeWidth,
            nodeHeight
        );
}


//实现判断蛇头是否碰到蛇身函数
bool Widget::checkContact(){
    for(int i = 1; i < snake.length(); i++)
        for (int j = i+1; j < snake.length(); j++) {
                if(snake[i] == snake[j]){
                    return true;
                }
        }
    return false;
}

main.cpp

#include "widget.h"

#include <QApplication>
#include <QLocale>
#include <QTranslator>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QTranslator translator;
    const QStringList uiLanguages = QLocale::system().uiLanguages();
    for (const QString &locale : uiLanguages) {
        const QString baseName = "Snake_" + QLocale(locale).name();
        if (translator.load(":/i18n/" + baseName)) {
            a.installTranslator(&translator);
            break;
        }
    }
    Widget w;
    w.show();
    return a.exec();
}

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

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

相关文章

【TypeScript】声明文件

在 TypeScript 中&#xff0c;声明文件&#xff08;Declaration Files&#xff09;用于描述已有 JavaScript 代码库的类型信息&#xff0c;以便在 TypeScript 项目中使用这些代码库时获得类型支持。 当你在 TypeScript 项目中引用外部 JavaScript 模块或库时&#xff0c;可能会…

uniapp离线打包apk - Android Studio

uniapp 离线打包 基于uni-app的andiord 离线打包 开发工具及所需要的jar包​1.将下载的App离线SDK解压打开&#xff0c;找到HBuilder-Integrate-AS &#xff0c;在Android Studio打开2.打开HBuilder X&#xff0c;发行->原生app本地打包->生成本地打包app资源3.在“HBuil…

“解放 Arweave“优惠:4EVERLAND的无缝上传教程

为了进一步展示 Arweave 的能力&#xff0c;4EVERLAND 骄傲地推出了“解放 Arweave”活动。我们认识到 Arweave 在数据完整性、抗审查性以及长期保存方面的无与伦比的优势&#xff0c;因此我们与这个去中心化的存储巨头建立了强大的集成。 克服了过去与加密货币支付逻辑相关的…

72 # http 缓存策略

前面实现了一个 http-server&#xff0c;并且实现了 gzip 的压缩&#xff0c;下面通过前面几节学习的缓存知识来添加一下缓存。 大致就是先强制缓存 10s&#xff0c;然后采用协商&#xff08;对比&#xff09;缓存&#xff0c;大致图如下 在之前的 http-server 的代码基础上添…

[虚幻引擎 UE5] EditableText(可编辑文本) 限制只能输入数字并且设置最小值和最大值

本蓝图函数可以格式化 EditableText 控件输入的数据&#xff0c;让其只能输入一定范围内的整数。 蓝图函数 调用方法 下载蓝图&#xff08;5.2.1版本&#xff09;https://dt.cq.cn/archives/618

放苹果(巧用递归)--夏令营

题目 tips&#xff1a; 1.写递归要有递归边界条件&#xff0c;递归过程就是向边界不断靠近 这里注意&#xff1a;虽然题目给的m,n输入数据范围是>1的&#xff0c;但不代表边界就是这个&#xff1b; 首先&#xff0c;n0肯定是不存在的&#xff0c;所以n的边界肯定是1&#…

Spring Cloud Alibaba-实现服务调用的负载均衡

1. 什么是负载均衡 通俗的讲&#xff0c; 负载均衡就是将负载&#xff08;工作任务&#xff0c;访问请求&#xff09;进行分摊到多个操作单元&#xff08;服务器,组件&#xff09;上进行执行。 根据负载均衡发生位置的不同,一般分为服务端负载均衡和客户端负载均衡。 服务端负…

PyTorch学习笔记(十七)——完整的模型验证(测试,demo)套路

完整代码&#xff1a; import torch import torchvision from PIL import Image from torch import nnimage_path "../imgs/dog.png" image Image.open(image_path) print(image)# 因为png格式是四个通道&#xff0c;除了RGB三通道外&#xff0c;还有一个透明度通…

Android Lottie加载gson文件动画

一&#xff1a;Lottie的使用 在你工程的build.gradle文件里添加如下配置 implementation com.airbnb.android:lottie:3.4.0二&#xff1a;布局文件直接引入LottieAnimationView <com.airbnb.lottie.LottieAnimationViewandroid:id"id/lottie_view"android:layout…

原生JS实现拾色器功能

没事儿干&#xff0c;写一个拾色器&#xff0c;原生 JS 实现&#xff0c;先看效果图&#xff1a; 一、写页面 <div class"circle"></div>.circle {width: 200px;height: 200px;border: 1px #999 solid;margin: 200px 0 0 200px;border-radius: 50%;back…

【TI毫米波雷达笔记】CCS雷达工程调试(以IWR6843AOP为例)

【TI毫米波雷达笔记】CCS雷达工程调试&#xff08;以IWR6843AOP为例&#xff09; 先前我们讨论了如何建立工程并编译 包括DSS和MSS部分 也就是DSP部分和cortex-r4f部分 通过编译 可以生成一个.out文件 如图 同样的 也有xer4f格、xe674格式等等 这取决于编译的工程配置 但这…

JUC学习笔记(一)

1. JUC概述及回顾 1.1. JUC是什么&#xff1f; 在 Java 5.0 提供了 java.util.concurrent(简称JUC)包&#xff0c;在此包中增加了在并发编程中很常用的工具类。此包包括了几个小的、已标准化的可扩展框架&#xff0c;并提供一些功能实用的类&#xff0c;没有这些类&#xff0…

C#|如何调试进依赖动态库中

第一步&#xff1a;打开项目属性 第二步 打开debug的本地调试可用 第三步 把要调试的代码拖进主界面打断点就可以进断点了

用友T3 T6 服务无法启动 windows10 11等操作系统 T3服务没有开启

windows 10 11 等高版本操作系统故障。 于2023-08-23日大量爆发。。 导致原因&#xff0c;windows操作系统根证书颁发机构吊销或已到期。 正版软件请打11.2最新补丁即可解决。 如果是老版本需要修复证书才可以。

Spring复习:(55)ApplicationContext中BeanFactoryPostProcessor是怎么添加到容器的?

容器创建时会调用AbstractApplicationContext的refresh方法&#xff0c;其中会调用invokeBeanFactoryPostProcessor方法&#xff0c;如下图 invokeBeanFactoryPostProcessors代码如下&#xff1a; 其中调用的PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcess…

STM32F4X USART串口使用

STM32F4X USART串口使用 串口概念起始位波特率数据位停止位校验位串口间接线 STM32F4串口使用步骤GPIO引脚复用函数串口初始化函数串口例程 串口概念 串口是MCU与外部通信的重要通信接口&#xff0c;也是MCU在开发过程中的调试利器。串口通信有几个重要的参数&#xff0c;分别…

11、vue3

一、为什么要学 Vue3 1.1 为什么要学 Vue3 1.2 Vue3的优势 1.3 Vue2 选项式 API vs Vue3 组合式API Vue3 组合式API vs Vue2 选项式 API 二、create-vue搭建Vue3项目 2.1 认识 create-vue 2.2 使用create-vue创建项目 前提环境条件 已安装 16.0 或更高版本的 Node.js node -…

【论文阅读】自动驾驶安全的研究现状与挑战

文章目录 摘要1.引言1.1.自动驾驶安全1.2.攻击面1.3.内容和路线图 2.自动驾驶技术2.1.组成2.2.技术 3.传感器安全3.1.照相机3.2.GNSS&#xff08;全球导航系统&#xff09;/IMU&#xff08;惯性测量单元&#xff09;3.3.超声波传感器3.4.毫米波雷达3.5.激光雷达3.6.多传感器交叉…

容器化微服务:用Kubernetes实现弹性部署

随着云计算的迅猛发展&#xff0c;容器化和微服务架构成为了构建现代应用的重要方式。而在这个过程中&#xff0c;Kubernetes&#xff08;常简称为K8s&#xff09;作为一个开源的容器编排平台&#xff0c;正在引领着容器化微服务的部署和管理革命。本文将深入探讨容器化微服务的…

32、启用 HTTP 响应压缩和编程式配置Web应用

★ 启用HTTP压缩 就是前端页面如果改动的比较多&#xff0c;那么响应就会比较慢&#xff0c;可以通过设置HTTP响应压缩来提高响应&#xff0c;如果前端改动少&#xff0c;那么就不需要启动这个响应压缩。 目的&#xff1a;为了提高HTTP响应数据在网络上的传输效率。▲ 设置如…