欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Qt之FlappyBird游戏开发

程序员文章站 2024-03-18 19:46:22
...

Qt之FlappyBird游戏开发

简述

最近浏览网站的时候,忘记在哪里看的这个FlappyBird了,这个小游戏在之前小火了一段时间。今天用QT简单的实现了一把,然后在网上找了一些相关的切图,便进行了制作。难度不是很大,只是通过写这篇博客,能有点启发以及大家共同学习。

效果图

Qt之FlappyBird游戏开发

Qt之FlappyBird游戏开发

Qt之FlappyBird游戏开发

代码

主界面控制

MainWindow::MainWindow(QWidget *parent)
    : BasicWindow(parent)
    , m_startGame(false)
{
    ui.setupUi(this);
    setAttribute(Qt::WA_TranslucentBackground);
    initControl();
}

void MainWindow::initControl()
{
    loadStyleSheet("MainWindow");
    m_scene = new MainGraphicsScene(this, rect());
    QGraphicsView* view = new QGraphicsView(m_scene, this);

    view->setScene(m_scene);
    view->setStyleSheet("border:none; background:transparent;");
    view->setCacheMode(QGraphicsView::CacheBackground);
    startWelcome();
}

void MainWindow::startWelcome()
{
    //道路
    GraphicsRoadItem *roadItem = new GraphicsRoadItem(m_scene);

    //小鸟
    m_bird = new FlappyBird(m_scene);

    //管道
    GraphicsPipeitem *pipeItem = new GraphicsPipeitem(m_scene);

    //游戏状态检测,开启定时器,50ms检测一次
    m_checkGameStatus = new QTimer(this);
    connect(m_checkGameStatus, SIGNAL(timeout()), this, SLOT(onCheckGameStatus()));

    //flappybird字母
    static const int nLetters = 10;
    static struct {
        char const *pix;
        qreal initX, initY;
        qreal destX, destY;
    } letterData[nLetters] = {
        { "F", -1000, -1000, 150, 100 },
        { "L", -800, -1000, 200, 100 },
        { "A", -600, -1000, 250, 100 },
        { "P", -400, -1000, 300, 100 },
        { "P", 1000, 2000, 350, 100 },
        { "Y", 800, 2000, 400, 100 },
        { "B", 600, 2000, 260, 160 },
        { "I", 400, 2000, 310, 160 },
        { "R", 200, 2000, 360, 160 },
        { "D", 0, 2000, 410, 160 } };

    QSequentialAnimationGroup * lettersGroupMoving = new QSequentialAnimationGroup(this);
    m_lettersGroupFading = new QParallelAnimationGroup(this);

    for (int i = 0; i < nLetters; ++i) {
        QString& htmlText = QString("<span style=\"font-family:'Berlin Sans FB';font-size:48px;font-weight:600;color:#194819;\">%1</span>").arg(letterData[i].pix);
        QGraphicsTextItem *letter = new QGraphicsTextItem();
        letter->setHtml(htmlText);
        letter->setPos(letterData[i].initX, letterData[i].initY);

        QPropertyAnimation *moveAnim = new QPropertyAnimation(letter, "pos", lettersGroupMoving);
        moveAnim->setEndValue(QPointF(letterData[i].destX, letterData[i].destY));
        moveAnim->setDuration(200);
        moveAnim->setEasingCurve(QEasingCurve::OutElastic);
        lettersGroupMoving->addPause(50);

        QPropertyAnimation *fadeAnim = new QPropertyAnimation(letter, "opacity", m_lettersGroupFading);
        fadeAnim->setDuration(1000);
        fadeAnim->setEndValue(0);
        fadeAnim->setEasingCurve(QEasingCurve::OutQuad);

        m_scene->addItem(letter);
    }
    lettersGroupMoving->start(QAbstractAnimation::DeleteWhenStopped);

    //游戏开始按钮
    QPixmap&& pix = QPixmap(":/FlappyBird/Resources/texture/startButton.png").scaled(QSize(160, 48), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    GraphicsButtonItem* btnItem = new GraphicsButtonItem(pix, m_scene);
    btnItem->setPos(QPointF(220, 340));
    QPropertyAnimation *fadeAnim = new QPropertyAnimation(btnItem, "opacity", m_lettersGroupFading);
    fadeAnim->setDuration(600);
    fadeAnim->setEndValue(0);
    fadeAnim->setEasingCurve(QEasingCurve::OutQuad);
    connect(btnItem, SIGNAL(clicked()), this, SLOT(onStartBtnClicked()));
    connect(fadeAnim, &QPropertyAnimation::finished, [this](){
        m_startGame = true;
        m_checkGameStatus->start(50);
        m_bird->flyLandfallAnimation();
    });
}

void MainWindow::onCheckGameStatus()
{
    //检测小鸟是否与地面和管道发生碰撞
    if (m_bird->checkIsCollided())
    {
        GameOver();
    }
}

void MainWindow::GameOver()
{
    static const int nLetters = 8;
    static struct {
        char const *pix;
        qreal initX, initY;
        qreal destX, destY;
    } letterData[nLetters] = {
        { "G", -1000, -1000, 150, 100 },
        { "A", -800, -1000, 200, 100 },
        { "M", -600, -1000, 250, 100 },
        { "E", -400, -1000, 300, 100 },
        { "O", 600, 2000, 260, 160 },
        { "V", 400, 2000, 310, 160 },
        { "E", 200, 2000, 360, 160 },
        { "R", 0, 2000, 410, 160 } };

    QParallelAnimationGroup * lettersGroupMoving = new QParallelAnimationGroup(this);

    for (int i = 0; i < nLetters; ++i) {
        QString& htmlText = QString("<span style=\"font-family:'Berlin Sans FB';font-size:48px;font-weight:600;color:#194819;\">%1</span>").arg(letterData[i].pix);
        QGraphicsTextItem *letter = new QGraphicsTextItem();
        letter->setHtml(htmlText);
        letter->setPos(letterData[i].initX, letterData[i].initY);

        QPropertyAnimation *moveAnim = new QPropertyAnimation(letter, "pos", lettersGroupMoving);
        moveAnim->setEndValue(QPointF(letterData[i].destX, letterData[i].destY));
        moveAnim->setDuration(200);
        moveAnim->setEasingCurve(QEasingCurve::OutElastic);
        m_scene->addItem(letter);
    }
    lettersGroupMoving->start(QAbstractAnimation::DeleteWhenStopped);

    m_scene->removeItem(m_bird);
}

void MainWindow::onStartBtnClicked()
{
    m_lettersGroupFading->start(QAbstractAnimation::DeleteWhenStopped);
}

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (m_startGame)
    {
        m_bird->keyPressEvent(event);
    }
}

小鸟组件绘制

FlappyBird::FlappyBird(QGraphicsScene *scene): m_curflyStatus(0)
          , m_IsLandFall(true)
          , m_IsRaise(true)
{
    scene->addItem(this);
    m_scene = scene;
    m_birdRefreashTime = new QTimer(this);
    connect(m_birdRefreashTime, SIGNAL(timeout()), this, SLOT(onRefreashBird()));
    m_birdRefreashTime->start(12);

    m_flyAnimation = new QPropertyAnimation(this, "pos");
}

void FlappyBird::onRefreashBird()
{
    update();
}

QRectF FlappyBird::boundingRect() const
{
    return QRectF(60, FLY_BIRD_SIZE * 5 , FLY_BIRD_SIZE, FLY_BIRD_SIZE);
}

void FlappyBird::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->save();
    if (m_curflyStatus < 10)
    {
        m_curflyStatus++;
        painter->drawImage(boundingRect(), QImage(":/FlappyBird/Resources/texture/bird1.png"));
    }
    else if (m_curflyStatus < 20)
    {
        m_curflyStatus++;
        painter->drawImage(boundingRect(), QImage(":/FlappyBird/Resources/texture/bird2.png"));
    }
    else if ( m_curflyStatus < 30)
    {
        m_curflyStatus++;
        painter->drawImage(boundingRect(), QImage(":/FlappyBird/Resources/texture/bird3.png"));
    }
    else
    {
        m_curflyStatus = 0;
    }
    painter->restore();
}

void FlappyBird::flyRaiseAnimation()
{
    if (m_IsRaise)
    {
        m_IsLandFall = false;
        m_IsRaise = false;
        m_flyAnimation->stop();

        if (pos().y() > -180)
        {
            m_flyAnimation->setDuration(300);
            m_flyAnimation->setEndValue(QPoint(pos().x(), pos().y() - FLY_BIRD_SIZE));
        }
        else
        {
            m_flyAnimation->setDuration(300);
            m_flyAnimation->setEndValue(pos());
        }
        m_flyAnimation->setEasingCurve(QEasingCurve::OutQuad);
        m_flyAnimation->start();
        connect(m_flyAnimation, SIGNAL(finished()), this, SLOT(onFlyRaiseAnimationFinished()));
    }
}

void FlappyBird::onFlyRaiseAnimationFinished()
{
    m_flyAnimation->disconnect(SIGNAL(finished()));
    m_IsRaise = true;
    m_IsLandFall = true;
    flyLandfallAnimation();
}

void FlappyBird::flyLandfallAnimation()
{
    if (m_birdRefreashTime->isActive())
    {
        m_birdRefreashTime->stop();
    }

    if (m_IsLandFall)
    {
        m_flyAnimation->stop();
        int fallHeight = m_scene->height() - pos().y();
        int time = 1000 * fallHeight / m_scene->height();
        m_flyAnimation->setDuration(time);
        m_flyAnimation->setEndValue(QPoint(pos().x(), pos().y() + fallHeight));
        m_flyAnimation->setEasingCurve(QEasingCurve::InQuad);
        m_flyAnimation->start();
        m_IsLandFall = false;
    }
}

bool FlappyBird::checkIsCollided()
{
    if (!collidingItems().isEmpty())
        return true;
    else
        return false;
}

void FlappyBird::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Space)
    {
        flyRaiseAnimation();
    }
}

游戏开始按钮

GraphicsButtonItem::GraphicsButtonItem(const QPixmap &pixmap, QGraphicsScene *scene) : pix(pixmap)
{
    scene->addItem(this);
    setCursor(QCursor(Qt::PointingHandCursor));
}

void GraphicsButtonItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if (event->button() == Qt::LeftButton)
    {
        emit clicked();
    }
    __super::mousePressEvent(event);
}

QSizeF GraphicsButtonItem::size() const
{
    return pix.size();
}

QRectF GraphicsButtonItem::boundingRect() const
{
    return QRectF(QPointF(0, 0), pix.size());
}

void GraphicsButtonItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->drawPixmap(0, 0, pix);
}

管道组件绘制

#define PIPE_WIDTH 60

GraphicsPipeitem::GraphicsPipeitem(QGraphicsScene *scene)
{
    m_scene = scene;
    m_scene->addItem(this);

    createPipeHeight();
    startMove();
}
void GraphicsPipeitem::createPipeHeight()
{
    m_upPipeHeight = qrand() % 100 + 80;
    m_downPipeHeight = m_scene->height() - m_upPipeHeight - 178;
}

QRectF GraphicsPipeitem::boundingRect() const
{
    return QRectF(m_scene->width(), 0, PIPE_WIDTH, m_scene->height());
}

QPainterPath GraphicsPipeitem::shape() const
{
    QPainterPath path;
    path.addRect(QRectF(m_scene->width(), 0, PIPE_WIDTH, m_upPipeHeight));
    path.addRect(QRectF(m_scene->width(), m_upPipeHeight + 140, PIPE_WIDTH, m_downPipeHeight));
    return path;
}


void GraphicsPipeitem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->save();
    painter->drawImage(QRectF(m_scene->width(), 0, PIPE_WIDTH, m_upPipeHeight), QImage(":/FlappyBird/Resources/texture/tubeup.png").scaled(PIPE_WIDTH, m_upPipeHeight));
    painter->drawImage(QRectF(m_scene->width(), m_upPipeHeight + 140, PIPE_WIDTH, m_downPipeHeight), QImage(":/FlappyBird/Resources/texture/tubedown.png").scaled(PIPE_WIDTH, m_downPipeHeight));
    painter->restore();
}

void GraphicsPipeitem::startMove()
{
    QPropertyAnimation* moveAnimation = new QPropertyAnimation(this, "pos");
    moveAnimation->setLoopCount(-1);
    moveAnimation->setDuration(3000);
    moveAnimation->setStartValue(QPoint(0, pos().y()));
    moveAnimation->setEndValue(QPoint(-1 * m_scene->width() - PIPE_WIDTH, pos().y()));
    moveAnimation->start();
    connect(moveAnimation, &QPropertyAnimation::currentLoopChanged, [this](int loopCount){
        createPipeHeight();
    });
}

地面绘制

#define ROAD_ITEM_HEIGHT 38

GraphicsRoadItem::GraphicsRoadItem(QGraphicsScene *scene) :m_scene(scene)
{
    scene->addItem(this);
    startMove();
}

QRectF GraphicsRoadItem::boundingRect() const
{
    return QRectF(0, m_scene->height() - ROAD_ITEM_HEIGHT, m_scene->width() * 2, ROAD_ITEM_HEIGHT);
}

void GraphicsRoadItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *)
{
    painter->drawImage(QRectF(0, m_scene->height() - ROAD_ITEM_HEIGHT, m_scene->width(), ROAD_ITEM_HEIGHT), QImage(":/FlappyBird/Resources/texture/road.png"));
    painter->drawImage(QRectF(m_scene->width(), m_scene->height() - ROAD_ITEM_HEIGHT, m_scene->width(), ROAD_ITEM_HEIGHT), QImage(":/FlappyBird/Resources/texture/road.png"));
}

void GraphicsRoadItem::startMove()
{
    QPropertyAnimation* moveAnimation = new QPropertyAnimation(this, "pos");
    moveAnimation->setLoopCount(-1);
    moveAnimation->setDuration(6000);
    moveAnimation->setStartValue(QPoint(0, pos().y()));
    moveAnimation->setEndValue(QPoint(0 - m_scene->width(), pos().y()));
    moveAnimation->setEasingCurve(QEasingCurve::Linear);
    moveAnimation->start();
}

结尾

全部代码,都在上面了,希望对大伙有所点启发和帮助,只为记录,只为分享! 愿所写能对你有所帮助。不忘记点个赞,谢谢~