贪吃蛇(使用QT)

贪吃蛇小游戏

  • 一.项目介绍
  • **[贪吃蛇项目地址](https://gitee.com/strandingzy/QT/tree/zyy/snake)**
    • 界面一:游戏大厅
    • 界面二:关卡选择界面
    • 界面三:游戏界面
  • 二.项目实现
    • 2.1 游戏大厅
    • 2.2关卡选择界面
    • 2.3 游戏房间
      • 2.3.1 封装贪吃蛇数据结构
      • 2.3.2 初始化游戏房间界面
      • 2.3.3 蛇的移动
      • 2.3.4 初始化贪吃蛇生体和食物节点
      • 2.3.5 实现定时器的超时槽函数
      • 2.3.6 实现各个方向的移动
      • 2.3.7 重写绘图事件函数进行渲染
      • 2.3.8 检查是否自己会撞到自己
      • 2.3.9 设置游戏开始和游戏暂停按钮
      • 2.3.10 设置游戏开始和游戏暂停按钮
      • 2.3.11 获取历史战绩
  • 代码的总体文件
  • gamehall.h
  • gameroom.h
  • gameselect.h
  • gamehall.cpp
  • gameroom.cpp
  • gameselect.cpp

一.项目介绍

贪吃蛇游戏是⼀款休闲益智类游戏。它通过控制蛇头方向吃食物,从而使得蛇变得越来越长。在本游戏中设置了上下左右四个⽅向键来控制蛇的移动方向。⻝物的产⽣是随机⽣成的,当蛇每吃⼀次⻝物就会增加⼀节身体,同时游戏积分也会相应的加⼀。在本游戏的设计中,蛇的⾝体会越吃越长,身体越长对应的难度就越⼤,因为⼀旦蛇头和⾝体相交游戏就会结束。

贪吃蛇项目地址

界面一:游戏大厅

在这里插入图片描述

当用户点击 “开始游戏” 按钮之后,就会进⼊到关卡选择界面。

界面二:关卡选择界面

在这里插入图片描述
在关卡选择界面上设置了三个游戏模式按钮,分别是:简单模式、正常模式、困难模式;⼀个 “历史战绩” 按钮;⼀个返回游戏大厅界⾯的按钮。

界面三:游戏界面

在这里插入图片描述
在游戏界⾯,如果想要开始游戏,⾸先点击 “开始” 按钮,此时蛇就会移动并且还有背景⾳效。如果想要暂停游戏,那么点击 “暂停” 按钮即可。当我们在游戏时,可以通过右边控制区域的上下左右⽅向键来控制蛇的移动。当蛇每吃⼀次⻝物时,伴随有吃⻝物的⾳效,蛇⾝会增加⼀节⻓度,并且分数
积分也会相应的加⼀。最后在控制区域的右下⻆布局了⼀个 “退出” 按钮,当点击 “退出” 按钮时,就会有⼀个弹窗提⽰,如下在这里插入图片描述
如果我们点击 “Ok” ,则会退出到游戏关卡选择界⾯,此时我们可以重新选择游戏模式。如果点击“Cancel” ,则不会退出游戏,我们还是处在当前游戏房间界⾯。

二.项目实现

2.1 游戏大厅

游戏大厅界面比较简单, 只有⼀个背景图和⼀个按钮

  • 背景图片的渲染,通过QT的绘图事件完成
void GameHall::paintEvent(QPaintEvent *event)
{//实例化画家对象QPainter painter(this);//实例化绘图设备QPixmap pix(":res/game_hall.png");//绘画painter.drawPixmap(0,0,this->width(),this->height(),pix);}
  • 按钮的响应通过QT的信号槽机制完成
GameHall::GameHall(QWidget *parent): QWidget(parent), ui(new Ui::GameHall)
{ui->setupUi(this);this->setFixedSize(1500,1000);//设置窗口大小this->setWindowTitle(QString("贪吃蛇游戏"));this->setWindowIcon(QIcon(":res/ico.png"));//设置窗口图标//QFont font("华文行楷",20);QPushButton * strbutton = new QPushButton(this);strbutton->setText("开始游戏");strbutton->setFont(font);strbutton->move(650,700);strbutton->setShortcut(QKeySequence(Qt::Key_Space));strbutton->setStyleSheet("QPushButton{border:0px}");//给按钮设置样式  去除边框GameSelect *  gameSelect = new GameSelect;connect(strbutton,&QPushButton::clicked,[=](){this->close();//当前窗口关闭gameSelect->setGeometry(this->geometry());//设置第二个窗口于当前窗口一致gameSelect->show();//新的窗口打开QSound::play(":res/clicked.wav");});
}
  • 这里的Qfound类,使用需要在.pro 的文件中加上 QT += core gui multimedia
  • 这里使用strbutton->setShortcut(QKeySequence(Qt::Key_Space));给按钮添加了快捷键
  • 使用strbutton->setStyleSheet("QPushButton{border:0px}");去除按钮边框样式
  • 通过槽函数跳转到新的窗口 ,在槽函数中使用了Lambda 表达式
    通过QSound创建音频对象,使点击按钮有声音

在这里插入图片描述

2.2关卡选择界面

关卡选择界⾯包含⼀个背景图和五个按钮,背景图的绘制和游戏⼤厅背景图绘制⼀样,同样使用的是Qt中的绘图事件,下面依次介绍。

  • 背景图片的渲染,通过QT的绘图事件完成
void GameHall::paintEvent(QPaintEvent *event)
{//实例化画家对象QPainter painter(this);//实例化绘图设备QPixmap pix(":res/game_hall.png");//绘画painter.drawPixmap(0,0,this->width(),this->height(),pix);}
  • 按钮的响应通过QT的信号和槽机制完成
#include "gameselect.h"
#include"gameroom.h"
#include<QIcon>
#include<QPushButton>
#include"gamehall.h"
#include<QSound>
#include<QPainter>GameSelect::GameSelect(QWidget *parent) : QWidget(parent)
{this->setFixedSize(1500,1000);//设置窗口大小this->setWindowIcon(QIcon(":res/ico.png"));this->setWindowTitle("游戏关卡选择");QPushButton* backBtn = new QPushButton(this);//返回按钮backBtn->move(1400,900);backBtn->setIcon(QIcon(":res/back.png"));backBtn->setShortcut(QKeySequence(Qt::Key_Escape));connect(backBtn,&QPushButton::clicked,[=](){this->close();GameHall * gameHall = new GameHall;gameHall->show();QSound::play(":res/clicked.wav");});QFont font("华文行楷",20);gameroom*gameRoom = new gameroom;QPushButton* simpleBtn = new QPushButton(this);simpleBtn->move(650,300);simpleBtn->setFont(font);simpleBtn->setText("简单模式");connect(simpleBtn,&QPushButton::clicked,[=](){this->close();gameRoom->setGeometry(this->geometry());gameRoom->show();});QPushButton*normalBtn = new QPushButton(this);normalBtn->move(650,400);normalBtn->setFont(font);normalBtn->setText("正常模式");connect(normalBtn,&QPushButton::clicked,[=](){this->close();gameRoom->setGeometry(this->geometry());gameRoom->show();});QPushButton*hardBtn = new QPushButton(this);hardBtn->move(650,500);hardBtn->setFont(font);hardBtn->setText("困难模式");connect(hardBtn,&QPushButton::clicked,[=]{this->close();gameRoom->setGeometry(this->geometry());gameRoom->show();});QPushButton*hisBtn = new QPushButton(this);hisBtn->move(650,600);hisBtn->setFont(font);hisBtn->setText("历史战绩");
}void GameSelect::paintEvent(QPaintEvent *event)
{QPainter painter(this);//实例化画家对象QPixmap pix(":res/game_select.png");painter.drawPixmap(0,0,this->width(),this->height(),pix);
}

connect(hardBtn,&QPushButton::clicked,[=]{ this->close(); gameRoom->setGeometry(this->geometry()); gameRoom->show(); });
通过关联信号槽,使得可以选择游戏模式,首先把当前窗口关闭,然后显示新的窗口,在设置与当前窗口的大小一样

2.3 游戏房间

游戏房间界面包含下面几个部分:

  • 背景的绘制
  • 蛇的绘制,蛇的移动,判断蛇是否会撞到自己
  • 积分的累加和绘制

怎么让蛇动起来?

  • 我们可以⽤⼀个链表表示贪吃蛇,⼀个小方块表示蛇的⼀个节点, 我们设置蛇的默认⻓度为3;
  • 向上移动的逻辑就是在蛇的上方加⼊⼀个小方块, 然后把最后⼀个小⽅块删除即可
  • 需要用到定时器Qtimer 每100 - 200ms 重新渲染

怎么判断蛇有没有吃到食物?

  • 判断蛇头和食物的坐标是否相交,

怎么控制蛇的移动?

  • 借助QT的实践机制实现, 重写keyPressEvent即可, 在函数中监控想要的键盘事件即可
  • 通过绘制四个按钮,使⽤信号和槽的机制控制蛇的上、下、左、右移动方向

2.3.1 封装贪吃蛇数据结构

#ifndef GAMEROOM_H
#define GAMEROOM_H#include <QWidget>
#include<QSound>enum class SnakeDirect
{UP=0,DOWN,LEFT,RIGHT
};class gameroom : public QWidget
{Q_OBJECT
public:explicit gameroom(QWidget *parent = nullptr);//重写绘图事件void paintEvent(QPaintEvent *event);void moveUp();void moveDown();void moveLeft();void moveRight();bool checkFail();void createNewFood();void  setTimeout(int timeout){moveTime=timeout;}private:const int KSnakeNodeWidth = 20;const int KSnakeNodeHeight = 20;const int KDefaultTimeout = 200;//移动速度QList<QRectF> snakeList;//表示链表QRectF foodRect;//食物SnakeDirect  moveDirect = SnakeDirect::UP;//默认移动方向QTimer *timer;//定时器bool  isGameStart = false;//游戏的开始QSound *sound;int moveTime = KDefaultTimeout;
};#endif // GAMEROOM_H

2.3.2 初始化游戏房间界面

设置窗口大小、标题、图标等

gameroom::gameroom(QWidget *parent) : QWidget(parent)
{this->setFixedSize(1500,1000);this->setWindowIcon(QIcon(":res/ico.png"));this->setWindowTitle("游戏房间");
}

2.3.3 蛇的移动

蛇的移动方向为:上、下、左、右。通过在游戏房间中布局四个按钮来控制蛇的移动方向。

· 注意: 这里贪吃蛇不允许直接掉头, 比如当前是向上的, 不能直接修改为向下。

 QPushButton*up= new QPushButton(this);QPushButton*down= new QPushButton(this);QPushButton*left= new QPushButton(this);QPushButton*right= new QPushButton(this);up->move(1320,750);down->move(1320,900);left->move(1220,825);right->move(1420,825);up->setText("↑");
//    up->setShortcut(QKeySequence(Qt::Key_W));down->setText("↓");
//    down->setShortcut(QKeySequence(Qt::Key_S));left->setText("←");
//    left->setShortcut(QKeySequence(Qt::Key_A));right->setText("→");
//    left->setShortcut(QKeySequence(Qt::Key_D));up->setStyleSheet("QPushButton{border:0px}");down->setStyleSheet("QPushButton{border:0px}");left->setStyleSheet("QPushButton{border:0px}");right->setStyleSheet("QPushButton{border:0px}");QFont ft("宋体",36);up->setFont(ft);down->setFont(ft);right->setFont(ft);left->setFont(ft);connect(up,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::DOWN){moveDirect = SnakeDirect::UP;}});connect(down,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::UP){moveDirect = SnakeDirect::DOWN;}});connect(left,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::LEFT){moveDirect = SnakeDirect::RIGHT;}});connect(right,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::RIGHT){moveDirect = SnakeDirect::LEFT;}});

2.3.4 初始化贪吃蛇生体和食物节点

GameRoom::GameRoom(QWidget *parent) :QWidget(parent),ui(new Ui::Widget){ snakeList.push_back(QRectF(this->width()*0.5,this->height()*0.5,KSnakeNodeWidth,KSnakeNodeHeight));moveUp();moveUp();moveUp();//创建食物createNewFood();}

moveUp() 的功能是将蛇向上移动⼀次, 即在上⽅新增⼀个节点, 但不删除尾部节点。
createNewFood() ⽅法的功能是随机创建⼀个⻝物节点:

void gameroom::createNewFood()
{foodRect = QRectF(qrand()%(1500/KSnakeNodeWidth)*KSnakeNodeWidth,qrand()%(this->height()/KSnakeNodeHeight)*KSnakeNodeHeight,KSnakeNodeWidth,KSnakeNodeHeight);
}

2.3.5 实现定时器的超时槽函数

定时器的是为了实现每隔⼀段时间能处理移动的逻辑并且更新绘图事件。

  • ⾸先, 需要判断蛇头和⻝物节点坐标是否相交
    • 如果相交, 需要创建新的⻝物节点, 并且需要更新蛇的⻓度, 所以 cnt 需要 +1 ;
    • 如果不相交, 那么直接处理蛇的移动即可。
  • 根据蛇移动方向 moveDirect 来处理蛇的移动, 处理方法是在前方加⼀个, 并且删除后方节点;
  • 重新触发绘图事件, 更新渲染
timer = new QTimer(this);connect(timer,&QTimer::timeout,[=]{int cnt =1 ;if(snakeList.front().intersects(foodRect)){//intersects判断会不会相交createNewFood();cnt++;QSound::play(":res/eatfood.wav");}while (cnt--) {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();});

2.3.6 实现各个方向的移动

各个方向的移动主要在于更新矩形节点的坐标, 要注意的是⼀定要处理边界的情况, 当边界不够存储⼀个新的节点, 我们需要处理穿墙逻辑。

  • 实现向上移动
void gameroom::moveUp()
{QPointF leftTop;//左上角坐标QPointF rightBottom;//右下角坐标auto snakeNode = snakeList.front();//头int headx = snakeNode.x();int heady = snakeNode.y();if(heady < 0){//穿墙了leftTop = QPointF(headx,this->height()-KSnakeNodeHeight);}else {leftTop = QPointF(headx,heady- KSnakeNodeHeight);}rightBottom = leftTop + QPointF(KSnakeNodeWidth,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(KSnakeNodeWidth,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(1500 - KSnakeNodeWidth,heady);}else {leftTop = QPointF(headx - KSnakeNodeWidth,heady);}rightBottom = leftTop + QPointF(KSnakeNodeWidth,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(heady + KSnakeNodeWidth> 1200){leftTop = QPointF(0,heady);}else {leftTop = snakeNode.bottomRight();}rightBottom = leftTop + QPointF(KSnakeNodeWidth,KSnakeNodeHeight);snakeList.push_front(QRectF(leftTop,rightBottom));
}

2.3.7 重写绘图事件函数进行渲染

重写基类的 paintEvent() 方法进行渲染:

  • 渲染背景图
  • 渲染蛇头
  • 渲染蛇身体
  • 渲染蛇尾巴
  • 渲染右边游戏控制区域
  • 渲染⻝物节点
  • 渲染当前分数
  • 游戏结束渲染 game over

void gameroom::paintEvent(QPaintEvent *event)
{QPainter painter(this);//实例画家对象QPixmap pix;pix.load(":res/game_room.png");painter.drawPixmap(0,0,1200,1400,pix);pix.load(":res/bg1.png");painter.drawPixmap(1200,0,300,1100,pix);//绘制蛇//头if(moveDirect == SnakeDirect::UP){pix.load(":res/up.png");}else if (moveDirect == SnakeDirect::DOWN) {pix.load(":res/down.png");}else if (moveDirect == SnakeDirect::LEFT) {pix.load(":res/left.png");}else if(moveDirect == SnakeDirect::RIGHT){pix.load(":res/right.png");}auto snakHead =  snakeList.front();painter.drawPixmap(snakHead.x(),snakHead.y(),snakHead.width(),snakHead.height(),pix);//身体pix.load(":res/Bd.png");for (int  i= 1;i < snakeList.size()-1;i++) {auto node = snakeList.at(i);   //at 获取每一个节点painter.drawPixmap(node.x(),node.y(),node.width(),node.height(),pix);}//尾巴auto snakeTail = snakeList.back();painter.drawPixmap(snakeTail.x(),snakeTail.y(),snakeTail.width(),snakeTail.height(),pix);//食物pix.load(":res/food.png");painter.drawPixmap(foodRect.x(),foodRect.y(),foodRect.width(),foodRect.height(),pix);//分数pix.load(":res/sorce_bg.png");painter.drawPixmap(this->width()*0.85,this->height()*0.1,90,40,pix);QPen pen;pen.setColor(Qt::black);QFont font("方正舒体",22);painter.setFont(font);painter.setPen(pen);painter.drawText(this->width()*0.9,this->height()*0.06,QString("%1").arg(snakeList.size()));//往文件中写分数int c = snakeList.size();QFile file("C:/Users/zyy/Desktop/1.txt");if(file.open(QIODevice::WriteOnly| QIODevice::Text)){QTextStream out(&file);int num = c;out<<num ;file.close();}//绘制有戏失败的效果if(checkFail()){pen.setColor(Qt::red);painter.setPen(pen);QFont font("方正舒体",22);painter.setFont(font);painter.drawText(this->width()*0.45,this->height()*0.5,QString("GAME OVER!"));timer->stop();QSound::play(":res/gameover.wav");sound->stop();}
}

2.3.8 检查是否自己会撞到自己

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

2.3.9 设置游戏开始和游戏暂停按钮

     QPushButton * strBtn = new QPushButton(this);QPushButton * stopBtn = new QPushButton(this);strBtn->move(1270,100);stopBtn->move(1270,170);strBtn->setText("开始");strBtn->setShortcut(QKeySequence(Qt::Key_C+Qt::CTRL));stopBtn->setText("暂停");stopBtn->setShortcut(QKeySequence(Qt::Key_V+Qt::CTRL));QFont font("楷体",20);strBtn->setFont(font);stopBtn->setFont(font);connect(strBtn,&QPushButton::clicked,[=]{sound = new QSound(":res/Trepak.wav");sound->play();sound->setLoops(-1);//一直循环播放isGameStart = true;timer->start(moveTime);});connect(stopBtn,&QPushButton::clicked,[=]{isGameStart = false;timer->stop();sound->stop();});

2.3.10 设置游戏开始和游戏暂停按钮

当我们点击退出游戏按钮时,当前游戏房间窗⼝不会⽴即退出,而是会弹窗提示,提示我们是否要退出游戏,效果如下图示:
在这里插入图片描述
这个弹窗提示我们是通过 Qt 中的消息盒子来实现的,具体实现过程如下:

 //退出游戏QPushButton*ExitBtn = new QPushButton(this);ExitBtn->setText("退出游戏");ExitBtn->move(1240,400);ExitBtn->setFont(font);QMessageBox *msg = new QMessageBox(this);QPushButton *ok = new QPushButton("ok");QPushButton *canel = new QPushButton("canel");msg->addButton(ok,QMessageBox::AcceptRole);msg->addButton(canel,QMessageBox::RejectRole);msg->setWindowTitle("退出游戏");msg->setText("确认退出游戏吗");connect(ExitBtn,&QPushButton::clicked,[=](){msg->show();msg->exec();//时间轮询QSound::play("res/clicked.wav");GameSelect *select = new GameSelect;if(msg->clickedButton()==ok){this->close();select->show();}else {msg->close();}});
}

2.3.11 获取历史战绩

对于历史战绩的获取我们是通过 Qt 中的读写文件操作来实现的。具体实现过程如下:

  • 写文件: 向文件中写入蛇的长度
    int c = snakeList.size();QFile file("C:/Users/zyy/Desktop/1.txt");if(file.open(QIODevice::WriteOnly| QIODevice::Text)){QTextStream out(&file);int num = c;out<<num ;file.close();}
  • 读文件:读取写入文件中蛇的长度
   int c = snakeList.size();QFile file("C:/Users/zyy/Desktop/1.txt");if(file.open(QIODevice::WriteOnly| QIODevice::Text)){QTextStream out(&file);int num = c;out<<num ;file.close();}

代码的总体文件

在这里插入图片描述

gamehall.h

#ifndef GAMEHALL_H
#define GAMEHALL_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class GameHall; }
QT_END_NAMESPACEclass GameHall : public QWidget
{Q_OBJECTpublic:GameHall(QWidget *parent = nullptr);~GameHall();//从写绘图事件void paintEvent(QPaintEvent *event);private:Ui::GameHall *ui;
};
#endif // GAMEHALL_H

gameroom.h

#ifndef GAMEROOM_H
#define GAMEROOM_H#include <QWidget>
#include<QSound>enum class SnakeDirect
{UP=0,DOWN,LEFT,RIGHT
};class gameroom : public QWidget
{Q_OBJECT
public:explicit gameroom(QWidget *parent = nullptr);//重写绘图事件void paintEvent(QPaintEvent *event);void moveUp();void moveDown();void moveLeft();void moveRight();bool checkFail();void createNewFood();void  setTimeout(int timeout){moveTime=timeout;}private:const int KSnakeNodeWidth = 20;const int KSnakeNodeHeight = 20;const int KDefaultTimeout = 200;//移动速度QList<QRectF> snakeList;//表示链表QRectF foodRect;//食物SnakeDirect  moveDirect = SnakeDirect::UP;//默认移动方向QTimer *timer;//定时器bool  isGameStart = false;//游戏的开始QSound *sound;int moveTime = KDefaultTimeout;
};#endif // GAMEROOM_H

gameselect.h

#ifndef GAMESELECT_H
#define GAMESELECT_H#include <QWidget>class GameSelect : public QWidget
{Q_OBJECT
public:explicit GameSelect(QWidget *parent = nullptr);//绘图事件void paintEvent(QPaintEvent *event);
signals:};#endif // GAMESELECT_H

gamehall.cpp

#include "gamehall.h"
#include "ui_gamehall.h"
#include<QPainter>
#include<QIcon>
#include<QFont>
#include<QPushButton>
#include"gameselect.h"
#include<QSound>GameHall::GameHall(QWidget *parent): QWidget(parent), ui(new Ui::GameHall)
{ui->setupUi(this);this->setFixedSize(1500,1000);//设置窗口大小this->setWindowTitle(QString("贪吃蛇游戏"));this->setWindowIcon(QIcon(":res/ico.png"));//设置窗口图标//QFont font("华文行楷",20);QPushButton * strbutton = new QPushButton(this);strbutton->setText("开始游戏");strbutton->setFont(font);strbutton->move(650,700);strbutton->setShortcut(QKeySequence(Qt::Key_Space));strbutton->setStyleSheet("QPushButton{border:0px}");//给按钮设置样式  去除边框GameSelect *  gameSelect = new GameSelect;connect(strbutton,&QPushButton::clicked,[=](){this->close();//当前窗口关闭gameSelect->setGeometry(this->geometry());//设置第二个窗口于当前窗口一致gameSelect->show();//新的窗口打开QSound::play(":res/clicked.wav");});
}GameHall::~GameHall()
{delete ui;}void GameHall::paintEvent(QPaintEvent *event)
{//实例化画家对象QPainter painter(this);//实例化绘图设备QPixmap pix(":res/game_hall.png");//绘画painter.drawPixmap(0,0,this->width(),this->height(),pix);}

gameroom.cpp

#include "gameroom.h"
#include<QPainter>
#include<QPixmap>
#include<QIcon>
#include<QTimer>
#include<QPushButton>
#include<QMessageBox>
#include<QFile>
#include<QTextStream>
#include"gameselect.h"gameroom::gameroom(QWidget *parent) : QWidget(parent)
{this->setFixedSize(1500,1000);this->setWindowIcon(QIcon(":res/ico.png"));this->setWindowTitle("游戏房间");//初始化贪吃蛇snakeList.push_back(QRectF(this->width()*0.5,this->height()*0.5,KSnakeNodeWidth,KSnakeNodeHeight));moveUp();moveUp();moveUp();//创建食物createNewFood();timer = new QTimer(this);connect(timer,&QTimer::timeout,[=]{int cnt =1 ;if(snakeList.front().intersects(foodRect)){//intersects判断会不会相交createNewFood();cnt++;QSound::play(":res/eatfood.wav");}while (cnt--) {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();});//开始暂停QPushButton * strBtn = new QPushButton(this);QPushButton * stopBtn = new QPushButton(this);strBtn->move(1270,100);stopBtn->move(1270,170);strBtn->setText("开始");strBtn->setShortcut(QKeySequence(Qt::Key_C+Qt::CTRL));stopBtn->setText("暂停");stopBtn->setShortcut(QKeySequence(Qt::Key_V+Qt::CTRL));QFont font("楷体",20);strBtn->setFont(font);stopBtn->setFont(font);connect(strBtn,&QPushButton::clicked,[=]{sound = new QSound(":res/Trepak.wav");sound->play();sound->setLoops(-1);//一直循环播放isGameStart = true;timer->start(moveTime);});connect(stopBtn,&QPushButton::clicked,[=]{isGameStart = false;timer->stop();sound->stop();});//方向控制QPushButton*up= new QPushButton(this);QPushButton*down= new QPushButton(this);QPushButton*left= new QPushButton(this);QPushButton*right= new QPushButton(this);up->move(1320,750);down->move(1320,900);left->move(1220,825);right->move(1420,825);up->setText("↑");up->setShortcut(QKeySequence(Qt::Key_W));down->setText("↓");down->setShortcut(QKeySequence(Qt::Key_S));left->setText("←");left->setShortcut(QKeySequence(Qt::Key_A));right->setText("→");left->setShortcut(QKeySequence(Qt::Key_D));up->setStyleSheet("QPushButton{border:0px}");down->setStyleSheet("QPushButton{border:0px}");left->setStyleSheet("QPushButton{border:0px}");right->setStyleSheet("QPushButton{border:0px}");QFont ft("宋体",36);up->setFont(ft);down->setFont(ft);right->setFont(ft);left->setFont(ft);connect(up,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::DOWN){moveDirect = SnakeDirect::UP;}});connect(down,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::UP){moveDirect = SnakeDirect::DOWN;}});connect(left,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::RIGHT){moveDirect = SnakeDirect::LEFT;}});connect(right,&QPushButton::clicked,[=](){if(moveDirect!= SnakeDirect::LEFT){moveDirect = SnakeDirect::RIGHT;}});//退出游戏QPushButton*ExitBtn = new QPushButton(this);ExitBtn->setText("退出游戏");ExitBtn->move(1240,400);ExitBtn->setFont(font);QMessageBox *msg = new QMessageBox(this);QPushButton *ok = new QPushButton("ok");QPushButton *canel = new QPushButton("canel");msg->addButton(ok,QMessageBox::AcceptRole);msg->addButton(canel,QMessageBox::RejectRole);msg->setWindowTitle("退出游戏");msg->setText("确认退出游戏吗");connect(ExitBtn,&QPushButton::clicked,[=](){msg->show();msg->exec();//时间轮询QSound::play("res/clicked.wav");GameSelect *select = new GameSelect;if(msg->clickedButton()==ok){this->close();select->show();}else {msg->close();}});
}void gameroom::paintEvent(QPaintEvent *event)
{QPainter painter(this);//实例画家对象QPixmap pix;pix.load(":res/game_room.png");painter.drawPixmap(0,0,1200,1400,pix);pix.load(":res/bg1.png");painter.drawPixmap(1200,0,300,1100,pix);//绘制蛇//头if(moveDirect == SnakeDirect::UP){pix.load(":res/up.png");}else if (moveDirect == SnakeDirect::DOWN) {pix.load(":res/down.png");}else if (moveDirect == SnakeDirect::LEFT) {pix.load(":res/left.png");}else if(moveDirect == SnakeDirect::RIGHT){pix.load(":res/right.png");}auto snakHead =  snakeList.front();painter.drawPixmap(snakHead.x(),snakHead.y(),snakHead.width(),snakHead.height(),pix);//身体pix.load(":res/Bd.png");for (int  i= 1;i < snakeList.size()-1;i++) {auto node = snakeList.at(i);   //at 获取每一个节点painter.drawPixmap(node.x(),node.y(),node.width(),node.height(),pix);}//尾巴auto snakeTail = snakeList.back();painter.drawPixmap(snakeTail.x(),snakeTail.y(),snakeTail.width(),snakeTail.height(),pix);//食物pix.load(":res/food.png");painter.drawPixmap(foodRect.x(),foodRect.y(),foodRect.width(),foodRect.height(),pix);//分数pix.load(":res/sorce_bg.png");painter.drawPixmap(this->width()*0.85,this->height()*0.1,90,40,pix);QPen pen;pen.setColor(Qt::black);QFont font("方正舒体",22);painter.setFont(font);painter.setPen(pen);painter.drawText(this->width()*0.9,this->height()*0.06,QString("%1").arg(snakeList.size()));//往文件中写分数int c = snakeList.size();QFile file("C:/Users/zyy/Desktop/1.txt");if(file.open(QIODevice::WriteOnly| QIODevice::Text)){QTextStream out(&file);int num = c;out<<num ;file.close();}//绘制有戏失败的效果if(checkFail()){pen.setColor(Qt::red);painter.setPen(pen);QFont font("方正舒体",22);painter.setFont(font);painter.drawText(this->width()*0.45,this->height()*0.5,QString("GAME OVER!"));timer->stop();QSound::play(":res/gameover.wav");sound->stop();}
}void gameroom::moveUp()
{QPointF leftTop;//左上角坐标QPointF rightBottom;//右下角坐标auto snakeNode = snakeList.front();//头int headx = snakeNode.x();int heady = snakeNode.y();if(heady < 0){//穿墙了leftTop = QPointF(headx,this->height()-KSnakeNodeHeight);}else {leftTop = QPointF(headx,heady- KSnakeNodeHeight);}rightBottom = leftTop + QPointF(KSnakeNodeWidth,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(KSnakeNodeWidth,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(1500 - KSnakeNodeWidth,heady);}else {leftTop = QPointF(headx - KSnakeNodeWidth,heady);}rightBottom = leftTop + QPointF(KSnakeNodeWidth,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(heady + KSnakeNodeWidth> 1200){leftTop = QPointF(0,heady);}else {leftTop = snakeNode.bottomRight();}rightBottom = leftTop + QPointF(KSnakeNodeWidth,KSnakeNodeHeight);snakeList.push_front(QRectF(leftTop,rightBottom));
}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::createNewFood()
{foodRect = QRectF(qrand()%(1500/KSnakeNodeWidth)*KSnakeNodeWidth,qrand()%(this->height()/KSnakeNodeHeight)*KSnakeNodeHeight,KSnakeNodeWidth,KSnakeNodeHeight);
}

gameselect.cpp

#include "gameselect.h"
#include"gameroom.h"
#include<QIcon>
#include<QPushButton>
#include"gamehall.h"
#include<QSound>
#include<QPainter>
#include<QTextEdit>
#include<QFile>
#include<QTextStream>GameSelect::GameSelect(QWidget *parent) : QWidget(parent)
{this->setFixedSize(1500,1000);//设置窗口大小this->setWindowIcon(QIcon(":res/ico.png"));this->setWindowTitle("游戏关卡选择");QPushButton* backBtn = new QPushButton(this);//返回按钮backBtn->move(1400,900);backBtn->setIcon(QIcon(":res/back.png"));backBtn->setShortcut(QKeySequence(Qt::Key_Escape));connect(backBtn,&QPushButton::clicked,[=](){this->close();GameHall * gameHall = new GameHall;gameHall->show();QSound::play(":res/clicked.wav");});QFont font("华文行楷",20);gameroom*gameRoom = new gameroom;QPushButton* simpleBtn = new QPushButton(this);simpleBtn->move(650,300);simpleBtn->setFont(font);simpleBtn->setText("简单模式");connect(simpleBtn,&QPushButton::clicked,[=](){this->close();gameRoom->setGeometry(this->geometry());gameRoom->show();gameRoom->setTimeout(300);});QPushButton*normalBtn = new QPushButton(this);normalBtn->move(650,400);normalBtn->setFont(font);normalBtn->setText("正常模式");connect(normalBtn,&QPushButton::clicked,[=](){this->close();gameRoom->setGeometry(this->geometry());gameRoom->show();gameRoom->setTimeout(200);});QPushButton*hardBtn = new QPushButton(this);hardBtn->move(650,500);hardBtn->setFont(font);hardBtn->setText("困难模式");connect(hardBtn,&QPushButton::clicked,[=]{this->close();gameRoom->setGeometry(this->geometry());gameRoom->show();gameRoom->setTimeout(300);});QPushButton*hisBtn = new QPushButton(this);hisBtn->move(650,600);hisBtn->setFont(font);hisBtn->setText("历史战绩");connect(hisBtn,&QPushButton::clicked,[=](){QWidget *widget = new QWidget;widget->setWindowTitle("历史战绩");widget->setFixedSize(500,300);QTextEdit *edit = new QTextEdit(widget);edit->setFont(font);edit->setFixedSize(500,300);QFile file("C:/Users/zyy/Desktop/1.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)
{QPainter painter(this);//实例化画家对象QPixmap pix(":res/game_select.png");painter.drawPixmap(0,0,this->width(),this->height(),pix);
}

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CTFWP】ctfshow-web40

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 题目介绍&#xff1a;题目分析&#xff1a;payload&#xff1a;payload解释&#xff1a;payload2&#xff1a;payload2解释&#xff1a;flag 题目介绍&#xff1a; …

第一阶段面试问题(后半部分)

1. c语言中const *p的用法 &#xff08;1&#xff09;const int *p; 或 int const *p; 指向常量整数的指针&#xff0c;通过这个指针不能修改它所指向的整数值&#xff0c;但可以修改指针本身来指向其他地址 const int a 10; const int *p &a; // *p 20; // 错误&…

vector中 resize()和reserve()

1.resize()改变容器大小 resize除了预留内存以外&#xff0c;还会调用容器元素的构造函数&#xff0c;不仅分配了N个对象的内存&#xff0c;还会构造N个对象。从这个层面上来说&#xff0c;resize()在时间效率上是比reserve()低的。 2.reserve()容器大小管理 用于预留内存。 …

Flask目录结构路由重定向简单实例讲解——轻量级的 Python Web 框架

假设一个flask目录结构如下&#xff1a; my_flask_app/ │ ├── app.py ├── routes/ │ ├── __init__.py │ ├── ZhejiangProvince/ │ │ ├── __init__.py │ │ ├── la.py │ │ └── el.py │ ├── GuangdongProvince/ │ │ ├…

C语言项目实战FTP文件传输(windows网络编程基础)

文章目录 前言一、客户端和服务端介绍二、客户端和服务器之间进行通信的过程客户端和服务器建立通信的流程通信过程的示例图流程说明 三、客户端代码编写代码解释 四、服务端代码编写代码解释 总结 前言 本篇文章开始将带大家来学习FTP文件传输助手的项目实现&#xff0c;这个…

【单片机毕业设计选题24099】-室内空气质量检测及净化系统

系统功能: 系统上电后OLED显示“欢迎使用请稍后”两秒后进入正常界面显示&#xff0c; 第一行显示温湿度和MQ2值 第二行显示采集到的甲醛值 第三行显示采集到的PM2.5值 第四行显示设定的PM2.5值 短按B4按键增加PM2.5设定阈值 短按B5按键减小PM2.5设定阈值 如果PM2.5采集…

达梦数据库dsc集群动态添加节点

前提条件&#xff1a;在安装好的的dsc集群&#xff1a;达梦数据库dsc集群保姆级部署文档_达梦数据库文档-CSDN博客上动态添加节点 1、环境信息 扩展节点信息&#xff1a; 操作环境&#xff1a;VMware Workstation 16 Pro dmdsc集群 机器ip 主机名 操作系统 资源配置 实…

【传知代码】基于标签相关性的多标签学习(论文复现)

在当今信息爆炸的时代&#xff0c;数据中包含的标签信息对于理解和分析复杂问题至关重要。在诸如文本分类、图像识别和推荐系统等应用中&#xff0c;如何有效地利用标签相关性提升多标签学习的效果成为了研究的热点之一。基于标签相关性的多标签学习方法&#xff0c;通过挖掘不…

JAVA项目基于SpringBoot的外卖点餐管理系统

目录 一、前言 二、技术介绍 三、系统实现 四、论文参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着生活节…

Flink笔记整理(六)

Flink笔记整理&#xff08;六&#xff09; 完整系列在公众号&#xff1a;是十三不是四十三&#xff0c;欢迎关注~ 文章目录 Flink笔记整理&#xff08;六&#xff09;八、状态管理8.1 Flink中的状态概述状态的分类 8.2 按键分区状态&#xff08;Keyed State&#xff09;值状态…

Windows 添加自定义服务实现开机(用户登录之前)自动运行 Python 脚本

实现效果 使用 Python 编写的一个脚本, 希望在 Windows 系统启动时, 用户登录之前就自动运行. 准备工作 首先确保 Python 脚本可以手动正常运行, 演示起见, 编写下面的一个简单的脚本用于在 C 盘根目录中生成一个包含脚本运行时间戳的文本文件. Python 脚本存放在 C:\Python…

python爬虫实践

两个python程序的小实验&#xff08;附带源码&#xff09; 题目1 爬取http://www.gaosan.com/gaokao/196075.html 中国大学排名&#xff0c;并输出。提示&#xff1a;使用requests库获取页面的基本操作获取该页面&#xff0c;运用BeautifulSoup解析该页面绑定对象soup&#x…

搭建jenkins一键部署java项目

一、搭建jenkins 链接: https://pan.baidu.com/s/1jzx15PiyI8EhLd_vg7q8bw 提取码: ydhl 复制这段内容后打开百度网盘手机App&#xff0c;操作更方便哦 直接使用docker导入镜像&#xff0c;运行就好 docker run -di --name jenkins -p 8080:8080 -v /home/jenkins_home:/var/je…

【人工智能】NLP入门指南:自然语言处理基础全解析

文章目录 前言一、NLPNLP&#xff08;自然语言处理&#xff09;NLU&#xff08;自然语言理解&#xff09;NLG&#xff08;自然语言生成&#xff09; 二、分词1.什么是分词2.常见的分词工具3.jieba分词 三、词向量1.什么是词向量2.文本张量表示方法3.常见的词向量模型3.1 ont-ho…

15.4 zookeeper java client之Curator使用(❤❤❤❤❤)

Curator使用 1. 为什么使用Curator对比Zookeeper原生2. 集成Curator2.1 依赖引入curator-frameworkcurator-recipes2.2 `yml`配置连接信息2.3 CuratorConfig配置类2.4 Curator实现Zookeeper分布式锁业务2.4.1 业务:可重入锁和不可重入锁可重入锁和不可重入锁InterProcessMutex …

scratch魔法门 2024年6月scratch四级 中国电子学会图形化编程 少儿编程等级考试四级真题和答案解析

目录 scratch魔法门 一、题目要求 1、准备工作 2、功能实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、pyt…

基于JAVA的美甲店员工管理系统,源码、部署+讲解

摘 要 随着社会科技的飞速发展和进步&#xff0c;网络技术的应用已经深入到生活的方方面面。在这样的背景下&#xff0c;企事业单位的绩效考评体系也受到了极大的影响和冲击。传统的绩效考评方式已经无法满足现代社会的高效需求&#xff0c;因此&#xff0c;研发一款智能化、高…

42 PCB布线叠层与阻抗介绍43 PCB布线过孔添加与设置44 差分对添加与设置45 布线间距规则与介绍

42 PCB布线叠层与阻抗介绍&&43 PCB布线过孔添加与设置&44 差分对添加与设置&&45 布线间距规则与介绍 第一部分 42 PCB布线叠层与阻抗介绍1 板子是怎么来的。2 四层板为例&#xff0c;做叠层和阻抗计算。 第二部分 43 PCB布线过孔添加与设置介绍PCBEdotor中…

c#中的BitConverter的java实现

最近在做c#项目的java迁移&#xff0c;发现部分C#方法java中没有对应实现如图&#xff1a; 且java中的数字类型都是有符号的所以转无符号的时候需要进行手动对符号位& 0xFFFF进行处理&#xff0c;目前只整理了项目中使用到的方法&#xff0c;后续有用到其他方法在进行追加如…

linux学习记录(一)--------目录及文件操作

文章目录 前言Linux目录及文件操作1.Linux目录结构2.常用的Linux命令3.vi编辑器的简单使用4.vi的两个模式 前言 小白学习linux记录有错误随时指出~ Linux目录及文件操作 Linux采用Shell命令->操作文件 1.Linux目录结构 根目录&#xff1a;/ 用户目录&#xff1a;~或者/ho…