Qt实战项目——贪吃蛇

news2024/11/16 10:22:12

一、项目介绍

本项目是一个使用Qt框架开发的经典贪吃蛇游戏,旨在通过简单易懂的游戏机制和精美的用户界面,为玩家提供娱乐和编程学习的机会。

二、主要功能

2.1 游戏界面

游戏主要是由三个界面构成,分别是游戏大厅、难度选择和游戏内界面,因此需要建立三个.cpp文件,分别对应的是gamehall.cpp、gameselect.cpp和gameroom.cpp。

2.1.1 gamehall.cpp

在游戏大厅界面,需要将图片设置到背景板上,并且创建一个“开始游戏”按钮

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

    // 设置窗口大小、图标、名字
    this->setFixedSize(1080,720);
    this->setWindowIcon(QIcon(":Resource/icon.png"));
    this->setWindowTitle("贪吃蛇大作战");

    // 设置开始按钮
    QFont font("华文行楷",18);
    QPushButton* pushButton_start = new QPushButton(this);
    pushButton_start->setText("开始游戏");
    pushButton_start->setFont(font);
    pushButton_start->move(520,400);
    pushButton_start->setGeometry(440,520,160,90);
    pushButton_start->setStyleSheet("QPushButton{border:0px;}");

    // 点击“开始游戏”按钮进入难度选择界面
    GameSelect* gameSelect = new GameSelect;
    connect(pushButton_start,&QPushButton::clicked,[=](){
       this->close();
       gameSelect->setGeometry(this->geometry());
       gameSelect->show();

       QSound::play(":Resource/clicked.wav");
    });
}

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

void GameHall::paintEvent(QPaintEvent *event)
{
    (void) *event;
    // 实例化画家
    QPainter painter(this);

    // 实例化绘图设备
    QPixmap pix(":Resource/game_hall.png");

    // 绘图
    painter.drawPixmap(0,0,this->width(),this->height(),pix);
}

2.1.2 gameselect.cpp

在游戏选择界面中,同样是需要设置背景,创建按钮等操作。

其中点击“简单模式”、“正常模式”、“困难模式”可以直接进入游戏房间内,而查阅“历史战绩”时会弹出一个新窗口显示战绩。

GameSelect::GameSelect(QWidget *parent) : QWidget(parent)
{
    // 设置“难度选择”界面的图标、名字
    this->setFixedSize(1080,720);
    this->setWindowIcon(QIcon(":Resource/icon.png"));
    this->setWindowTitle("难度选择");

    QFont font("华文行楷",24);
    GameRoom* gameRoom = new GameRoom;

    // 设置选择难度按钮和历史战绩按钮
    QPushButton* pushButton_easy = new QPushButton(this);
    pushButton_easy->move(460,160);
    pushButton_easy->setGeometry(460,160,150,80);
    pushButton_easy->setText("简单模式");
    pushButton_easy->setFont(font);
    pushButton_easy->setStyleSheet("QPushButton{border:0px;color:white}");

    QPushButton* pushButton_normal = new QPushButton(this);
    pushButton_normal->move(460,280);
    pushButton_normal->setGeometry(460,280,150,80);
    pushButton_normal->setText("正常模式");
    pushButton_normal->setFont(font);
    pushButton_normal->setStyleSheet("QPushButton{border:0px;color:white}");

    QPushButton* pushButton_hard = new QPushButton(this);
    pushButton_hard->move(460,400);
    pushButton_hard->setGeometry(460,400,150,80);
    pushButton_hard->setText("困难模式");
    pushButton_hard->setFont(font);
    pushButton_hard->setStyleSheet("QPushButton{border:0px;color:white}");

    QPushButton* pushButton_record = new QPushButton(this);
    pushButton_record->move(460,520);
    pushButton_record->setGeometry(460,520,150,80);
    pushButton_record->setText("历史战绩");
    pushButton_record->setFont(font);
    pushButton_record->setStyleSheet("QPushButton{border:0px;color:white}");

    // 点击不同困难模式按钮进入游戏房间
    connect(pushButton_easy,&QPushButton::clicked,[=](){
        this->close();
        gameRoom->setGeometry(this->geometry());
        gameRoom->show();
        QSound::play(":Resource/clicked.wav");
        gameRoom->setTimeout(300);
    });

    connect(pushButton_normal,&QPushButton::clicked,[=](){
        this->close();
        gameRoom->setGeometry(this->geometry());
        gameRoom->show();
        QSound::play(":Resource/clicked.wav");
        gameRoom->setTimeout(200);
    });

    connect(pushButton_hard,&QPushButton::clicked,[=](){
        this->close();
        gameRoom->setGeometry(this->geometry());
        gameRoom->show();
        QSound::play(":Resource/clicked.wav");
        gameRoom->setTimeout(100);
    });

    // 设置历史战绩窗口
    connect(pushButton_record,&QPushButton::clicked,[=](){
        QWidget* widget = new QWidget;
        widget->setWindowTitle("历史战绩");
        widget->setWindowIcon(QIcon(":Resource/icon.png"));
        widget->setFixedSize(500,300);
        QSound::play(":Resource/clicked.wav");

        QTextEdit* edit = new QTextEdit(widget);
        edit->setFont(font);
        edit->setFixedSize(500,300);

        QFile file("D:/bite/C-program/project/Snake/gamedata.txt");
        file.open(QIODevice::ReadOnly);

        QTextStream in(&file);
        int data = in.readLine().toInt();
        edit->append("历史得分为:");
        edit->append(QString::number(data));

        widget->show();
    });
}

void GameSelect::paintEvent(QPaintEvent *event)
{
    (void) *event;
    QPainter painter(this);
    QPixmap pix(":Resource/game_select.png");
    painter.drawPixmap(0,0,this->width(),this->height(),pix);
}

同时在这个界面中,我们需要创建一个“回退”按钮,点击可回退到游戏大厅界面

    // 设置回退按钮
    QPushButton* pushButton_back = new QPushButton(this);
    pushButton_back->move(1000,640);
    pushButton_back->setGeometry(1000,640,60,60);
    pushButton_back->setIcon(QIcon(":Resource/back.png"));

    // 点击回退按钮回到上一页
    connect(pushButton_back,&QPushButton::clicked,[=](){
        this->close();
        GameHall* gameHall = new GameHall;
        gameHall->show();
        QSound::play(":Resource/clicked.wav");
    });

2.1.3 gameroom.cpp

在游戏房间界面中,我们可以看到许多元素,其中不仅有“开始”、“暂停”、“退出”三个按钮,还有控制小蛇移动的方向键按钮,还有计分板等等元素。

我们首先要做的是设计背景以及创建各个按钮。

    // 设置游戏房间大小、图标、名字
    this->setFixedSize(1080,720);
    this->setWindowIcon(QIcon(":Resource/icon.png"));
    this->setWindowTitle("游戏房间");

    // 开始游戏、暂停游戏
    QFont font("楷体",20);

    QPushButton* pushButton_start = new QPushButton(this);
    pushButton_start->move(890,460);
    pushButton_start->setGeometry(890,460,100,60);
    pushButton_start->setText("开始");
    pushButton_start->setFont(font);
    connect(pushButton_start,&QPushButton::clicked,[=](){
        isGameStart = true;
        timer->start(moveTimeout);
        sound = new QSound(":Resource/Trepak.wav");
        sound->play();
        sound->setLoops(-1);
    });

    QPushButton* pushButton_stop = new QPushButton(this);
    pushButton_stop->move(890,540);
    pushButton_stop->setGeometry(890,540,100,60);
    pushButton_stop->setText("暂停");
    pushButton_stop->setFont(font);
    connect(pushButton_stop,&QPushButton::clicked,[=](){
        isGameStart = false;
        timer->stop();
        sound->stop();
    });

    // 设置方向键的位置、大小、图标和快捷键
    QPushButton* pushButton_up = new QPushButton(this);
    pushButton_up->move(900,220);
    pushButton_up->setGeometry(900,220,80,60);
    pushButton_up->setIcon(QIcon(":Resource/up1.png"));
    connect(pushButton_up,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::DOWN)
            moveDirect = SnakeDirect::UP;
    });
    pushButton_up->setShortcut(QKeySequence(Qt::Key_W));

    QPushButton* pushButton_down = new QPushButton(this);
    pushButton_down->move(900,340);
    pushButton_down->setGeometry(900,340,80,60);
    pushButton_down->setIcon(QIcon(":Resource/down1.png"));
    connect(pushButton_down,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::UP)
            moveDirect = SnakeDirect::DOWN;
    });
    pushButton_down->setShortcut(QKeySequence(Qt::Key_S));

    QPushButton* pushButton_left = new QPushButton(this);
    pushButton_left->move(820,280);
    pushButton_left->setGeometry(820,280,80,60);
    pushButton_left->setIcon(QIcon(":Resource/left1.png"));
    connect(pushButton_left,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::RIGHT)
            moveDirect = SnakeDirect::LEFT;
    });
    pushButton_left->setShortcut(QKeySequence(Qt::Key_A));

    QPushButton* pushButton_right = new QPushButton(this);
    pushButton_right->move(980,280);
    pushButton_right->setGeometry(980,280,80,60);
    pushButton_right->setIcon(QIcon(":Resource/right1.png"));
    connect(pushButton_right,&QPushButton::clicked,[=](){
        if(moveDirect != SnakeDirect::LEFT)
            moveDirect = SnakeDirect::RIGHT;
    });
    pushButton_right->setShortcut(QKeySequence(Qt::Key_D));

    // 设置退出按钮
    QPushButton* pushButton_exit = new QPushButton(this);
    pushButton_exit->move(890,620);
    pushButton_exit->setGeometry(890,620,100,60);
    pushButton_exit->setText("退出");
    pushButton_exit->setFont(font);

2.2 游戏规则

蛇移动时不能碰到自己的身体,否则游戏结束。每吃掉一个食物(食物会随机刷新),身体变长,分数增加。

    // 初始化贪吃蛇
    snakeList.push_back(QRectF(this->width() * 0.5,this->height() * 0.5,kSnakeNodeWight,kSnakeNodeHeight));

    moveUP();
    moveUP();

    creatFood();

    timer = new QTimer(this);
    connect(timer,&QTimer::timeout,[=](){
        int count = 1;
        if(snakeList.front().intersects(foodRect))
        {
            creatFood();
            count++;
            QSound::play(":Resource/eatfood.wav");
        }

        while(count--)
        {
            switch(moveDirect)
            {
            case SnakeDirect::UP:
                moveUP();
                break;
            case SnakeDirect::DOWN:
                moveDOWN();
                break;
            case SnakeDirect::LEFT:
                moveLEFT();
                break;
            case SnakeDirect::RIGHT:
                moveRIGHT();
                break;
            }
        }

        snakeList.pop_back();
        update();
    });

    // 绘制蛇
    if(moveDirect == SnakeDirect::UP)
    {
        pix.load(":Resource/up.png");
    }
    else if(moveDirect == SnakeDirect::DOWN)
    {
        pix.load(":Resource/down.png");
    }
    else if(moveDirect == SnakeDirect::LEFT)
    {
        pix.load(":Resource/left.png");
    }
    else
    {
        pix.load(":Resource/right.png");
    }

    // 绘制蛇头、身体和尾巴
    auto Head = snakeList.front();
    painter.drawPixmap(Head.x(),Head.y(),Head.width(),Head.height(),pix);

    pix.load(":Resource/Bd.png");
    for (int i = 0;i < snakeList.size() - 1;i++)
    {
        auto Body = snakeList.at(i);
        painter.drawPixmap(Body.x(),Body.y(),Body.width(),Body.height(),pix);
    };

    auto tail = snakeList.back();
    painter.drawPixmap(tail.x(),tail.y(),tail.width(),tail.height(),pix);

    // 绘制食物
    pix.load(":Resource/food.png");
    painter.drawPixmap(foodRect.x(),foodRect.y(),foodRect.width(),foodRect.height(),pix);

bool GameRoom::checkFail()
{
    for(int i = 0;i < snakeList.size();i++)
    {
        for(int j = i + 1;j < snakeList.size();j++)
        {
            if(snakeList.at(i) == snakeList.at(j))
            {
                return true;
            }
        }
    }
    return false;
}

void GameRoom::creatFood()
{
    foodRect = QRectF(qrand() % (800 / kSnakeNodeWight) * kSnakeNodeWight,
                      qrand() % (this->height() / kSnakeNodeHeight) * kSnakeNodeHeight,
                      kSnakeNodeWight,kSnakeNodeHeight);
}

2.3 控制方式

使用界面上的方向键(上、下、左、右)或者通过键盘上的快捷键(W、S、A、D)来控制蛇的移动方向,但不能直接反向移动。

void GameRoom::moveUP()
{
    QPointF leftTop;
    QPointF rightBottom;

    // 蛇头
    auto snakeNode = snakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    // 如果穿模
    if(headY < 20)
    {
        leftTop = QPointF(headX,this->height() - kSnakeNodeHeight);
    }
    else
    {
        leftTop = QPointF(headX,headY - kSnakeNodeHeight);
    }

    rightBottom = leftTop + QPointF(kSnakeNodeWight,kSnakeNodeHeight);

    snakeList.push_front(QRectF(leftTop,rightBottom));
}

void GameRoom::moveDOWN()
{
    QPointF leftTop;
    QPointF rightBottom;

    auto snakeNode = snakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    if(headY > this->height())
    {
        leftTop = QPointF(headX,0);
    }
    else
    {
        leftTop = snakeNode.bottomLeft();
    }

    rightBottom = leftTop + QPointF(kSnakeNodeWight,kSnakeNodeHeight);

    snakeList.push_front(QRectF(leftTop,rightBottom));
}

void GameRoom::moveLEFT()
{
    QPointF leftTop;
    QPointF rightBottom;

    auto snakeNode = snakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    if(headX < 0)
    {
        leftTop = QPointF(800 - kSnakeNodeWight,headY);
    }
    else
    {
        leftTop = QPointF(headX - kSnakeNodeWight,headY);
    }

    rightBottom = leftTop + QPointF(kSnakeNodeWight,kSnakeNodeHeight);
    snakeList.push_front(QRectF(leftTop,rightBottom));
}

void GameRoom::moveRIGHT()
{
    QPointF leftTop;
    QPointF rightBottom;

    auto snakeNode = snakeList.front();
    int headX = snakeNode.x();
    int headY = snakeNode.y();

    if(headX > 760)
    {
        leftTop = QPointF(0,headY);
    }
    else
    {
        leftTop = snakeNode.topRight();
    }

    rightBottom = leftTop + QPointF(kSnakeNodeWight,kSnakeNodeHeight);
    snakeList.push_front(QRectF(leftTop,rightBottom));
}

三、结语

这个项目比较简单,通过这个项目,不仅可以学习到Qt的GUI开发和事件处理技术,还能熟悉C++编程及基本的游戏开发概念。关于Qt中的一些基础知识,我也会在后续逐步更新。

好了,源码我会放在下面,大家有兴趣的可以看一看,欢迎大家一键三连!!!

四、源码

https://gitee.com/hu-jiahao143/project/commit/f83b287c800dd105f8358d7ffb244202ebf015c9

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

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

相关文章

three.js 第六节 - 纹理以及贴图【.hdr文件(hdr贴图)】- 色彩空间

素材 这是素材 更多素材、案例、项目 好几个G一共&#xff0c;加我q178373168&#xff0c;60大洋拿走 源码 源码 // ts-nocheck // 引入three.js import * as THREE from three // 导入轨道控制器 import { OrbitControls } from three/examples/jsm/controls/OrbitControls…

渗透测试工程师常见面试33题

问1&#xff1a;关于sql注入&#xff0c;都分为那些&#xff1f; 答1&#xff1a;主要分为两个大类&#xff0c;有回显和无回显。其中无回显的称为盲注&#xff0c;包括时间盲注、DNSlog注入也算一种&#xff0c;布尔盲注&#xff1b;有回显的包括联合注入、报错注入、宽字节注…

存储故障导致的Oracle 19c 数据库集群无法启动案例分析

背景 在某周末11点多&#xff0c;突然收到批量数据库宕机告警信息&#xff0c;同时收到多个微信群数据库无法连接、访问报错的反馈&#xff0c;顿时感觉这是出大事了。 故障分析 登录环境后&#xff0c;查看数据库alert日志&#xff0c;日志中出现数据库不可用信息&#xff…

python-登录界面-demo

文章目录 前言python-登录界面-demo 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都不会太差&#xff0c;实在白嫖的话&#xff0c;那欢迎常来啊!!! python-…

免费分享:中国1:250万地质图(附下载方法)

中国1&#xff1a;250万地质图反映了中国区域地质构造面貌和基本特征&#xff0c;表示了中国区域地质的特色&#xff0c;地质编图的标准化程度较高&#xff0c;地质图的编制过程中充分采用了信息技术&#xff0c;反映了地质调查与科研的若干新进展与认识。 数据简介 本数据为空…

Firefox 编译指南2024 Windows10篇- 源码获取(二)

1. 引言 在成功准备了编译环境之后&#xff0c;下一步就是获取Firefox的源码。源码是编译任何软件的基础&#xff0c;对于开源项目如Firefox尤其重要。通过获取并理解源码&#xff0c;开发者不仅能够编译出自定义版本的Firefox&#xff0c;还能对其进行修改和优化&#xff0c;…

坚持100天学习打卡Day1

1.大小端 2.引用的本质 及 深拷贝与浅拷贝 3.初始化列表方式 4.类对象作为类成员 5.静态成员 static

中国杀出全球首个烹饪大模型

什么&#xff1f;烹饪也有大模型&#xff1f;&#xff01; 没有听错&#xff0c;这就是国产厨电龙头老板电器最新发布——“食神”大模型。 数十亿级行业数据&#xff0c;数千万级知识图谱加持&#xff0c;据称还是全球首个。 它能为每个人提供个性化量身定制的解决方案&…

Pixel手机中文网-全球最大的华人Pixel手机论坛

Pixel手机中文网&#xff0c;使用Pixel手机华人的聚集地&#xff0c;快来加入这个大家庭分享和创作吧 &#x1f603; googlepixel.cn

信息系统安全风险评估,让风险看得见!

信息安全风险管理依据等级保护的思想和适度安全的原则&#xff0c;平衡成本与效益&#xff0c;合理部署和利用信息安全的信任体系、监控体系和应急处理等重要的基础设施&#xff0c;确定合适的安全措施&#xff0c;从而确保机构具有完成其使命的信息安全保障能力。亿林依据《国…

武汉星起航:国家政策助力亚马逊电商平台蓬勃发展

在全球化浪潮和数字经济快速发展的双重推动下&#xff0c;跨境电商已成为推动国际贸易增长的新引擎。武汉星起航持续关注到&#xff0c;亚马逊电商平台作为其中的佼佼者&#xff0c;在享受国家政策红利的同时&#xff0c;也展现出了强大的发展活力和市场潜力。 近年来&#xf…

JavaScript的学习之文档的加载

目录 一、onload的运用 浏览器在加载一个页面时&#xff0c;是按照自上而下的顺序加载的&#xff0c;读取到一行就执行一行&#xff0c; 如果script标签写到页面的上方&#xff0c;在代码执行时&#xff0c;页面还没有加载&#xff0c;所以要将JS代码写道页面下面 一、onload的…

代码随想录第35天|动态规划

理论基础 动态规划是由前一个状态推导出来的, 而贪心是局部直接选取最优 五部曲: 确定dp数组&#xff08;dp table&#xff09;以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 debug过程 : dp数组打印查看 509. 斐波那契数 参考 //动态规划的方法 …

科普:什么是 BC-404 ?全方位解读最新通缩型 NFT 标准

区块链技术飞速发展的今天&#xff0c;创新从未停歇。继 ERC-404 标准问世后&#xff0c;一个名为 BC-404 的新标准应运而生&#xff0c;为 NFT 市场带来了全新的可能性。BC-404&#xff08;Bonding Curve 404&#xff09;—基于对 ERC-404 的改进&#xff0c;加密货币中第一个…

【大模型】大模型微调方法总结(二)

1.Adapter Tuning 1.背景 2019年谷歌的研究人员首次在论文《Parameter-Efficient Transfer Learning for NLP》提出针对 BERT 的 PEFT微调方式&#xff0c;拉开了 PEFT 研究的序幕。他们指出&#xff0c;在面对特定的下游任务时&#xff0c;如果进行 Full-Fintuning&#xff0…

基于Simulink的行波故障测距

基于MATLAB/SIMULINK的输电线路故障行波仿真方法 为了更深入地学习和研究输电线路故障行波&#xff0c;通过matlab将复杂的电力系统简化为一个等效电路模型&#xff0c;使得故障的仿真和行波的提取更加直观和方便。首先&#xff0c;我们根据电力系统的实际情况&#xff0c;建立…

政务网站(.gov)应选择什么样的SSL证书

政府网站作为公共服务的重要平台&#xff0c;承载着发布政策信息、提供在线服务、促进政民互动等功能&#xff0c;其数据安全性和网站可信度尤为重要。因此&#xff0c;选择合适的SSL证书对于政府网站而言&#xff0c;不仅是遵循网络安全法规的需要&#xff0c;也是提升公众信任…

【vscode使用】一文帮你解决vscode打开文件不覆盖问题

【vscode使用】一文帮你解决vscode打开文件不覆盖问题 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容文档关注&#xf…

Go 延迟调用 defer

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Go语言之函数和方法

个人网站&#xff1a; http://hardyfish.top/ 免费书籍分享&#xff1a; 资料链接&#xff1a;https://url81.ctfile.com/d/57345181-61545511-81795b?p3899 访问密码&#xff1a;3899 免费专栏分享&#xff1a; 资料链接&#xff1a;https://url81.ctfile.com/d/57345181-6…