QT项目实战:贪吃蛇小游戏

目录

内容介绍

一.添加头文件

二.初始化蛇与奖品

三.设置背景图,蛇与奖品

1.设置背景图

2.画蛇

3.画奖品

四.小蛇移动

1.控制方向

2.向上移动

3.向下移动

4.向右移动

5.向左移动

五.随机生成奖品位置

六.是否吃到奖品

七.删除操作

八.游戏结束

九.效果演示

十.代码演示

.h

.cpp


内容介绍

首先,在构造函数中,创建了一个定时器,并设置了信号和槽的连接,这样定时器每次超时时间到达,就会调用timeout函数。同时,在构造函数中也创建了蛇和奖品,并将它们画在了界面上。

在keyPressEvent函数中,根据用户按下的方向键,来改变蛇的移动方向。

在addTop、addDown、addRight、addLeft函数中,根据蛇的当前移动方向,来生成蛇的新身体部分,并插入到蛇的身体列表的最前面。

在addNewReward函数中,生成一个新的奖品,并将它的位置记录在rewardNode变量中。

在paintEvent函数中,使用QPainter来画出背景、蛇和奖品,并判断蛇是否与奖品或者自己的身体发生了碰撞,如果发生了碰撞,则游戏结束。

在deleteLast函数中,删除蛇的最后一节身体。

在checkContact函数中,判断蛇的身体是否发生了重合,如果发生了重合,则返回true,否则返回false。

在timeout函数中,根据蛇的当前移动方向,生成新的身体部分,并插入到蛇的身体列表的最前面,同时判断蛇的头部是否碰到了奖品,如果碰到了,则增长蛇的身体,并生成一个新的奖品,如果蛇的身体发生了重合,则游戏结束。

一.添加头文件

#include <QKeyEvent> 是 C++ 源代码文件中包含 Qt 库的头文件,用于处理键盘事件。在应用程序中,可以通过重写 QWidget::keyPressEvent() 或 QWidget::keyReleaseEvent() 函数来处理键盘事件。这两个函数的参数都是 QKeyEvent 对象,可以用它来获取键盘事件的详细信息,例如按下或释放的键、键的序号、修饰键等。

#include <QTimer> 是 C++ 源代码文件中包含 Qt 库的头文件,用于创建定时器。QTimer 是 Qt 中用于定时操作的类,可以用来触发特定时间间隔后的操作。通过 QTimer,可以实现定时执行某些任务、定时更新界面等功能。

#include <QPainter> 是 C++ 源代码文件中包含 Qt 库的头文件,用于绘制图形和图像。QPainter 是 Qt 中用于绘制图形和图像的类,可以用来绘制各种形状、文本、图像等。通过 QPainter,可以实现自定义绘制、图形界面美化等功能。

二.初始化蛇与奖品

三.设置背景图,蛇与奖品

1.设置背景图

    QPixmap pix;
    pix.load("D://3.jpg");
    painter.drawPixmap(0,0,600,368,pix);

2.画蛇

创建了一个 QPainter 对象 painter,并设置了画笔颜色为黑色、画刷颜色为深紫色,填充方式为实心填充。然后,将画笔和画刷设置到 painter 对象中。最后,遍历 snake 数组,绘制每个矩形。

3.画奖品

首先设置了画笔颜色为红色、画刷颜色为深紫色,填充方式为实心填充。然后,将画笔和画刷设置到 painter 对象中。接着,绘制了一个奖品圆形。如果 checkContact() 函数返回 true,则表示与奖品接触,此时会设置字体为方正舒体,大小为30,加粗,斜体,并绘制游戏结束提示信息。最后,停止计时器,并调用父类的 paintEvent() 函数。

四.小蛇移动

1.控制方向

(1).定义一个 QKeyEvent 指针 event,用于接收键盘事件。

(2).使用 switch 语句根据 event->key() 返回的键值进行不同的操作。

如果按下的是 Qt::Key_Up 键,则判断当前移动方向 moveFlag 是否为 DIR_DOWN,如果不是,则将 moveFlag 设置为 DIR_UP。
如果按下的是 Qt::Key_Down 键,则判断当前移动方向 moveFlag 是否为 DIR_UP,如果不是,则将 moveFlag 设置为 DIR_DOWN。
如果按下的是 Qt::Key_Left 键,则判断当前移动方向 moveFlag 是否为 DIR_RIGHT,如果不是,则将 moveFlag 设置为 DIR_LEFT。
如果按下的是 Qt::Key_Right 键,则判断当前移动方向 moveFlag 是否为 DIR_LEFT,如果不是,则将 moveFlag 设置为 DIR_RIGHT。
如果按下的是 Qt::Key_Space 键,则判断游戏是否已经开始,如果未开始,则将 gameStart 设置为 true,启动定时器 timer,并设置定时器的时间间隔为 time;如果游戏已经开始,则将 gameStart 设置为 false,停止定时器 timer。
如果按下的是其他键,则不做任何操作。

2.向上移动

小蛇向上移动可以看做将最下面的方块删除,在最上方增加一个方块,实现小蛇向上移动。其他方向依照这种逻辑即可。

3.向下移动

4.向右移动

5.向左移动

五.随机生成奖品位置

六.是否吃到奖品

1.定义一个整型变量 count,初始化为 1。

2.判断蛇的第一个节点 snake[0] 是否与食物 rewardNode 重合,如果重合,则 count 加 1,调用 addNewReward() 函数添加新的食物。

3.使用 while 循环 count 次,根据当前移动方向 moveFlag 的值,分别调用 addTop()addDown()addLeft()addRight() 函数,添加新的蛇节点。

4.调用 deleteLast() 函数删除蛇的最后一个节点。

5.调用 update() 函数更新界面。

七.删除操作

八.游戏结束

检查蛇身是否相撞,标准游戏结束

九.效果演示

十.代码演示

.h

#ifndef WIDGET_H
#define WIDGET_H
#include <QKeyEvent>
#include <QWidget>
#include <QTimer>
#include <QPainter>

namespace Ui {
class Widget;
}

typedef enum Direct{
    DIR_LEFT,
    DIR_RIGHT,
    DIR_UP,
    DIR_DOWN
}dir_t;

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

protected:
    void keyPressEvent(QKeyEvent *event);

    void addTop();
    void addDown();
    void addRight();
    void addLeft();

    void addNewReward();

    void paintEvent(QPaintEvent *event);

    void deleteLast();

    bool checkContact();

private:
    Ui::Widget *ui;

    dir_t moveFlag = DIR_UP;
    bool gameStart = false;

    QTimer *timer;

    int time = 100;//超时时间间隔

    //奖品
    QRectF rewardNode;

    //蛇
    QList <QRectF> snake;

    //小方块的高度
    int nodeWidth = 20;
    int nodeHeight = 20;

protected slots:
    //定时器
    void timeout();

};

#endif // WIDGET_H

.cpp

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    //使用定时器
    timer = new QTimer();
    //信号槽
    connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));

    resize(600,368);

    //初始化蛇身子
    QRectF rect(300,180,nodeHeight,nodeWidth);
    snake.append(rect);
    addTop();
    addTop();

    //初始化奖品
    addNewReward();
}

Widget::~Widget()
{
    delete ui;
}

//控制方向
void Widget::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_Up:
        if(moveFlag != DIR_DOWN){
           moveFlag = DIR_UP;
        }
        break;
    case Qt::Key_Down:
        if(moveFlag != DIR_UP){
           moveFlag = DIR_DOWN;
        }
        break;
    case Qt::Key_Left:
        if(moveFlag != DIR_RIGHT){
            moveFlag = DIR_LEFT;
        }
        break;
    case Qt::Key_Right:
        if(moveFlag != DIR_LEFT){
            moveFlag = DIR_RIGHT;
        }
        break;
    case Qt::Key_Space:
        if(gameStart == false){
            gameStart = true;
            //启动定时器
            timer->start(time);
        }else{
            gameStart = false;
            timer->stop();
        }
        break;
    default:
        break;
    }
}

void Widget::addTop()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].y() - nodeHeight < 0){
        leftTop = QPointF(snake[0].x() , this->height() - nodeHeight);
        rightBotom = QPointF(snake[0].x() + nodeWidth , this-> height());
    }else{
        leftTop = QPointF(snake[0].x(),snake[0].y() - nodeHeight);
        rightBotom = snake[0].topRight();
    }

    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addDown()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].y() + nodeHeight * 2 > this -> height()){
        leftTop = QPointF(snake[0].x() , 0);
        rightBotom = QPointF(snake[0].x() + nodeWidth , nodeHeight);
    }else{
        leftTop = snake[0].bottomLeft();
        rightBotom = snake[0].bottomRight() + QPointF(0,nodeHeight);
    }

    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addRight()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].x() + nodeWidth*2 > this->width()){
        leftTop = QPointF(0,snake[0].y());
    }else{
        leftTop = snake[0].topRight();
    }

    rightBotom = leftTop + QPointF(nodeWidth,nodeHeight);
    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addLeft()
{
    QPointF leftTop;
    QPointF rightBotom;

    if(snake[0].x() - nodeWidth < 0){
        leftTop = QPointF(this->width() - nodeWidth , snake[0].y());
    }else{
        leftTop = snake[0].topLeft() - QPointF(nodeWidth,0);
    }

    rightBotom = leftTop + QPointF(nodeWidth,nodeHeight);
    snake.insert(0,QRectF(leftTop,rightBotom));
}

void Widget::addNewReward()
{
    rewardNode = QRectF(qrand()%(this->width()/20) * 20,
                        qrand()%(this->height()/20) * 20,
                        nodeHeight,
                        nodeWidth);
}

void Widget::paintEvent(QPaintEvent *event)
{
    QPainter painter(this);
    QPen pen;
    QBrush brush;

    //背景图
    QPixmap pix;
    pix.load("D://3.jpg");
    painter.drawPixmap(0,0,600,368,pix);

    //画蛇
    pen.setColor(Qt::black);
    brush.setColor(Qt::darkMagenta);
    brush.setStyle(Qt::SolidPattern);
    painter.setPen(pen);
    painter.setBrush(brush);

    for(int i = 0 ; i < snake.length(); i++){
        painter.drawRect(snake[i]);
    }

    //画奖品
    pen.setColor(Qt::red);
    brush.setColor(Qt::darkMagenta);
    brush.setStyle(Qt::SolidPattern);
    painter.setPen(pen);
    painter.setBrush(brush);
    painter.drawEllipse(rewardNode);

    if(checkContact()){
        QFont font("方正舒体",30,QFont::ExtraLight,false);
        painter.setFont(font);
        painter.drawText((this->width()-300)/2,
                         (this->height()-30)/2,
                         QString("GAME OVER"));
        timer->stop();
    }

    QWidget::paintEvent(event);
}

void Widget::deleteLast()
{
    snake.removeLast();
}

bool Widget::checkContact()
{
    for(int i=0; i<snake.length() ; i++){
        for(int j=i+1 ; j<snake.length();j++){
            if(snake[i] == snake[j]){
                return  true;
            }
        }
    }
    return  false;
}

void Widget::timeout()
{
    int count = 1;
    //判断是否有重合
    if(snake[0].intersects(rewardNode)){
       count++;
       addNewReward();
    }

    while (count--) {
        switch (moveFlag) {
        case DIR_UP:
            addTop();
            break;
        case DIR_DOWN:
            addDown();
               break;
            case DIR_LEFT:
               addLeft();
               break;
            case DIR_RIGHT:
               addRight();
               break;
            default:
               break;
        }
    }

    deleteLast();
    update();
}






  • 13
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值