QT贪吃蛇项目

一、游戏项目具体功能及演示

1.1具体功能

        1.蛇的初始化;

        2.控制蛇上下左右移动;

        3.随机生成食物;

        4.蛇吃食物,蛇身变长;

        5.蛇头撞蛇尾即游戏结束;

        6.计分功能;

        7.简单QPushButton使用

1.2 游戏演示

        1.2.1 游戏主界面

        游戏界面由三个按键分别实现:开始游戏、暂停游戏和重新开始,如图1所示。

图1 游戏主界面

1.2.2 游戏进行时

        游戏进行时会随机刷新食物的位置,当蛇头与食物相交蛇身变长并分数板进行加分,如图2所示。

图2 游戏进行时

1.2.3 游戏结束

        当蛇头撞到蛇身即游戏结束,点击重新开始游戏重开,分数板归零,如图3所示。

图3 游戏结束

二、涉及到QT相关内容

1.QT定时器的使用;

2.键盘事件类;

3.画家、画笔、画刷等绘图相关类;

4.信号与槽机制。

三、代码实现

        代码主要由widget.h头文件和widget.cpp主要功能实现组成。

        3.1 首先引入头文件,涉及到按键事件、定时器、绘画事件等;

#include <QKeyEvent>
#include <QWidget>
#include <QTimer>
#include <QPaintEvent>
#include <QPainter>
#include <iostream>
#include <QDateTime>
#include <QLabel>
#include <QElapsedTimer>

        3.2头文件widget类中添加 绘画事件、按键操作、蛇的一系列操作声明;这类操作函数均放置在protected权限中

protected:
    //渲染、绘图 事件
    void paintEvent(QPaintEvent *event);

//1.按键处理
//
void keyPressEvent(QKeyEvent *event);
// 贪吃蛇吃食物,变长
    void addTop();//蛇顶部加长
   void addDown();//蛇底部加长
   void addRight();//蛇右部加长
   void addLeft();//蛇左部加长
   void deleteLast();//蛇尾部删除
   void reStarGame();//重新开始游戏

3..3枚举蛇的移动方向

typedef enum Direct{
    DIR_LEFT,
    DIR_RIGHT,
    DIR_UP,
    DIR_DOWN

}dir_t;

3.4widget.h 头文件完整代码

#ifndef WIDGET_H
#define WIDGET_H
#include <QKeyEvent>
#include <QWidget>
#include <QTimer>
#include <QPaintEvent>
#include <QPainter>
#include <iostream>
#include <QDateTime>
#include <QLabel>
#include <QElapsedTimer>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
//0、蛇的表示 可以用vector、list
//
//1.按键处理
//2.使用定时器
 //2.1 关联信号槽
 //2.2 启动定时器
 //2.3 实现对应的超时处理函数
//3.渲染

 typedef enum Direct{
    DIR_LEFT,
    DIR_RIGHT,
    DIR_UP,
    DIR_DOWN

}dir_t;

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
protected:
    //渲染、绘图 事件
    void paintEvent(QPaintEvent *event);

//1.按键处理
//
void keyPressEvent(QKeyEvent *event);
// 贪吃蛇吃食物,变长
   void addTop();
   void addDown();
   void addRight();
   void addLeft();
   void deleteLast();

   void updateScore(int timeAliveSeconds, int snakeLength);
private:
    Ui::Widget *ui;
    dir_t moveFlag=DIR_UP;//方向标志
    bool gameStart = false;//游戏开始标志
    //定时器
    QTimer *timer;
    QTimer *timer2;
    int time = 100;//ms
    int time1 = 1000;
    //蛇
    QList<QRectF>snake; //<QRectF>表示区域
    //小方块的大小 宽高
    int nodeWidth = 20;
    int nodeHeight = 20;
    //蛇的食物
    QRectF rewardNode;
    void addNewReword();
    bool checkContact();

    //记录开始的时间
    QDateTime startTime;   // 记录游戏开始的时间
     QLabel *scoreLabel;
     int score;
     int initialLength ; // 蛇的初始长度
     QElapsedTimer elapsedTimer; // 计时器,用于跟踪游戏运行时间

protected slots:
    void timeout();
    void timeout2();
};
#endif // WIDGET_H

3.6 widget.cpp 完整代码

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    //使用定时器
    timer=new QTimer();

    //定时器有一个超时信号timer函数
    //信号槽(信号和对应的函数关联)
    connect(timer,SIGNAL(timeout()),this,SLOT(timeout()));
//    resize(600,300);
    //定时器2
    timer2 =new QTimer();
     connect(timer2,SIGNAL(timeout2()),this,SLOT(timeout2()));
    setFixedSize(600, 300); // 设置窗口大小为宽400像素,高300像素

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

    //初始化蛇的食物
   addNewReword();
score = 0;
//    elapsedTimer.start();//游戏时间开始计时
//   //在构造函数中初始化 QLabel
//   scoreLabel = new QLabel(this);//()中需要添加this指针,指向对象本身
//    scoreLabel->setText("Score:0");
//    scoreLabel->setFont(QFont("Arial",12));
//    scoreLabel->setGeometry(50, 50, 150, 30);  // 设置位置和大小
//    scoreLabel->show();  // 显示 QLabel
}

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



//控制方向
void Widget::keyPressEvent(QKeyEvent *event)
{
    //需要添加#include <QKeyEvent>头文件  否则报错
    switch((event ->key())) {
    case Qt::Key_Up:
        if(moveFlag!=DIR_DOWN) //贪吃蛇上下行动时,需要判断,不然会撞死
        {
         moveFlag=DIR_UP;
        }
        break;
    case Qt::Key_Down:
        if(moveFlag!=DIR_DOWN){
        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);
           timer2->start(time1);
        }
        else
        {
            gameStart = false;
            timer->stop();//停止定时器1
            timer2->stop();//停止定时器1
        }
        break;
    default:
        break;
    }


}



void Widget::timeout()
{
    int count=1;
    //判断蛇是否吃到食物
    if(snake[0].intersects(rewardNode))
    {
        count++;
        addNewReword();
        score++;
    }
    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();

}

void Widget::timeout2()//计分板定时器
{
    int currentLength = snake.size();

       // 计算经过的时间(秒)
       qint64 elapsedTime = elapsedTimer.elapsed();
       int seconds = elapsedTime / 1000;

       // 根据公式计算分数
       score = seconds * 1 + (currentLength - initialLength) * 10;

       // 其他游戏逻辑(移动蛇身、检查碰撞等)

       // 更新显示
       update();
}


void Widget::addTop()
{//QPointF 点类, 有关点的操作
    QPointF leftTop;
    QPointF rightBotom;
    if(snake[0].y()-nodeHeight<0)
    {
       leftTop = QPoint(snake[0].x(),this->height()-nodeHeight);//this->height窗口的高度,与
       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::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::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::paintEvent(QPaintEvent *event)
{
    //画家类 画笔
    QPainter painter(this);
    //背景图片
    QPixmap pix;
    pix.load("C:/Users/ASUS/Documents/snakes0718/snake.jpeg");
    painter.drawPixmap(0,0,600,300,pix);

    //画蛇 画笔
    QPen pen;
    pen.setColor(Qt::black);
    //画刷
    QBrush brush;
    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::red);
    brush.setStyle(Qt::SolidPattern);
    brush.setColor(Qt::red);
    brush.setStyle(Qt::SolidPattern);
    painter.setPen(pen);
    painter.setBrush(brush);
//    painter.drawRect(rewardNode); 画方形
    painter.drawEllipse(rewardNode);

    // 绘制分数
        QFont font("方正舒体", 16, QFont::ExtraLight, false);  // 设置字体
        painter.setFont(font);
        painter.drawText(10, 20, QString("分数: %1").arg(score));  // 绘制分数文本

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


    QWidget::paintEvent(event);

}


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

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

}

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


四、参考资料

1.基于Qt实现的贪吃蛇游戏_qt写贪吃蛇-CSDN博客

2.【QT项目】QT项目实战:贪吃蛇项目丨完整详细视频教程,小白就业捷径_哔哩哔哩_bilibili

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值