flappy_bird(qt5.9.0实现)

前言

这个小游戏是我本人在大一小学期时所做的项目,难度不大,但是需要考虑很多游戏参数的问题, 比如画面更新的速度要设定为16ms,就是60帧;游戏背景画面其实不动,防止玩家眩晕,而是底下的管道移动;实现小鸟扑闪翅膀的动作是通过三张图片叠加完成的。

需要打包的源码(文件夹打包)的话点击这个链接

完整程序源码下载

游戏素材下载点击下面这个链接

游戏素材下载

论文参考点击下面链接(论文中的图片出现了加载问题后期会进行优化)

论文

工程

 按照这个构建文件即可

bestscore.h

#ifndef BESTSCORE_H
#define BESTSCORE_H


class bestscore
{
public:
    bestscore();
    int score;
    //存储最高分
    void save();
    //读入最高分
    void init();
};

#endif // SCORE_H

globalutils.h

#ifndef GLOBALUTILS_H
#define GLOBALUTILS_H
#include <QObject>

class GlobalUtils
{
public:
    GlobalUtils();
    //获取随机数
    static int getRandomNum(uint seed, int limit);
};

#endif // GLOBALUTILS_H

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QPainter>
#include <QDebug>
#include <QTimer>
#include <QVector>
#include <QKeyEvent>
#include <QLabel>
#include <QPushButton>
#include "pipechannel.h"
#include "globalutils.h"
#include <bestscore.h>
#include <qsound.h>

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    void paintEvent(QPaintEvent *event);
    //可通过按下键盘实现小鸟上升
    //void keyPressEvent(QKeyEvent *event);
    void mousePressEvent(QMouseEvent * event);
    //初始化
    void init();
    //初始化游戏相关
    void initGame();
    //初始化速度
    void initSpeed();
    //开始游戏
    void startGame();
    //停止游戏
    void stopGame();
    //绘制背景
    void drawBack();
    //绘制bird
    void drawBird();
    //绘制管道
    void drawPipe();
    //碰撞判定
    bool isCrush();
public slots:
     //循环绘制(定时器)
    void loopPaint();
    //游戏的循环
    void slotStartGame();
private:
    QPixmap background; //背景图
    QPixmap  ground; //地面
    QPixmap bird1; //小鸟的三种状态
    QPixmap bird2;
    QPixmap bird3;

    int birdNo = 1;
    int birdNoMax = 15;
    int birdY = 0; // 小鸟的位置Y坐标
    int birdX = 0; // 小鸟的位置X坐标
    QMatrix matrix;
    enum GameStatus{STOPING = 1, RUNNING}; //游戏状态枚举
    enum BirdStatus{DEFAULT, UP, DOWN}; //小鸟飞行状态枚举
    GameStatus gameStatus = GameStatus::STOPING; //游戏状态 默认停止
    BirdStatus birdStatus = BirdStatus::DOWN;
    double birdDownSpeed = 0;
    double birdUpSpeed = 2.0;
    QTimer *timer = nullptr;
    QVector<int> * v = nullptr;//地面
    int groundSize = 0; //地面个数
    int moveSpeed = 2; //移动速度
    QVector<PipeChannel*> * pipeChannels = nullptr;//管道

    int score = 0;
    bestscore best;
    QLabel* scoreLabel  = nullptr; //记录分数

    QLabel* scoreLabel1  = nullptr; //记录最高分数

    QPushButton* startButton = nullptr;

};

#endif // MAINWINDOW_H

pipechannel.h

#ifndef PIPECHANNEL_H
#define PIPECHANNEL_H

#include <QObject>
#include <QPixmap>
#include <QImage>
#include <QPainter>
/**
 * @brief The PipeChannel class
 * 通道
 */
class PipeChannel : public QObject
{
    Q_OBJECT
public:
    bool isScore = false; //是否已经计分
    //winHeight 窗口高度 h1 底下管道高度 x 位置横坐标 groundHeight 地面高度 parent
    PipeChannel(int winHeight, int h1, int x, int groundHeight, QObject *parent = nullptr);
    //初始化管道
    void initPipe();
    //绘制管道
    void draw(QPainter &painter);
    //setX 设置位置 X坐标
    void setX(int x);
    //getX 获取位置
    int getX();
    //getY1 获取管道1(底部管道)的y坐标
    int getY1();
    //getPiPe2Height 获取管道2的高度
    int getPiPe2Height();
    //获取管道宽度
    int getPipeWidth();
    //设置底下管道高度
    void setH1(int h1);
    ~PipeChannel();
signals:

public slots:
private:
     QImage pipeImage; // 管道图片
     QImage imageMirror; //管道图像 镜像
    int channelWidth = 250; //通道宽度
    int h1 = 150; //底下管道的高度
    int pipeWidth = 90; //管道宽度
    QPixmap pipe1; //底下管道
    QPixmap pipe2; //上面管道
    int winHeight, //窗口高度
    x, //位置横坐标
    groundHeight; //地面高度
};

#endif // PIPECHANNEL_H

bestscore.cpp

#include<bestscore.h>
#include<qfile.h>
#include<qstring.h>
#include<qcoreapplication.h>
#include<qdebug.h>

//读入最高分
bestscore::bestscore()
{
    QFile file(QCoreApplication::applicationDirPath().append("/score.txt"));
    file.open(QFile::ReadOnly);
    QByteArray ba = file.readAll();
    file.close();
    score = ba.toInt();
}
//存储最高分
void bestscore::save()
{
    QFile file(QCoreApplication::applicationDirPath().append("/score.txt"));
    file.open(QFile::WriteOnly|QFile::Text);
    file.write(QString::number(score).toLocal8Bit());
    file.close();
}
//初始化
void bestscore::init()
{
    QFile file(QCoreApplication::applicationDirPath().append("/score.txt"));
    file.open(QFile::ReadOnly);
    QByteArray ba = file.readAll();
    file.close();
    score = ba.toInt();
}

globalutils.h

#include "globalutils.h"

GlobalUtils::GlobalUtils(){

}

//获取随机数 0 ~ 200之间
int GlobalUtils::getRandomNum(uint seed,int limit){
    qsrand(seed);
    return qrand()%limit;
}

main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

mainwindow.cpp

#include "mainwindow.h"
#include <QString>
#include <QDebug>
#include <QCoreApplication>
#define DIEPATH QCoreApplication::applicationDirPath().append("/res/hit.wav")
#define WINGPATH QCoreApplication::applicationDirPath().append("/res/wing.wav")
#define BGMPATH QCoreApplication::applicationDirPath().append("/res/bgm.wav")
#define POINTPATH QCoreApplication::applicationDirPath().append("/res/point.wav")

MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent)
{
    init();
    initGame();
}

MainWindow::~MainWindow()
{
    delete  pipeChannels;
}
void MainWindow::paintEvent(QPaintEvent *)
{
        //qDebug() << "paintEvent";
      drawBack();
      drawPipe();
      drawBird();
}
void MainWindow::mousePressEvent(QMouseEvent * )
{
    // qDebug() << "click";
     //QSound::play(WINGPATH);//鸟上升时扑打翅膀的声音
     birdStatus = BirdStatus::UP;
     initSpeed();
}
//初始化
void MainWindow::init()
{
    QSound::play(BGMPATH);
    //this->move(400, 300);
    this->setFixedSize(480, 640);
     qDebug() << "init ";
   ground = QPixmap( QCoreApplication::applicationDirPath().append("/res/ground.png")); // 使用绝对路径
    if(!ground){
        qDebug() << "ground is null" << endl;
    }
    ground = ground.scaled(20,80); // 缩放
    int groundHeight = ground.height();
    background = QPixmap( QCoreApplication::applicationDirPath().append("/res/background.png"));
    background = background.scaled(this->width(), this->height() - groundHeight); //缩放

    groundSize = this->width()/ground.width() + 1; //防止除不尽有余数,所以加1
    v = new QVector<int>(groundSize);

    for(int i = 0; i < groundSize; i++){
         int x = i * ground.width();
         (*v)[i] = x;

    }
    QPixmap bird( QCoreApplication::applicationDirPath().append("/res/bird.png"));
    bird1 = bird.copy(0, 0, 92, 64);
    bird1 = bird1.scaled(60, 42);

    bird2 = bird.copy(92, 0, 92, 64);
    bird2 = bird2.scaled(60, 42);

    bird3 = bird.copy(92*2, 0, 92, 64);
    bird3 = bird3.scaled(60, 42);

    scoreLabel = new QLabel(this);
    scoreLabel->setGeometry(width()/2, 0, 50, 50);

    QFont font ( "Microsoft YaHei", 20, 75); //第一个属性是字体(微软雅黑),第二个是大小,第三个是加粗(权重是75)
    scoreLabel->setFont(font);
    QPalette pa;
    pa.setColor(QPalette::WindowText,Qt::white);
    scoreLabel->setPalette(pa);
    this->score = 0;
    scoreLabel->setText(QString::number(this->score));

    //最高分数设置
    scoreLabel1 = new QLabel(this);
    scoreLabel1->setGeometry(0, this->height() - 50, 250, 50);

    scoreLabel1->setFont(font);
    scoreLabel1->setPalette(pa);
    scoreLabel1->setText("best score " + QString::number(best.score));

    startButton = new QPushButton(this);
    font = QFont( "Microsoft YaHei", 25, 75); //第一个属性是字体(微软雅黑),第二个是大小,第三个是加粗(权重是75)
    startButton->setFont(font);
    startButton->setPalette(pa);
    startButton->setText(QString("start"));
    startButton->setGeometry(width()/2 -50, height()/2 -30, 100, 60);
   // startButton->hide();

    timer = new QTimer(this);
    //事件
    connect(timer,SIGNAL(timeout()),this,SLOT(loopPaint()));
    connect(startButton,SIGNAL(clicked()),this,SLOT(slotStartGame()));
}
//初始化游戏相关
void MainWindow::initGame()
{
    best.init();
    birdX = this->width() / 3;
    birdY = this->height() / 2 - bird1.height();
    this->score = 0;
    scoreLabel->setText(QString::number(this->score));
    scoreLabel1->setText("best score " + QString::number(best.score));
    uint seed_x = static_cast<uint>(clock());
    int l = GlobalUtils::getRandomNum(seed_x,200); //获取随机数
    if(pipeChannels == nullptr){
        pipeChannels = new QVector<PipeChannel*>(2); //创建两个通道
    }else {
        delete (*pipeChannels)[0];
        delete (*pipeChannels)[1];
    }
    (*pipeChannels)[0] = new PipeChannel(height(), 100+l, width()+100, ground.height(), this);//窗口高度 底下管道高度 x位置横坐标 地面高度 objet parent
    (*pipeChannels)[1] = new PipeChannel(height(), 150 +l, 2 * width() + l, ground.height(), this);
}
//初始化速度
 void MainWindow::initSpeed()
 {
     birdDownSpeed = 2.0;
     birdUpSpeed = 5.0;
 }
//开始游戏
void MainWindow::startGame()
{
    QSound::play(POINTPATH);
    startButton->hide();
    initGame();
    initSpeed();
    gameStatus = GameStatus::RUNNING;
    birdStatus = BirdStatus::DOWN;
    timer->start(16);
}
//停止游戏
void MainWindow::stopGame()
{
    timer->stop();
    gameStatus = GameStatus::STOPING;
    startButton->show();
    if(score > best.score)
    {
        best.score = score;
        best.save();
    }
}
//绘制背景
void MainWindow::drawBack()
{
     QPainter painter(this);
     int height = ground.height();
     int pos;
     // 绘制背景图
     painter.drawPixmap(0,0, background);
     // 绘制地面
     for(int i = 0; i < groundSize; i++)
     {
         pos = (*v)[i];
         painter.drawPixmap(pos,this->height() - height, ground);//绘制一个地面图像
         // 改变坐标 移动起来
         pos -= moveSpeed;
         if(pos <= -ground.width()){
             pos = (groundSize-1) *  ground.width();
         }
         (*v)[i] = pos;
     }
}
//绘制bird
void MainWindow::drawBird()
{

     QPainter painter(this);
     QPixmap bird = bird1;
     if(gameStatus == GameStatus::RUNNING){
         //绘制哪一个 (动画效果)
         if(birdNo < birdNoMax/3){
             bird = bird1;
         }else if(birdNo < birdNoMax/3 * 2){
             bird = bird2;
         }else{
             bird = bird3;
         }
         birdNo++;
         if(birdNo > birdNoMax){
             birdNo = 1;
         }
     }
     // 小鸟下降
     if(birdStatus == BirdStatus::DOWN){
         birdY += birdDownSpeed;
         birdDownSpeed+=0.1;
         //matrix.rotate(1); //下降的同时旋转
         //bird = bird.transformed(matrix, Qt::SmoothTransformation);
     }
    // 判断是否碰撞
     if(isCrush()){
        QSound::play(DIEPATH);
        stopGame();
     }
    // 上升
     if(birdStatus == BirdStatus::UP){
         birdY -= birdUpSpeed;
         birdUpSpeed -= 0.2;
         //matrix.rotate(-1);
         //bird = bird.transformed(matrix, Qt::SmoothTransformation);
         if(birdUpSpeed <= 0.0){
             birdStatus = BirdStatus::DOWN;
            // qDebug() << "down";
             initSpeed();
         }
     }
     painter.drawPixmap(birdX, birdY, bird);
}
//绘制管道
void MainWindow::drawPipe()
{
    QPainter painter(this);
    uint seed_x = static_cast<uint>(clock());
    int l = GlobalUtils::getRandomNum(seed_x,200); //获取随机数
    int otherX = 0; //另一个通道的位置, 只有两个管道
    for(int i = 0; i < pipeChannels->size(); i++)
    {
        PipeChannel * pipeChannel = (*pipeChannels)[i];
        if(i ==0)
        {
            otherX = (*pipeChannels)[1]->getX();
        }else {
            otherX = (*pipeChannels)[0]->getX();
        }
        pipeChannel->draw(painter);
        int x = pipeChannel->getX();

        if(x + pipeChannel->getPipeWidth() < birdX && !pipeChannel->isScore)
        {
            QSound::play(POINTPATH);
            this->score++; //增加得分
            scoreLabel->setText(QString::number(this->score));
            pipeChannel->isScore = true;
        }
        x -= moveSpeed; // 改变坐标移动起来
        if(x <= -pipeChannel->getPipeWidth())// 移出窗口了
        {
            x = otherX + width() / 2 +  2 * l; //重新设置位置
            pipeChannel->setH1(50+l); //重新设置高度
            pipeChannel->initPipe();//重新初始化
        }
        pipeChannel->setX(x);
    }
}
//碰撞检测
bool MainWindow::isCrush()
{
    if (birdY >= this->height() - ground.height() - bird1.height()){ //是否碰撞地面
        return true;
    }
    //if(birdY <= 0)return true;//是否碰撞天空
    //是否碰撞管道
    for(PipeChannel* c : *pipeChannels){
        if(birdX + bird1.width() > c->getX() && birdX < c->getX() + c->getPipeWidth()
                && (birdY < c->getPiPe2Height() || birdY+bird1.height() > c->getY1())){
            return true;
        }
    }
    return false;
}
//循环绘制
void MainWindow::loopPaint(){
    update();
}
//循环游戏
void MainWindow::slotStartGame(){
    startGame();
}

pipechannel.cpp

#include "pipechannel.h"
#include <QCoreApplication>
//静态成员初始化

//winHeight 窗口高度 h1 底下管道高度 x 位置横坐标 groundHeight 地面高度 parent
PipeChannel::PipeChannel(int winHeight,int h1, int x, int groundHeight, QObject *parent) : QObject(parent)
{
    this->h1 = h1;
    this->x = x;
    this->groundHeight = groundHeight;
    this->winHeight = winHeight;
    initPipe();
}
//初始化管道
void PipeChannel::initPipe()
{
    pipeImage =QImage( QCoreApplication::applicationDirPath().append("/res/pipe.png"));
    imageMirror = pipeImage.mirrored(false, true);

    pipe1 = QPixmap::fromImage(pipeImage);
    pipe2 = QPixmap::fromImage(imageMirror);
    int h2 = getPiPe2Height();
    pipe1 = pipe1.copy(0, 0, pipe1.width(), h1);
    pipe1 = pipe1.scaled(pipeWidth, h1);

    pipe2 = pipe2.copy(0, pipe2.height()-h2, pipe2.width(), h2);
    pipe2 = pipe2.scaled(pipeWidth, h2);
    isScore = false;
}
//绘制管道
void PipeChannel::draw(QPainter &painter){
    painter.drawPixmap(x, getY1(), pipe1);//管道1(下面)
    painter.drawPixmap(x, 0, pipe2);//管道2
}
//设置位置 X坐标
void PipeChannel::setX(int x)
{
      this->x = x;
}
//getX 获取位置
int PipeChannel::getX()
{
    return this->x;
}
//getY1 获取管道1(底部管道)的y坐标
int PipeChannel::getY1()
{
    return winHeight - h1 - groundHeight;
}
//获取管道2的高度
int PipeChannel::getPiPe2Height()
{
    return winHeight - channelWidth -h1;
}
//获取管道宽度
int PipeChannel::getPipeWidth()
{
    return this->pipeWidth;
}
//设置高度
void PipeChannel::setH1(int h1)
{
    this->h1 = h1;
}
PipeChannel::~PipeChannel()
{

}

  • 8
    点赞
  • 62
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值