QT實現(xiàn)貪吃蛇游戲
為了熟悉QT的相關(guān)知識,我用了大約8個小時的時間用QT再次寫了一遍貪吃蛇。
因為QT的機制和平時寫的程序流程不同,所以程序中可能沒有遵守代碼規(guī)范。
運行效果:

程序內(nèi)除了實現(xiàn)貪吃蛇的基本功能外,還添加了記錄得分、調(diào)節(jié)游戲速度、重新開始游戲等功能。
游戲內(nèi)容使用QPainter類進行繪制。
編譯環(huán)境:
Windows Qt 5.9.0 Qt Creator 4.3.0
思路:
數(shù)組存儲蛇節(jié)點坐標的貪吃蛇游戲思路。
因為編寫過程中使用了分文件編寫,且QT框架的其他部分較多,這里僅展示程序?qū)崿F(xiàn)主要代碼。
代碼:
MainWindow.h 主窗口類頭文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QPainter> //畫家類
#include "snake.h" //蛇類
#include <QKeyEvent> //鍵盤事件
#include "food.h" //食物類
#include <QTimer> //定時器類
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
//繪圖事件
void paintEvent(QPaintEvent *);
//游戲結(jié)束的情況
void GameOver();
private:
Ui::Widget *ui;
Snake snake; //蛇對象
char key = 'd'; //接收鍵值,初始向右
Food food; //食物對象
//創(chuàng)建定時器
QTimer *timer = new QTimer(this);
bool gameflag = false;//游戲結(jié)束標記
int score = 0; //記錄得分
int timerspeed = 200; //定時器速度
bool bugflag = false; //用于解決操控過快導致撞死自己的bug的標志
//鍵盤事件,改變鍵值
void keyPressEvent(QKeyEvent *ev);
};
#endif // WIDGET_H
MainWindow.cpp函數(shù)實現(xiàn)
#include "MainWindow.h"
#include "ui_widget.h"
#include <QLabel>
#include <QPushButton>
//主窗口構(gòu)造函數(shù)
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
ui->setupUi(this); //初始化出.ui界面
//一個格子寬高25像素,地圖寬高600像素,每行有24個格子
//設(shè)置固定窗口大小
setFixedSize(1000,600);
//設(shè)置得分標簽控件的屬性
ui->label->setFont(QFont("黑體", 20, QFont::Bold, false));
//設(shè)置游戲速度標簽內(nèi)容
ui->label_2->setFont(QFont("楷體", 16, QFont::Bold, false));
ui->label_2->setText("游戲速度:");
//滑動條控件,這里將數(shù)值按一定比例放大縮小
ui->horizontalSlider->setValue(200 >> 2);
//監(jiān)聽滑動條控件滑動時,改變定時器速度為滑動條的值
connect(ui->horizontalSlider,QSlider::valueChanged,[=]()
{
timerspeed = ui->horizontalSlider->value() << 2;
});
//設(shè)置重新開始按鈕屬性和內(nèi)容
ui->pushButton_3->setFont(QFont("黑體",12));
ui->pushButton_3->setText("重新開始");
//監(jiān)聽重新開始按鈕
connect(ui->pushButton_3,QPushButton::clicked,[=]()
{
snake.Reset(); //重置蛇
score = 0; //得分清0
key = 'd'; //key值向右
timerspeed = 200 << 2;//重置游戲速度
timer->start(timerspeed);//重置定時器
ui->horizontalSlider->setValue(200 >> 2);//重置滑動條
gameflag = false; //重置游戲結(jié)束標志
});
//開啟定時器
timer->start(timerspeed);
//監(jiān)聽定時器的信號,執(zhí)行表達式內(nèi)容
connect(timer,&QTimer::timeout,[=]()
{
timer->start(timerspeed); //調(diào)整定時器的速度
snake.Move(key); //蛇移動函數(shù)
//吃到食物的情況
(food.getfoodX() == snake.snakevec.at(0).x && food.getfoodY() == snake.snakevec.at(0).y) ?
score++, food.randfood(): //得分+1,隨機產(chǎn)生一個食物坐標
snake.snakevec.pop_back(); //刪除蛇尾
GameOver(); //游戲失敗的情況
update(); //調(diào)用繪圖函數(shù)
//更新標簽控件的文本內(nèi)容
ui->label->setText(QString("得分:%1").arg(score));
});
}
//游戲失敗情況
void Widget::GameOver()
{
//撞墻失敗
if(snake.snakevec.at(0).x >= 24 * 25 || snake.snakevec.at(0).x < 0 ||
snake.snakevec.at(0).y < 0 || snake.snakevec.at(0).y >= 24 * 25)
{
gameflag = true;
timer->stop(); //暫停定時器
return;
}
//撞自己失敗
for(int i = 1; i < snake.snakevec.size(); i++)
{
if(snake.snakevec.at(0).x == snake.snakevec.at(i).x &&
snake.snakevec.at(0).y == snake.snakevec.at(i).y)
gameflag = true, timer->stop();
}
}
//繪圖事件
void Widget::paintEvent(QPaintEvent *)
{
//創(chuàng)建畫家對象,指定繪圖設(shè)備為this主窗口
QPainter painter(this);
//設(shè)置畫刷為紅色
QBrush brush1(Qt::red);
//讓畫家使用畫刷
painter.setBrush(brush1);
//畫食物
painter.drawRect(QRect(food.getfoodX(),food.getfoodY(),25,25));
//設(shè)置畫刷為黑色
QBrush brush2(Qt::blue);
painter.setBrush(brush2);
//遍歷蛇坐標容器,畫出代表蛇身的矩形
for(auto snakenodei : snake.snakevec)
//蛇頭畫圓形
(snakenodei.x == snake.snakevec.at(0).x &&
snakenodei.y == snake.snakevec.at(0).y) ?
//蛇頭畫圓形
painter.setRenderHint(QPainter::Antialiasing), //用抗鋸齒畫圓
painter.drawEllipse(QPoint(snakenodei.x + 12,snakenodei.y + 12),12,12):
//蛇身畫矩形
painter.drawRect(QRect(snakenodei.x,snakenodei.y,25,25));
bugflag = false; //在畫出蛇之后將標志設(shè)為假
//設(shè)置畫刷為黑色
QBrush brush3(QColor(128, 64, 0));
painter.setBrush(brush3);
//畫出墻壁
for(int i = 0; i < 24; i++)
painter.drawRect(QRect(25 * 25, i * 25, 25, 25));
//游戲結(jié)束時的繪圖
if(gameflag)
{ //設(shè)置文字屬性
QFont font("楷體", 80, QFont::Bold, false);
painter.setFont(font); //使用字體
painter.drawText(QRect(240, 200, 1000, 500), "游戲結(jié)束"); //畫出游戲結(jié)束文字
}
}
//鍵盤事件,改變貪吃蛇移動方向
void Widget::keyPressEvent(QKeyEvent *ev)
{
if(bugflag) //bug標志為真直接返回
return;
switch (ev->key())
{
case Qt::Key_W:
if(key == 's') break; //按下與當前方向相反按鍵時break
key = 'w';
bugflag = true; break; //按鍵之后將標志設(shè)為真
case Qt::Key_S:
if(key == 'w') break;
key = 's';
bugflag = true; break;
case Qt::Key_A:
if(key == 'd') break;
key = 'a';
bugflag = true; break;
case Qt::Key_D:
if(key == 'a') break;
key = 'd';
bugflag = true; break;
case Qt::Key_Space: //空格鍵暫停,這里有點問題
timer->stop(); break;
default: //其他鍵重啟定時器
timer->start(timerspeed);
}
}
//析構(gòu)
Widget::~Widget()
{
delete ui;
}
snake.h 蛇類頭文件
#ifndef SNAKE_H
#define SNAKE_H
#include <QWidget>
#include <QVector> //Qt動態(tài)數(shù)組類
class Snake : public QWidget
{
Q_OBJECT
private:
//蛇坐標結(jié)構(gòu)
struct Snakecoor
{
int x;
int y;
};
public:
explicit Snake(QWidget *parent = nullptr);
//蛇坐標容器
QVector<Snakecoor> snakevec;
//蛇移動主要函數(shù)
void Move(char key);
//根據(jù)key值確定蛇的移動方向
void DeteDirMove(char key, Snakecoor &nexthead);
//重置蛇
void Reset();
signals:
public slots:
};
#endif // SNAKE_H
snake.cpp 蛇類函數(shù)實現(xiàn)
#include "snake.h"
//構(gòu)造函數(shù)初始化一條蛇
Snake::Snake(QWidget *parent) : QWidget(parent)
{
Snakecoor snakenode; //臨時結(jié)構(gòu)變量用于創(chuàng)建蛇
for (int i = 5; i >= 0; i--) //從右向左創(chuàng)建5節(jié)蛇身,容器的第一個元素為蛇頭
{
snakenode.x = 25 * 8 + i * 25;//每個格子寬25像素
snakenode.y = 25 * 8;
snakevec.push_back(snakenode);//將蛇節(jié)點插入到數(shù)組中
}
}
//確定蛇的移動方向,確定新蛇頭的坐標位置
inline void Snake::DeteDirMove(char key, Snakecoor &nexthead)
{
switch (key)
{
case 'w':
//新蛇頭坐標根據(jù)舊蛇頭坐標改變
nexthead.x = snakevec.front().x;
nexthead.y = snakevec.front().y - 25;
break;
case 's':
nexthead.x = snakevec.front().x;
nexthead.y = snakevec.front().y + 25;
break;
case 'a':
nexthead.x = snakevec.front().x - 25;
nexthead.y = snakevec.front().y;
break;
case 'd':
nexthead.x = snakevec.front().x + 25;
nexthead.y = snakevec.front().y;
}
}
//蛇移動主要函數(shù)
void Snake::Move(char key)
{
Snakecoor nexthead; //創(chuàng)建一個新的蛇頭
DeteDirMove(key, nexthead); //確定新蛇頭的位置
snakevec.insert(snakevec.begin(), nexthead); //將新蛇頭插入到容器頭部
}
//重置蛇函數(shù)
void Snake::Reset()
{
snakevec.clear(); //清空容器
//初始化蛇
Snakecoor snakenode;
for (int i = 5; i >= 0; i--)
{
snakenode.x = 25 * 8 + i * 25;
snakenode.y = 25 * 8;
snakevec.push_back(snakenode);
}
}
food.h 食物類頭文件
#ifndef FOOD_H
#define FOOD_H
#include <QWidget>
class Food : public QWidget
{
Q_OBJECT
public :
int food_x = 0; //食物坐標
int food_y = 0;
public:
explicit Food(QWidget *parent = nullptr);
void randfood(); //隨機產(chǎn)生食物坐標
int getfoodX(); //返回食物坐標
int getfoodY();
signals:
public slots:
};
#endif // FOOD_H
food.cpp 食物類函數(shù)實現(xiàn)
#include "food.h"
#include <ctime>
Food::Food(QWidget *parent) : QWidget(parent)
{
randfood(); //創(chuàng)建對象時產(chǎn)生就一個隨機坐標
}
//隨機產(chǎn)生食物坐標
void Food::randfood()
{
srand((unsigned)time(NULL));
food_x = (rand() % 24) * 25;
food_y = (rand() % 24 )* 25;
}
//獲取食物的坐標
int Food::getfoodX()
{
return food_x;
}
int Food::getfoodY()
{
return food_y;
}
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
C++中聲明類的class與聲明結(jié)構(gòu)體的struct關(guān)鍵字詳解
這篇文章主要介紹了C++中聲明類的class與聲明結(jié)構(gòu)體的struct關(guān)鍵字,默認情況下結(jié)構(gòu)的所有成員均是公有的,而類的所有成員是私有的,需要的朋友可以參考下2016-01-01
c++11中關(guān)于std::thread的join的詳解
這篇文章主要介紹了c++11中關(guān)于std::thread的join詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-03-03
C語言數(shù)據(jù)結(jié)構(gòu) 棧的基礎(chǔ)操作
這篇文章主要介紹了C語言數(shù)據(jù)結(jié)構(gòu) 棧的基礎(chǔ)操作的相關(guān)資料,需要的朋友可以參考下2017-05-05
win10環(huán)境下C++ vs2015編譯opencv249的教程
這篇文章主要介紹了win10環(huán)境下C++ vs2015編譯opencv249的教程,本文分步驟給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-03-03

