QT-贪吃蛇小游戏

news2024/12/27 10:42:22

QT-贪吃蛇小游戏

  • 一、演示效果
  • 二、核心代码
  • 三、下载链接


一、演示效果

请添加图片描述

二、核心代码

#include "Food.h"
#include <QTime>
#include <time.h>
#include "Snake.h"


Food::Food(int foodSize):foodSize(foodSize)
{

    coordinate.x = -1;
    coordinate.y = -1;
    qsrand(time(NULL));
}

void Food::createFood(int map_row,int map_col,vector<Point> snakeCoords){

    int foodX = qrand()%map_col*foodSize;
    int foodY = qrand()%map_row*foodSize;
    // 防止將食物生成在上一個位置
    while(foodX == coordinate.x && foodY == coordinate.y){
        foodX =  qrand()%map_col*foodSize;
        foodY = qrand()%map_row*foodSize;
    }
    //防止食物生成在蛇身上
    while(1){
        bool flag = true;
        for(auto& e:snakeCoords){
            if(e.x == foodX && e.y == foodY){
                foodX =  qrand()%map_col*foodSize;
                foodY = qrand()%map_row*foodSize;
                flag = false;
                break;
            }
        }
        if(flag)break;
    }

    coordinate.x = foodX;
    coordinate.y = foodY;

}

//返回食物座標
Point Food::getCoor()const{
    return this->coordinate;
}

#include "MapScene.h"
#include <QDebug>
#include <QMessageBox>
#include <QPushButton>
#include <vector>
#include <QPainter>
#include "data.h"
#include <QJsonDocument>
#include <QJsonObject>
#include "User.h"
#include <QDateTime>
#include "NetworkManager.h"



MapScene::MapScene(QWidget *parent,int row,int col,Snake* snake,int speed) : BaseScene(parent),row(row),col(col),snake(snake),speed(speed)
{
   // srand((unsigned)time(NULL)); //亂數種

    //載入和設置CSS樣式表
    QFile cssFile;
    cssFile.setFileName("./css/mapScene.css");
    cssFile.open(QIODevice::ReadOnly);
    QString styleSheet = cssFile.readAll();
    cssFile.close();
    this->setStyleSheet(styleSheet);

    //對界面進行基本的設置(只需做一次)
    int mapWidth = col*snake->getSize(),mapHeight = row*snake->getSize();
    int controlBarHeight = 50;
    this->setFixedSize(mapWidth,mapHeight+controlBarHeight);
    this->setWindowTitle(u8"【SuperSnake】遊戲界面");

    //主要的初始化
    this->initControlBar(mapWidth,mapHeight,controlBarHeight); //初始化最底下的控制欄
    this->initMap();

    //介紹遊戲操作的對話框
    QMessageBox::information(this,u8"提示",u8"【操作說明】使用W、A、S、D改變蛇的運動方向(注:WASD對應上下左右)");


    connect(gameTimer,&QTimer::timeout,this,&MapScene::onGameRunning);


}

//更新排行榜( 注:不同速度有不同的記錄 )
/*
 * 排行榜.json的儲存格式:
 * {
 *   "speed1":{
 *          "userId": {
                "maxScore": XXX,
                "userName": "XXX"
                "date":"XXX"
            }
 *    },
 *
 *   "speed2":{},
 *   "speed3":{}
 *    //....
 * }
*/
bool MapScene::updateRankList(){

    /* 打開文件 */
    QFile rankListFile;
    rankListFile.setFileName(rankListPath);
    rankListFile.open(QIODevice::ReadOnly);
    QByteArray rankListData = rankListFile.readAll(); //讀取所有內容

    QJsonDocument jsonDoc = QJsonDocument::fromJson(rankListData); //將數據解析為Json格式
    QJsonObject jsonObj = jsonDoc.object(); //轉為QJsonObject類型

    /* 獲取各種信息 */
    QString userId = User::getCurrentUserId();
    QString userName = User::getCurrentUserName();
    QString speed = QString::number(this->speed);
    QDateTime current_date_time =QDateTime::currentDateTime();
    QString current_date =current_date_time.toString("yyyy-MM-dd");


    QJsonObject speedObj = jsonObj[speed].toObject();

    //當【指定速度的排行榜】不包含當前的userId時,代表第一次遊玩該速度,可直接記錄該速度的排行榜中
    if(!speedObj.contains(userId)){
        QJsonObject newRankRecord;
        newRankRecord.insert("userName",userName);
        newRankRecord.insert("maxScore",score);
        newRankRecord.insert("date",current_date);
        speedObj.insert(userId,newRankRecord);

    }else{
        int maxScore = speedObj[userId].toObject()["maxScore"].toInt();
        //當局分數<=最高分時,不作記錄,直接返回
        if(score<=maxScore){
            rankListFile.close();
            return false;
        }

        //更新最高分
        QJsonObject userIdObj = speedObj[userId].toObject();
        userIdObj["maxScore"] = score;
        userIdObj["date"] = current_date;
        speedObj[userId] = userIdObj;


    }
    rankListFile.close();
    rankListFile.open(QIODevice::WriteOnly);

    /* 更新排行榜內容 */
    jsonObj[speed] = speedObj;
    jsonDoc.setObject(jsonObj);
    rankListFile.write(jsonDoc.toJson());

    rankListFile.close();
    return true;

}

bool MapScene::updateRankList(QString url){
    QByteArray rankListData = NetworkManager::get(url);

    QJsonDocument jsonDoc = QJsonDocument::fromJson(rankListData); //將數據解析為Json格式
    QJsonObject jsonObj = jsonDoc.object(); //轉為QJsonObject類型

    /* 獲取各種信息 */
    QString userId = User::getCurrentUserId();
    QString userName = User::getCurrentUserName();
    QString speed = QString::number(this->speed);
    QDateTime current_date_time = QDateTime::currentDateTime();
    QString current_date =current_date_time.toString("yyyy-MM-dd");


    QJsonObject speedObj = jsonObj[speed].toObject();

    //當【指定速度的排行榜】不包含當前的userId時,代表第一次遊玩該速度,可直接記錄該速度的排行榜中
    if(!speedObj.contains(userId)){
        QJsonObject newRankRecord;
        newRankRecord.insert("userName",userName);
        newRankRecord.insert("maxScore",score);
        newRankRecord.insert("date",current_date);
        speedObj.insert(userId,newRankRecord);

    }else{
        int maxScore = speedObj[userId].toObject()["maxScore"].toInt();
        //當局分數<=最高分時,不作記錄,直接返回
        if(score<=maxScore){
            return false;
        }

        //更新最高分
        QJsonObject userIdObj = speedObj[userId].toObject();
        userIdObj["maxScore"] = score;
        userIdObj["date"] = current_date;
        speedObj[userId] = userIdObj;


    }


    /* 更新排行榜內容 */
    jsonObj[speed] = speedObj;
    jsonDoc.setObject(jsonObj);
    NetworkManager::put(url,jsonDoc);
    return true;
}

void MapScene::onGameRunning(){
    snake->move();
    moveFlag = true; //表示上一次的【方向指令】已執行完成,可以接收下一個【方向指令】

    /*取得蛇的各項信息*/
    int snakeSize = snake->getSize();
    std::vector<Point> snakeCoords = snake->getCoords();
    Point foodCoord = food->getCoor();
    //獲取食物座標
    int snakeNum = snake->getNum();


    //判斷蛇有無吃東西
    if(isSnakeEat(snakeCoords,foodCoord)){
        snake->addNum();
        food->createFood(this->row,this->col,snakeCoords);

        score+=100; //每食一個食物+100分
        scoreLabel->setText(QString(u8"分數:%1").arg(score));
        scoreLabel->adjustSize(); //防止分數顯示不完全

        snake->setSnakeColor(QColor(rand()%256,rand()%256,rand()%256)); //改變蛇的顏色
    }
    //判斷蛇是否死亡
    if(isSnakeDead(snakeCoords,snakeSize,snakeNum)){
        gameTimer->stop();
        QString server = User::getCurrentServer();

        NetworkManager nw;

        bool ret;
        if(server == "local"){
            ret = updateRankList();
        }
        else{
            nw.createLoadDialog();
            ret = updateRankList(webJsonUrl_RL);
        }

        QString resultStr = QString(u8"你的分數為:%1").arg(score);
        if(!ret){
            resultStr+="  >>排行榜沒有任何改變@@<<";
        }else{
            resultStr+="  >>排行榜已更新^.^<<";
        }
        /* 注:QMessageBox要放在initMap()的之後,因為它是模態對話框,會阻塞進程,從而導致一些bug */
        this->initMap();

        if(server == u8"web")nw.closeLoadDialog();

        QMessageBox::information(this,u8"遊戲結束",resultStr);

    }


    update(); //手動調用paintEvent
}

void MapScene::initControlBar(int mapWidth,int mapHeight,int controlBarHeight){
    //開始遊戲的按鈕
    QPushButton* startGameBtn = new QPushButton(u8"開始遊戲",this);
    startGameBtn->setFont(QFont(u8"Adobe 楷体 Std R",14));
    startGameBtn->adjustSize();
    startGameBtn->move(mapWidth*0.03,mapHeight+controlBarHeight/2-startGameBtn->height()/2);
    connect(startGameBtn,&QPushButton::clicked,[this](){
        gameTimer->start();

    });

    //暫停遊戲的按鈕
    QPushButton* pauseGameBtn = new QPushButton(u8"暫停遊戲",this);
    pauseGameBtn->setFont(QFont(u8"Adobe 楷体 Std R",14));
    pauseGameBtn->adjustSize();
    pauseGameBtn->move(mapWidth*0.05+startGameBtn->width()+10,mapHeight+controlBarHeight/2-pauseGameBtn->height()/2);
    connect(pauseGameBtn,&QPushButton::clicked,[this](){
        gameTimer->stop();

    });

    //返回的按鈕
    QPushButton* backBtn = new QPushButton(u8"返回",this);
    backBtn->setFont(QFont(u8"Adobe 楷体 Std R",14));
    backBtn->adjustSize();
    backBtn->move(mapWidth*0.95-backBtn->width(),mapHeight+controlBarHeight/2-backBtn->height()/2);
    connect(backBtn,&QPushButton::clicked,[this](){
        gameTimer->stop();
        this->close();
        //發送返回【設定界面】的信號
        emit backToSettingScene();


    });

    scoreLabel = new QLabel(u8"分數:0",this);
    scoreLabel->setFont(QFont(u8"Adobe 楷体 Std R",14));
    scoreLabel->adjustSize();
    scoreLabel->move(mapWidth*0.05+startGameBtn->width()+pauseGameBtn->width()+20,mapHeight+controlBarHeight/2-scoreLabel->height()/2);
}

// 畫蛇的函數
void MapScene::drawSnake(QPainter& painter,std::vector<Point>& snakeCoords,int snakeNum,int snakeSize){
    QColor snakeColor = snake->getSnakeColor();
    //設置畫家各項屬性
    painter.setPen(QPen(snakeColor));

    painter.setBrush(QBrush(snakeColor));

    //畫蛇
    for(int i = 0;i<snakeNum;i++){
        painter.drawRect(snakeCoords[i].x,snakeCoords[i].y,snakeSize,snakeSize);
    }
}

//畫食物的函數
void MapScene::drawFood(QPainter& painter,int snakeSize){
    //設置畫家各項屬性
    painter.setPen(QColor());
    //創建畫刷
    QBrush brush(QColor(255,255,0));;
    painter.setBrush(brush);

    Point foodCoor = food->getCoor();
    painter.drawEllipse(foodCoor.x,foodCoor.y,snakeSize,snakeSize);

}

//判斷蛇是否死亡
bool MapScene::isSnakeDead(std::vector<Point>& snakeCoords,int& snakeSize,int& snakeNum){
    //檢查蛇有無超出邊界
    if(snakeCoords[0].x<0 || snakeCoords[0].x>=this->col*snakeSize || snakeCoords[0].y<0 || snakeCoords[0].y>=this->row*snakeSize )return true;
    //檢查有沒有碰到自己
    for(int i = 1;i < snakeNum;i++){
        if(snakeCoords[0].x == snakeCoords[i].x && snakeCoords[0].y == snakeCoords[i].y )return true;
    }
    return false;
}

//判斷蛇有無吃東西
bool MapScene::isSnakeEat(std::vector<Point>& snakeCoords,Point& foodCoord){
        //檢查蛇頭有沒有吃到食物
        if(snakeCoords[0].x == foodCoord.x && snakeCoords[0].y == foodCoord.y)return true;
        return false;
}

// 繪圖事件
void MapScene::paintEvent(QPaintEvent* event){

    QPainter painter(this);
    int snakeSize = snake->getSize();
    std::vector<Point> snakeCoords = snake->getCoords();
    int snakeNum = snake->getNum();

    //分隔線:分開遊戲區域和控制區域
    painter.drawLine(0,row*snakeSize,col*snakeSize,row*snakeSize);


    drawFood(painter,snakeSize);
    drawSnake(painter,snakeCoords,snakeNum,snakeSize);

}

// 通過 wasd 改變蛇的方向
void MapScene::changeSnakeDir(QKeyEvent* event){
    // wasd->上下左右
    //通過wasd改變蛇的運動方向
    int snakeDir = snake->getDir();
    switch(event->key()){
        case Qt::Key_W:
            if(snakeDir!=DOWN){
                snake->setDir(UP);
                moveFlag = false;
            }
            break;
        case Qt::Key_A:
            if(snakeDir!=RIGHT){
                snake->setDir(LEFT);
                moveFlag = false;
            }
            break;
        case Qt::Key_S:
            if(snakeDir!=UP){
                snake->setDir(DOWN);
                moveFlag = false;
            }
            break;
        case Qt::Key_D:
            if(snakeDir!=LEFT){
                snake->setDir(RIGHT);
                moveFlag = false;
            }
            break;

    }
}

//鍵盤點擊事件
void MapScene::keyPressEvent(QKeyEvent* event){
    //當moveFlag為false時,代表上一次發出的【方向指令】還在使用中,故直接return,防止有bug
    if(!moveFlag)return;
    changeSnakeDir(event);

}

void MapScene::initMap(){

    //設置定時器
    if(!gameTimer)gameTimer = new QTimer(this);
    gameTimer->setInterval(100/speed);

    //初始化食物對象
    if(!food)food = new Food(snake->getSize());
    food->createFood(this->row,this->col,snake->getCoords());


    //初始化蛇
    snake->init();



    //重置分數
    score = 0;
    scoreLabel->setText(QString(u8"分數:%1").arg(score));
    scoreLabel->adjustSize();



}

MapScene::~MapScene(){
    //因為food沒有加入到【對象樹】中,所以要手動釋放
    if(food!=nullptr){
        delete food;
        food = nullptr;
    }


}

三、下载链接

https://download.csdn.net/download/u013083044/89656909

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

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

相关文章

多线程(4)——单例模式、阻塞队列、线程池、定时器

1. 多线程案例 1.1 单例模式 单例模式能保证某个类在程序中只存在唯一一份实例&#xff0c;不会创建出多个实例&#xff08;这一点在很多场景上都需要&#xff0c;比如 JDBC 中的 DataSource 实例就只需要一个 tip&#xff1a;设计模式就是编写代码过程中的 “软性约束”&am…

系统稳定性建设的深度剖析与未来展望

一、系统稳定性的重要意义 系统稳定性是系统正常运行的关键&#xff0c;其缺失会导致严重后果&#xff0c;如经济损失、用户流失等。 以在线学习平台为例&#xff0c;如果系统频繁出现卡顿、掉线等问题&#xff0c;影响用户的学习体验&#xff0c;导致用户流失&#xff0c;平…

【HTML】从0开始构建HTML页面

1、HTML文档基本格式 1.1、!DOCTYPE:文档类型声明 1.2、html:根标签 1.3、head:头部标签 1.4、body:主体标签 2、头部相关标签 2.1、< title> < title>标签用于定义HTML页面的标题&#xff0c;即给网页取一个名字&#xff0c;必须位于< head>标签之内。 …

Programmatically add website content to OpenAI with C#

题意&#xff1a;使用 C# 以编程方式将网站内容添加到 OpenAI。 问题背景&#xff1a; Our goal is to have a ChatGPT answer questions about our websites content. 我们的目标是让 ChatGPT 回答关于我们网站内容的问题。 We are trying to integrate something similar t…

设计模式笔记01(java版)

文章目录 设计模式概述学习设计模式的必要性设计模式分类创建型模式结构型模式行为型模式 UML类图概述类图的作用类图表示法类的表示方式类与类之间关系的表示方式1&#xff0c;单向关联2&#xff0c;双向关联3&#xff0c;自关联聚合关系组合关系依赖关系继承关系实现关系 软件…

【hot100篇-python刷题记录】【买卖股票的最佳时机】

摆烂几天,又来了。 R5-贪心篇(不像) 贪心的常规思路是找到贪心切入点,例如最经典的算法是安排最多活动问题,需要以结束时间排序,然后遍历不冲突,计算最大数即可(每次都选择最早结束的活动)。 贪心算法的使用需要满足贪心特征。即局部最优解等于全局最优解。 对于本…

Docker 修改容器端口映射(以 Portainer 为例)

文章目录 背景解决第1步:找到容器id第2步:查找docker根目录第3步:停止docker服务第4步:修改容器的hostconfig.json配置文件第5步:启动docker服务第6步:验证参考背景 项目中有个服务也使用了9000端口,而Portainer的默认端口也是9000。结果可想而知,端口冲突,肯定有一个…

stable diffusion inpainting(img2img+inpaint/inpaint-model)

https://zhuanlan.zhihu.com/p/681250295https://zhuanlan.zhihu.com/p/681250295AIGC专栏4——Stable Diffusion原理解析-inpaint修复图片为例_diffusion inpaint-CSDN博客文章浏览阅读1.7w次,点赞42次,收藏79次。Inpaint是Stable Diffusion中的常用方法,一起简单学习一下。…

CAS-ViT实战:使用CAS-ViT实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…

springer 投稿系统中返修注意点

初次提交 初次提交时&#xff0c; manuscript 提交的是 pdf 文件 返修后提交 在经过返修之后需要提交的是注意一下几点&#xff1a; 此时提交的Blined manuscript &#xff0c;虽然名字没变&#xff0c;但不能再提交pdf 文件&#xff0c; 而需要提交的是可编辑的源文件 .te…

Sketch-gen模型部署教程

一、介绍 SketchGen: 一种图像转草图工具&#xff0c;主要用于制作合成数据集或生成参考图。它能够帮助研究人员和开发者快速地从真实图像中提取出线稿轮廓&#xff0c;这对于计算机视觉任务如物体识别、场景理解等非常有用。通过使用这种工具&#xff0c;可以更方便地创建训练…

【科技赋能教育】电路仿真软件:解锁电路教学新篇章,让知识触手可及!

教育领域正经历着一场前所未有的变革。电路学&#xff0c;作为理工科学生必修的一门基础课程&#xff0c;其抽象性、复杂性和实验条件的高要求&#xff0c;曾让无数学生望而却步。然而&#xff0c;随着电路仿真软件的出现&#xff0c;这一切正悄然发生着变化&#xff0c;它不仅…

Java面试-基础

1. 面向对象 什么是面向对象 什么是面向对象&#xff1f; 对比面向过程&#xff0c;是两种不同的处理问题的角度 面向过程更注重事情的每一个步骤及顺序&#xff0c;面向对象更注重事情有哪些参与者 &#xff08;对象&#xff09;、及各自需要做什么 封装、继承、多态 2. …

yum 安装 MySQL 8.0【2024最新教程】

文章目录 第 1 步:添加 MySQL Yum Repository第 2 步:【可选】选择 MySQL 版本第 3 步:安装 MySQL Community Server第 4 步:启动 MySQL第 5 步:修改 root 密码第 6 步:【可选】修改配置文件默认配置修改数据目录和日志文件添加更多配置第 7 步:【可选】修改 root 可外部…

【应急响应】-linux日志被删除?

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 在 Linux 应急响应中&#xff0c;攻击者清理日志的行为给追踪和分析带来了巨大挑战。本文将介绍在日志被…

【STM32项目】在FreeRtos背景下的实战项目的实现过程(三)

个人主页~ 实战项目的实现过程&#xff08;一&#xff09;~ 实战项目的实现过程&#xff08;二&#xff09;~ 实战项目的实现过程 五、读例程1、初始化函数2、while函数3、头文件4、源文件 六、移植程序 五、读例程 将一些特定的模块调试好&#xff0c;就是那些使用别的软件…

在Windows Server上安装typecho博客程序(基于IIS)

Typecho Typecho是由type和echo两个词合成的&#xff0c;来自于开发团队的头脑风暴。Typecho基于PHP5开发&#xff0c;支持多种数据库&#xff0c;是一款内核强健﹑扩展方便﹑体验友好﹑运行流畅的轻量级开源博客程序。 网上大多在Windows下安装Typecho的教程都是基于Apache服…

铁电随机存取存储器(FeRAM):前景黯淡,难以突破

铁电随机存取存储器&#xff08;FeRAM&#xff09;是一种快速、非易失性存储器&#xff0c;但它面临着与Optane相似的困境——目前的发展停滞不前。 ### 快速非易失性存储器技术 存在多种快速、非易失性存储器技术&#xff0c;理论上可以填补NAND和NOR与DRAM之间的存储层级空…

ArgoCD 启用Web终端,实现命令行进容器【详细步骤】

文章目录 Terminal 简介启用 Terminal第 1 步:启用 UI第 2 步:设置 Role 或 ClusterRole第 3 步:授权 API扩展知识 - ArgoCD RBACArgoCD 内置用户内置角色匿名/已认证用户 PolicyRBAC 模型结构应用相关 Policy项目级别配置页面配置ArgoCD CLI 配置扩展argocd-cm 示例argocd-…

Vue循环遍历:v-for 指令

1、v-for 指令 在程序设计中&#xff0c;循环控制是变化最丰富的技术。Vue.js 提供了列表渲染的功能&#xff0c;可将数组或对象中的数据循环渲染到 DOM 中。在 Vue.js 中&#xff0c;列表渲染使用的是 v-for 指令&#xff0c;其效果类似于 JavaScript 中的遍历。语法格式如下…