用QT庫寫的一個小遊戲——FlappyBird(憤怒的小鳥)
阿新 • • 發佈:2019-01-12
最近瀏覽網站的時候,忘記在哪裡看的這個FlappyBird了,這個小遊戲在前兩年小火了一段時間。今天用QT簡單的實現了一把,然後在網上找了一些相關的切圖,便進行了製作。難度不是很大,只是通過寫這篇部落格,能有點啟發以及大家共同學習。
如果你對C語言C++感興趣,那麼歡迎加入學習群【892643663】,獲取全套免費C/C++企業實戰級課程資源(素材+原始碼+視訊)和編譯大禮包。
程式碼
主介面控制
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();
}
全部程式碼,都在上面了,希望對大夥有所點啟發和幫助,只為記錄,只為分享! 願所寫能對你有所幫助。
如果你對C語言C++感興趣,那麼歡迎加入學習群【892643663】,獲取全套免費C/C++企業實戰級課程資源(素材+原始碼+視訊)和編譯大禮包。