时间:2021-04-09 09:02:29 | 栏目:C代码 | 点击:次
简述
最近浏览网站的时候,忘记在哪里看的这个FlappyBird了,这个小游戏在之前小火了一段时间。今天用QT简单的实现了一把,然后在网上找了一些相关的切图,便进行了制作。难度不是很大,只是通过写这篇博客,能有点启发以及大家共同学习。
效果图
代码
主界面控制
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(); }
结尾
全部代码,都在上面了,希望对大伙有所点启发和帮助,只为记录,只为分享! 愿所写能对你有所帮助。