贪吃蛇(使用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);
}
相关文章:

贪吃蛇(使用QT)
贪吃蛇小游戏 一.项目介绍**[贪吃蛇项目地址](https://gitee.com/strandingzy/QT/tree/zyy/snake)**界面一:游戏大厅界面二:关卡选择界面界面三:游戏界面 二.项目实现2.1 游戏大厅2.2关卡选择界面2.3 游戏房间2.3.1 封装贪吃蛇数据结构2.3.2 …...

【案例40】Apache中mod_proxy模块的使用
NC中间件 应用场景:配置了apache的情况,包括uap集群,配置https等场景下均适用;如果是单机(NC单结点情况不存在问题,则不用配置这项; was环境也不用配置此项。) 解决方案:按如下两…...
简单安装Android Studio并使用
在Windows上安装Android Studio的步骤如下: ### 1. 检查系统要求 确保你的计算机符合Android Studio的系统要求,通常包括: - Windows 8/10/11 - 64位处理器 - 最少4 GB RAM(推荐8 GB) - 最少2 GB可用硬盘空间ÿ…...
在Python中,模块(Module)和包(Package)
在Python中,模块(Module)和包(Package)是组织代码、提高代码复用性、促进代码维护的两种重要机制。它们各自扮演着不同的角色,但又紧密相连,共同构成了Python程序架构的基础。以下将详细阐述Pyt…...
Node版本管理工具
一、nvm 安装 二、常用命令 nvm v //查看nvm 版本号nvm install latest // 下载最新的 node 版本 nvm install 版本号 //安装node对应的版本nvm uninstall 版本号 //卸载对应的版本nvm list // 查看下载的所有版本的nodenvm use 版本号 // 只有引入了才能使用…...
创建并发布NPM模块
创建模块项目 $ mkdir my-npm-package $ cd my-npm-package $ npm init添加模块代码 创建新文件 index.js,内容如下 function helloworld() {console.log(Hello World!); }module.exports helloworld;测试模块 在模块目录(my-npm-package࿰…...
20240807软考架构-------软考31-35答案解析
每日打卡题31-35答案 31、【2015年真题】 难度:一般 对于遗留系统的评价框架如下图所示,那么处于“高水平、低价值”区的遗留系统适合于采用的演化策略为 ( )。 A.淘汰B.继承C.改造D.集成 答案…...

简单实现二叉树(链表实现)
接着上一节。我们接着学习二叉树,学习使用链表建立二叉树时,最好把每个子程序的递归展开图,都画一下 前言 在学习二叉树的基本结构前,需先要创建一颗二叉树,然后才能学习其相关的基本操作,由于现在大家二…...

搭建 Web 群集Haproxy
案例概述 Haproxy 是目前比较流行的一种群集调度工具,同类群集调度工具有很多,如 LVS 和Nginx。相比较而言,LVS 性能最好,但是搭建相对复杂;Nginx 的upstream模块支持群集功能,但是对群集节点健康检查功能不强…...

PDF隐写思路
文章目录 使用工具技术细节小结 使用工具 工具:WPS、010Editor、WbStego 技术细节 步骤: 使用 WPS 查看文件,看是否能打开。 若不能打开则使用 010Editor 查看是否少了头文件等。 正常的 PDF 缺少头文件的 PDF 如果缺少头文件则使用 010…...
Pycharm 常用快捷键
快捷键作用描述Ctrl Space基本的代码自动完成Ctrl Shift Space选择代码自动完成Ctrl D复制当前行或符号Ctrl X剪切当前行或符号Ctrl C复制当前行或符号Ctrl V粘贴剪贴板内容Ctrl Y删除当前行Ctrl A全选当前文件内容Ctrl Z撤销操作Ctrl Shift Z重做操作Ctrl F查找文…...
android音频录音,(一)MediaRecorder简介
1.MediaRecorder概述 Android 多媒体框架支持捕获和编码各种常见的音频和视频格式,简要介绍音频录音。 2.MediaRecorder 源码路径:frameworks/base/media/java/android/media/MediaRecorder.java 源码接口: setAudioSource(MediaRecorde…...

autoX.js
一. 概述 AutoX.js 使用 JavaScript 作为脚本语言,目前使用 Rhino 1.7.13 作为脚本引擎,支持 ES5 与部分 ES6 特性。 下载地址: https://github.com/kkevsekk1/AutoX/releases 官方文档: AutoX.js 二. 用法 1. 首先在官网下…...

日本软文发稿:日本主流发稿媒体有哪些?
日本软文发稿:日本主流发稿媒体有哪些 在日本发布软文时,选择合适的主流媒体进行推广是非常关键的。以下是一些在日本广受欢迎、影响力较大的媒体推荐(排列不区分媒体排名顺序): 1. 朝日新闻 (Asahi Shimbun) 朝日新…...

翰德恩赋能中国邮政信息科技产品创新系列培训
为了增强中邮信科公司需求分析工程师的专业素养,提升其业务需求和业务价值的挖掘能力,进而设计并交付满足用户期望的产品,提升用户体验,运营管理部于2024年4月至6月成功举办了六期需求分析工程师能力提升系列培训。 本次系列培训…...

分享一个基于SpringBoot的英语学习平台Java英语学习任务打卡系统(源码、调试、LW、开题、PPT)
💕💕作者:计算机源码社 💕💕个人简介:本人 八年开发经验,擅长Java、Python、PHP、.NET、Node.js、Android、微信小程序、爬虫、大数据、机器学习等,大家有这一块的问题可以一起交流&…...
Golang学习笔记
Go 语言学习笔记 1. 引言 Go 语言是由 Google 开发的一种静态类型、编译型的系统编程语言。它以简洁、高效和易于理解著称,并且支持并发编程。 2. 安装与环境配置 2.1 安装 Go 访问 Go 官方网站 下载适合你操作系统的安装包。安装完成后,设置 GOPAT…...

详解【多线程与并发】之线程
目录 1、线程 1.1线程Thread 1.2线程特点 1.3线程函数的原型 1.4Linux对于pthread的API的支持 1.4.1创建一个线程 1.4.2线程的退出 1.5资源分离 2、线程的同步/互斥机制 2.1线程互斥锁 2.1.1初始化线程互斥锁 2.2线程互斥锁的PV 操作 2.2.1P 操作(上锁…...
Linux安全与高级应用(四)深入探索MySQL数据库:安装、管理与安全实践
文章目录 标题:全面解析LAMP平台部署及应用第一部分:LAMP平台概述第二部分:准备工作第三部分:安装和配置PHP第四部分:配置Apache第五部分:测试LAMP平台第六部分:部署phpMyAdmin总结 ὄ…...

「iOS」自定义Modal转场——抽屉视图的实现
「iOS」自定义Modal转场——抽屉视图的实现 文章目录 「iOS」自定义Modal转场——抽屉视图的实现前言错误尝试自定义Modal转场实现流程自定义动画类UIPresentationController 成果展示参考文章 前言 在仿写网易云的过程之中,看到学长之前仿写时实现的抽屉视图&…...

基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...

大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...

ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
JDK 17 新特性
#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持,不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的ÿ…...
【Go语言基础【13】】函数、闭包、方法
文章目录 零、概述一、函数基础1、函数基础概念2、参数传递机制3、返回值特性3.1. 多返回值3.2. 命名返回值3.3. 错误处理 二、函数类型与高阶函数1. 函数类型定义2. 高阶函数(函数作为参数、返回值) 三、匿名函数与闭包1. 匿名函数(Lambda函…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...