How QT implements the snake game

This article mainly introduces how QT implements the snake game, which has certain reference value. Interested friends can refer to it. I hope you will gain a lot after reading this article. Let the editor take you to understand it together.

running result:

In addition to realizing the basic functions of Snake, the program also adds functions such as recording scores, adjusting the game speed, and restarting the game.

Game content is drawn using the QPainter class.

The benefits of this article, free to receive Qt development learning materials package, technical video, including (Qt actual combat project, C++ language foundation, C++ design mode, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Compiler Environment:

Windows Qt 5.9.0 Qt Creator 4.3.0

Ideas:

The idea of ​​​​snake game that stores the coordinates of snake nodes in an array.

Because the writing process uses sub-file writing, and there are many other parts of the QT framework, only the main code for program implementation is shown here.

code:

MainWindow.h main window class header file

#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 *);

    //游戏结束的情况
    void GameOver();

private:
    Ui::Widget *ui;

    Snake snake;        //蛇对象
    char key = 'd';     //接收键值,初始向右
    Food food;          //食物对象
    //创建定时器
    QTimer *timer = new QTimer(this);
    bool gameflag = false;//游戏结束标记
    int score = 0;      //记录得分
    int timerspeed = 200; //定时器速度
    bool bugflag = false; //用于解决操控过快导致撞死自己的bug的标志

    //键盘事件,改变键值
    void keyPressEvent(QKeyEvent *ev);
};

#endif // WIDGET_H

MainWindow.cpp function implementation

#include "MainWindow.h"
#include "ui_widget.h"
#include <QLabel>
#include <QPushButton>

//主窗口构造函数
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget)
{
    ui->setupUi(this);      //初始化出.ui界面

    //一个格子宽高25像素,地图宽高600像素,每行有24个格子

    //设置固定窗口大小
    setFixedSize(1000,600);

    //设置得分标签控件的属性
    ui->label->setFont(QFont("黑体", 20, QFont::Bold, false));

    //设置游戏速度标签内容
    ui->label_2->setFont(QFont("楷体", 16, QFont::Bold, false));
    ui->label_2->setText("游戏速度:");

    //滑动条控件,这里将数值按一定比例放大缩小
    ui->horizontalSlider->setValue(200 >> 2);

    //监听滑动条控件滑动时,改变定时器速度为滑动条的值
    connect(ui->horizontalSlider,QSlider::valueChanged,[=]()
    {
        timerspeed = ui->horizontalSlider->value() << 2;
    });

    //设置重新开始按钮属性和内容
    ui->pushButton_3->setFont(QFont("黑体",12));
    ui->pushButton_3->setText("重新开始");

    //监听重新开始按钮
    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;   //重置游戏结束标志
    });

    //开启定时器
    timer->start(timerspeed);

    //监听定时器的信号,执行表达式内容
    connect(timer,&QTimer::timeout,[=]()
    {
        timer->start(timerspeed); //调整定时器的速度

        snake.Move(key);    //蛇移动函数

        //吃到食物的情况
        (food.getfoodX() == snake.snakevec.at(0).x && food.getfoodY() == snake.snakevec.at(0).y) ?
        score++, food.randfood():    //得分+1,随机产生一个食物坐标
        snake.snakevec.pop_back();   //删除蛇尾

        GameOver();    //游戏失败的情况

        update();      //调用绘图函数

        //更新标签控件的文本内容
        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 *)
{
    //创建画家对象,指定绘图设备为this主窗口
    QPainter painter(this);

    //设置画刷为红色
    QBrush brush2(Qt::red);
    //让画家使用画刷
    painter.setBrush(brush2);
    //画食物
    painter.drawRect(QRect(food.getfoodX(),food.getfoodY(),25,25));

    //设置画刷为黑色
    QBrush brush3(Qt::blue);
    painter.setBrush(brush3);

    //遍历蛇坐标容器,画出代表蛇身的矩形
    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;    //在画出蛇之后将标志设为假

    //设置画刷为黑色
    QBrush brush4(QColor(128, 64, 0));
    painter.setBrush(brush4);

    //画出墙壁
    for(int i = 0; i < 24; i++)
          painter.drawRect(QRect(25 * 25, i * 25, 25, 25));

    //游戏结束时的绘图
    if(gameflag)
    {   //设置文字属性
        QFont font("楷体", 80, QFont::Bold, false);
        painter.setFont(font);      //使用字体
        painter.drawText(QRect(240, 200, 1000, 500), "游戏结束");   //画出游戏结束文字
    }
}

//键盘事件,改变贪吃蛇移动方向
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;        //按键之后将标志设为真
    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);
    }
}

//析构
Widget::~Widget()
{
    delete ui;
}

snake.h snake header file

#ifndef SNAKE_H
#define SNAKE_H

#include <QWidget>
#include <QVector>      //Qt动态数组类

class Snake : public QWidget
{
    Q_OBJECT
private:
    //蛇坐标结构
    struct  Snakecoor
    {
        int x;
        int y;
    };

public:
    explicit Snake(QWidget *parent = nullptr);

    //蛇坐标容器
    QVector<Snakecoor> snakevec;

    //蛇移动主要函数
    void Move(char key);

    //根据key值确定蛇的移动方向
    void DeteDirMove(char key, Snakecoor &nexthead);

    //重置蛇
    void Reset();

signals:

public slots:
};

#endif // SNAKE_H

snake.cpp snake function implementation

#include "snake.h"

//构造函数初始化一条蛇
Snake::Snake(QWidget *parent) : QWidget(parent)
{
    Snakecoor snakenode;            //临时结构变量用于创建蛇
    for (int i = 5; i >= 0; i--) //从右向左创建5节蛇身,容器的第一个元素为蛇头
    {
        snakenode.x = 25 * 8 + i * 25;//每个格子宽25像素
        snakenode.y = 25 * 8;
        snakevec.push_back(snakenode);//将蛇节点插入到数组中
    }
}

//确定蛇的移动方向,确定新蛇头的坐标位置
inline void Snake::DeteDirMove(char key, Snakecoor &nexthead)
{
    switch (key)
    {
    case 'w':
        //新蛇头坐标根据旧蛇头坐标改变
        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;
    }
}

//蛇移动主要函数
void Snake::Move(char key)
{
    Snakecoor nexthead;  //创建一个新的蛇头

    DeteDirMove(key, nexthead);  //确定新蛇头的位置

    snakevec.insert(snakevec.begin(), nexthead);    //将新蛇头插入到容器头部
}

//重置蛇函数
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 food header file

#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();    //随机产生食物坐标
    int getfoodX();     //返回食物坐标
    int getfoodY();

signals:

public slots:
};

#endif // FOOD_H

food.cpp food function implementation

#include "food.h"
#include <ctime>

Food::Food(QWidget *parent) : QWidget(parent)
{
    randfood();     //创建对象时产生就一个随机坐标

}

//随机产生食物坐标
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;
}

The benefits of this article, free to receive Qt development learning materials package, technical video, including (Qt actual combat project, C++ language foundation, C++ design mode, introduction to Qt programming, QT signal and slot mechanism, QT interface development-image drawing, QT network, QT database programming, QT project combat, QSS, OpenCV, Quick module, interview questions, etc.) ↓↓↓↓↓↓See below↓↓Click on the bottom of the article to receive the fee↓↓

Guess you like

Origin blog.csdn.net/m0_73443478/article/details/132637971