QT实现贪吃蛇游戏

为了熟悉QT的相关知识,我用了大约8个小时的时间用QT再次写了一遍贪吃蛇。

因为QT的机制和平时写的程序流程不同,所以程序中可能没有遵守代码规范。

运行效果:

程序内除了实现贪吃蛇的基本功能外,还添加了记录得分、调节游戏速度、重新开始游戏等功能。

游戏内容使用QPainter类进行绘制。

编译环境:

Windows Qt 5.9.0 Qt Creator 4.3.0

思路:

数组存储蛇节点坐标的贪吃蛇游戏思路。

因为编写过程中使用了分文件编写,且QT框架的其他部分较多,这里仅展示程序实现主要代码。

代码:

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

    //游戏结束的情况
    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函数实现

#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 brush1(Qt::red);
    //让画家使用画刷
    painter.setBrush(brush1);
    //画食物
    painter.drawRect(QRect(food.getfoodX(),food.getfoodY(),25,25));

    //设置画刷为黑色
    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;    //在画出蛇之后将标志设为假

    //设置画刷为黑色
    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));

    //游戏结束时的绘图
    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 蛇类头文件

#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 蛇类函数实现

#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 食物类头文件

#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 食物类函数实现

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

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持云海天教程。