QT实现贪吃蛇游戏

 更新时间:2021年05月17日 15:50:16   作者:白家名  
这篇文章主要为大家详细介绍了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;
}

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

相关文章

  • C语言 超详细介绍与实现线性表中的无头单向非循环链表

    C语言 超详细介绍与实现线性表中的无头单向非循环链表

    无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多
    2022-03-03
  • QT已有项目导入工程时注意事项图文详解

    QT已有项目导入工程时注意事项图文详解

    QT开发这几年大大小小项目做了不少,花了点时间对知识点总结整合了一部分,下面这篇文章主要给大家介绍了关于QT已有项目导入工程时注意事项的相关资料,需要的朋友可以参考下
    2023-11-11
  • C++ Qt实现音视频播放功能

    C++ Qt实现音视频播放功能

    Qt版本 5.9 基于C++11 Qt核心组件与附加组件安装时请打钩 否则可能出现项目中缺少视频播放模块的问题,由于最近着手的Qt项目需要视频播放自己做的时候踩很多坑避免以后踩坑,故在此记录实现过程,感谢的朋友参考下吧
    2021-11-11
  • C++ for循环与nullptr的小知识点分享

    C++ for循环与nullptr的小知识点分享

    这篇文章主要是来和大家介绍一些C++中的小知识点,本文分享的是for循环与nullptr,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起了解一下
    2023-05-05
  • 解析设计模式中的Prototype原型模式及在C++中的使用

    解析设计模式中的Prototype原型模式及在C++中的使用

    这篇文章主要介绍了设计模式中的Prototype原型模式及在C++中的使用,需要的朋友可以参考下
    2016-03-03
  • Objective-C 消息传递机制详解

    Objective-C 消息传递机制详解

    Objective-C语言中方法的传递有二种:①Selector ② Blocks,本文主要说一下Selector,本文以Objective-C 消息传递机制进行详细介绍,关于Blocks会在后续总结一下
    2012-11-11
  • OpenCV图像算法实现图像切分图像合并示例

    OpenCV图像算法实现图像切分图像合并示例

    这篇文章主要为大家介绍了python图像算法OpenCV实现图像切分图像合并操作示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-06-06
  • C++实现拼图游戏代码(graphics图形库)

    C++实现拼图游戏代码(graphics图形库)

    这篇文章主要为大家详细介绍了C++实现拼图游戏代码,带有graphics图形库,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • 基于C/C++将派生类赋值给基类的超详细讲解

    基于C/C++将派生类赋值给基类的超详细讲解

    类其实也是一种数据类型,也可以发生数据类型转换,下面这篇文章主要给大家介绍了关于基于C/C++将派生类赋值给基类的超详细讲解,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-06-06
  • C++实现LeetCode(70.爬楼梯问题)

    C++实现LeetCode(70.爬楼梯问题)

    这篇文章主要介绍了C++实现LeetCode(70.爬楼梯问题),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07

最新评论