天天看點

Qt遊戲程式設計——飛機大戰

效果圖:

Qt遊戲程式設計——飛機大戰
Qt遊戲程式設計——飛機大戰

這篇文章記錄了我用一周的時間從零Qt基礎到制作出飛機大戰的過程。

Qt相比MFC封裝好了大量函數,而且非常有條理。飛機大戰的實作主要用到了Qt的信号與槽機制、事件響應機制、計時器、随機數、QPainter繪圖以及最關鍵的圖形視圖架構,在做飛機大戰之前,建議掌握以下内容:

1.了解信号與槽機制,熟練使用connect函數

2.了解Qt時間響應機制,并學會響應滑鼠、鍵盤事件

3.學會用QTimer控制時間

4.學會生成随機數(本文制作的遊戲基本全部使用随機數控制,如果要制作高品質遊戲的話建議結合計時器自己寫各物體的行為)

5.學會使用QPainter繪圖

6.最關鍵的一點,一定要熟練掌握圖形視圖架構,這是本文遊戲程式設計的核心

7.多百度,多參考幫助文檔

這裡強力推薦Qt社群的快速入門教程,上面的内容在基礎篇基本全部包含,而且Qt社群有大量的下載下傳資源:

​​http://www.qter.org/portal.php?mod=view&aid=26​​

建議下載下傳Qt5版本,并下載下傳版本新一點的Qt creator,網上很多下載下傳教程與下載下傳資源,這裡不再啰嗦

當完成了上面全部内容後,就可以正式開始了:

PS:遊戲邏輯我會放在最後介紹,當然讀者也可以跳到下面及時參考遊戲邏輯的代碼

根據面向對象的思想,我們應該有一個飛行物體的抽象類,為遊戲中的所有飛行物體提供一個接口,飛行物體有飛機和子彈,而飛機有分為玩家飛機和敵機,子彈也分為玩家子彈和敵機子彈,是以還要建飛機抽象類和子彈抽象類繼承飛行物體抽象類,對于飛機類,編寫玩家飛機實體類和敵機實體類來繼承實作飛機抽象類,對于子彈類,編寫玩家子彈實體類和敵機子彈實體類繼承實作子彈抽象類,現在,讀者便可以編寫各式各樣的飛機子彈了,編寫完成後隻要繼承相應的玩家飛機、敵機、玩家子彈、敵機子彈類就可以了。為了更好的實作各式各樣的飛機子彈,我們建立飛機工廠和子彈工廠,在工廠中創造我們需要的物體。

大體思路說完以後,我們來說說這幾個類的具體實作。

先看最核心的的類——飛行物體抽象類,所有飛行物體都應該有大小、速度、圖檔,以及動畫函數、碰撞檢測函數和摧毀函數,然後編寫飛機抽象類,飛機都有血量,而且都會發射子彈,還要有一個函數負責給飛機減血,之後編寫子彈抽象類,子彈是飛機大戰中最終要的元素,除了各式各樣的圖檔外,還應該朝不同方向發射,這就需要一個角度。

對于實體類,首先要實作基類中的純虛函數。對于玩家飛機,我們應該為其添加鍵盤控制,并在實作發射子彈函數時通過子彈工廠裝填玩家子彈,對于敵機,為了遊戲性讀者應該會添加各式各樣的雜魚、BOSS來繼承敵機類,這就需要我們為每種類型的敵機都添加其屬于自己的子彈、動畫效果,為了實作各種移動我們應該為其添加一個角度,便于在飛機工廠中及時控制。建各式各樣的飛機飛行效果。玩家子彈、敵機子彈同理編寫各式各樣的子類,配合子彈工廠實作各種效果。

這裡繪圖,動畫,碰撞檢測函數系統都封裝好了,完成了準備工作的讀者應該可以熟練掌握。

除此之外遊戲還有好多其他元素,比如各種補充包,我們可以寫一個supply類繼承飛行物體類,然後編寫子類實作各種補充包。玩家技能則可以看做特殊的子彈繼承子彈類,在子彈工廠中創造出來。動态效果的話可以用一組圖檔通過不斷變換來實作,還有更多豐富功能請讀者自行添加......

對于遊戲邏輯,其實應該讀者自己編寫,我這裡隻提供了一個關卡的模闆space,space應該具有一個update函數不斷刷屏,配合系統提供的advance函數實作移動,同時用step變量來判斷隔多久生成敵人、補充包,并用level判斷一次生成多少個敵人,然後就是一些開始遊戲、暫停遊戲、遊戲結束之類的邏輯以及顯示界面了(這裡我沒有實作得分顯示之類的,本來我想用QGraohicsTextItem實作的,結果發現确實dll而runtime error!)

說了這麼多,該上代碼了:

全局變量:

#ifndef GLOBAL_H
#define GLOBAL_H

//此代碼僅是一個模闆,元素比較單一,但功能接口比較全,各種類型都可以通過在相應的工廠中改變參數實作,或是繼承強大的基類實作
//遊戲效果可以在全局變量中便捷的控制,添加新元素時注意添加相應的全局變量
//某些細節可能不條理,可以進行适當地調整
//代碼中間很少加注釋,參考度娘

#include <QtWidgets>

//Name
#define BLOODSUPPLYNAME 1
#define BOMBSUPPLYNAME 2
#define BULLETSUPPLYNAME 3
#define PLAYERPLANENAME 4
#define ENEMYPLANENAME 5
#define PLAYERBULLETNAME 6
#define ENEMYBULLETNAME 7
#define BOMBNAME 8

//Size
#define SCENEWIDTH  1000
#define SCENEHEIGHT  1000
#define BLOODSUPPLYSIZE 30
#define BOMBSUPPLYSIZE 30
#define BULLETSUPPLYSIZE 30
#define PLAYERPLANESIZE 40
#define FISHSIZE 100
#define BOSSSIZE 200
#define PLAYERBULLETSIZE 20
#define FISHBULLETSIZE1 20
#define FISHBULLETSIZE2 20
#define FISHBULLETSIZE3 20
#define BOSSBULLETSIZE1 80
#define BOSSBULLETWIDTH2 200
#define BOSSBULLETHEIGHT2 40
#define BOSSBULLETSIZE3 200
#define BOSSBULLETWIDTH4 200
#define BOSSBULLETHEIGHT4 200
#define BOMBSIZE 800

//Speed
#define BLOODSUPPLYSPEED 20
#define BOMBSUPPLYSPEED 20
#define BULLETSUPPLYSPEED 20
#define PLAYERPLANESPEED 10
#define FISHSPEED 10
#define BOSSSPEED 2
#define PLAYERBULLETSPEED 50
#define FISHBULLETSPEED1 20
#define FISHBULLETSPEED2 20
#define FISHBULLETSPEED3 20
#define BOSSBULLETSPEED1 5
#define BOSSBULLETSPEED2 20
#define BOSSBULLETSPEED3 20
#define BOSSBULLETSPEED4 5
#define BOMBSPEED 10

//UpdateTime, 因為包含一些内部邏輯, 直接改動可能會有錯誤, 改之前確定了解advance函數中step的意義
const int PLAYERSHOOTSTEP  = 5;
const int BOSSSHOOTSTEP = 20;
const int FISHSHOOTSTEP = 20;
const int BOMBPIXSTEP = 10;
const int BOSSPIXSTEP = 10;

//Score
#define FISHSCORE 10
#define BOSSSCORE 1000

//Hp
#define FISHHP 10
#define BOSSHP 1000

//UP(上限)
#define PLAYERPLANEBLOOD 3
#define PLAYERPLANEBOMB 3
#define PLAYERPLANEBULLET 3

//隻實作了boss的動态、玩家的動态和炸彈動态,背景滾動和爆炸效果同理,這裡懶得寫了
//想要改變動态效果的話注意圖檔數量,然後自行在飛機、子彈工廠以及控制台中進行相應的改變
const QString background = ":/image/background/background.jpeg";
const QString bloodsupplypix = ":/image/bloodsupply/bloodsupply.png";
const QString bomb0 = ":image/bomb/bomb0.png";
const QString bomb1 = ":image/bomb/bomb1.png";
const QString bombsupplypix = ":/image/bombsupply/bombsupply.png";
const QString boss0 = ":/image/enemyplane/boss0.png";
const QString boss1 = ":/image/enemyplane/boss1.png";
const QString boss2 = ":/image/enemyplane/boss2.png";
const QString boss3 = ":/image/enemyplane/boss3.png";
const QString bossbullet0 = ":/image/enemybullet/bossbullet0.png";
const QString bossbullet1 = ":/image/enemybullet/bossbullet1.png";
const QString bossbullet2 = ":/image/enemybullet/bossbullet2.png";
const QString bossbullet3 = ":/image/enemybullet/bossbullet3.png";
const QString bulletsupplypix = ":/image/bulletsupply/bulletsupply.png";
const QString fish0 = ":/image/enemyplane/fish0.png";
const QString fish1 = ":/image/enemyplane/fish1.png";
const QString fish2 = ":/image/enemyplane/fish2.png";
const QString fishbullet0 = ":/image/enemybullet/fishbullet0.png";
const QString fishbullet1 = ":/image/enemybullet/fishbullet1.png";
const QString fishbullet2 = ":/image/enemybullet/fishbullet2.png";
const QString playerbullet0 = ":/image/playerbullet/playerbullet0.png";
const QString playerbullet1 = ":/image/playerbullet/playerbullet1.png";
const QString playerbullet2 = ":/image/playerbullet/playerbullet2.png";
const QString playerplane0 = ":/image/PlayerPlane/playerplane0.png";
const QString playerplane1 = ":/image/PlayerPlane/playerplane1.png";
const QString playerplane2 = ":/image/PlayerPlane/playerplane2.png";
const QString playerplane3 = ":/image/PlayerPlane/playerplane3.png";

#endif // GLOBAL_H      

飛行物體抽象類

#ifndef FLYER_H
#define FLYER_H

#include <QtWidgets>
#include "global.h"

typedef enum {UP, DOWN, LEFT, RIGHT} CHECK_FLAG;
typedef QList<QPixmap> QPixmaps;

class Flyer : public QGraphicsObject
{
    Q_OBJECT
public:
    Flyer(qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0)
        : QGraphicsObject(parent), m_w(w), m_h(h), m_speed(speed), m_pixpos(0), m_step(0) {
        for (int i = 0; i < pixs.size(); i++) {
            QPixmap temp(pixs.at(i)), t;
            t = temp.scaled(m_w, m_h, Qt::KeepAspectRatioByExpanding);
            m_pixs.append(t);
        }
        scene->addItem(this);
    }
    virtual ~Flyer() {}
    virtual int name() const = 0;
    virtual void posLost() = 0;
    virtual void doCollide() = 0;
    virtual void fall() = 0;
    bool checkPos(CHECK_FLAG flag)
    {
        bool ok = false;
        switch (flag) {
        case UP:
            if (scenePos().ry() >= -m_h) ok = true;
            break;
        case DOWN:
            if (scenePos().ry() <= SCENEHEIGHT) ok = true;
            break;
        case LEFT:
            if (scenePos().rx() >= -m_w) ok = true;
            break;
        case RIGHT:
            if (scenePos().rx() <= SCENEWIDTH) ok = true;
            break;
        }
        return ok;
    }
protected:
    qreal m_w, m_h, m_speed, m_pixpos;//物體大小 & 速度 & 圖檔位置 & 動畫控制
    uint m_step;//因為要取餘是以必須是int
    QPixmaps m_pixs;
};

#endif // FLYER_H      

飛機抽象類

#ifndef FLIGHTVEHICLE_H
#define FLIGHTVEHICLE_H

#include "flyer.h"
#include "unflyer.h"

class FlightVehicle : public Flyer
{
    Q_OBJECT
public:
    FlightVehicle(qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0)
        :Flyer(w, h, speed, pixs, scene, parent), m_blood(blood) {

    }
    virtual ~FlightVehicle() {}
    virtual void strike() = 0;
    virtual void shoot() = 0;
protected:
    qreal m_blood;//血量
signals:
    //向控制中心發送消息更改顯示, 注釋的那一塊崩了
    void sig_score(int score);
    //void sig_blood(int blood);
    //void sig_bomb(int bomb);
    void sig_fall();
};

#endif // FLIGHTVEHICLE_H      

子彈抽象類

#ifndef BULLET_H
#define BULLET_H

#include "flyer.h"
#include "unflyer.h"
#include <math.h>

#define ROTATE 57.26

class Bullet : public Flyer
{
public:
    Bullet(qreal angle,
           qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0)
        :Flyer(w, h, speed, pixs, scene, parent),
         m_angle(angle),
         xSpeed(::cos(m_angle / ROTATE) * m_speed),
         ySpeed(::sin(m_angle / ROTATE) * m_speed) {

    }
    virtual ~Bullet() {

    }
protected:
    qreal m_angle, xSpeed, ySpeed;
};

#endif // BULLET_H      

玩家飛機

#ifndef PLAYERPLANE_H
#define PLAYERPLANE_H

#include "flightvehicle.h"
#include "bulletfactory.h"

class PlayerPlane : public FlightVehicle
{
public:
    PlayerPlane(qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0);
    ~PlayerPlane();
    QRectF boundingRect() const;
    QPainterPath shape() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    int name() const;
    void advance(int);
    void posLost();
    void doCollide();
    void fall();
    void strike();
    void shoot();
protected:
    void keyPressEvent(QKeyEvent *event);
    void keyReleaseEvent(QKeyEvent *event);
private:
    qreal m_bomb, m_bullet;//必殺技 & 子彈形态
    bool W, A, S, D, bomb;
};

#endif // PLAYERPLANE_H      
#include "playerPlane.h"

PlayerPlane::PlayerPlane(qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent):
    FlightVehicle(blood, w, h, speed, pixs, scene, parent),
    m_bomb(3), m_bullet(0), W(false), A(false), S(false), D(false), bomb(false) {
    setPos((scene->width() - m_w)/2, scene->height() - m_h);
    setFlag(QGraphicsItem::ItemIsFocusable);
}

PlayerPlane::~PlayerPlane() {

}

QRectF PlayerPlane::boundingRect() const
{
    return m_pixs.at(0).rect();
}

QPainterPath PlayerPlane::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

void PlayerPlane::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->drawPixmap(0, 0, m_pixs.at(m_pixpos));
}

int PlayerPlane::name() const {
    return PLAYERPLANENAME;
}

void PlayerPlane::advance(int) {
    m_step++;
    if (m_step % PLAYERSHOOTSTEP == 0) {
        shoot();
    }
    if (m_step == PLAYERSHOOTSTEP * 10) m_step = 0;
    if (W && checkPos(UP)) {
        m_pixpos = 0;
        QPointF pos = scenePos();
        pos.ry() -= m_speed;
        setPos(pos);
    }
    if (S && checkPos(DOWN)) {
        m_pixpos = 1;
        QPointF pos = scenePos();
        pos.ry() += m_speed;
        setPos(pos);
    }
    if (A && checkPos(LEFT)) {
        m_pixpos = 2;
        QPointF pos = scenePos();
        pos.rx() -= m_speed;
        setPos(pos);
    }
    if (D && checkPos(RIGHT)) {
        m_pixpos = 3;
        QPointF pos = scenePos();
        pos.rx() += m_speed;
        setPos(pos);
    }
    if (!W && !S && !A && !D) m_pixpos = 0;
    doCollide();
}

void PlayerPlane::posLost() {

}

void PlayerPlane::doCollide()
{
    foreach (QGraphicsItem *t, collidingItems()) {
        if (t->type() != UnFlyer::TYPE) {
            Flyer *flyer = static_cast<Flyer*>(t);
            switch (flyer->name()) {
            case ENEMYPLANENAME:
                strike();
                if (m_blood == 0) {
                    m_bomb = 0;
                    m_bullet = 0;
                    fall();
                }
                break;
            case ENEMYBULLETNAME:
                flyer->fall();
                strike();
                if (m_blood == 0) {
                    m_bomb = 0;
                    m_bullet = 0;
                    fall();
                }
                break;
            case BLOODSUPPLYNAME:
                flyer->fall();
                if (m_blood < PLAYERPLANEBLOOD) {
                    m_blood++;
                    //emit sig_blood(m_blood);
                }
                break;
            case BOMBSUPPLYNAME:
                flyer->fall();
                if (m_bomb < PLAYERPLANEBOMB) {
                    m_bomb++;
                    //emit sig_bomb(m_bomb);
                }
                break;
            case BULLETSUPPLYNAME:
                flyer->fall();
                if (m_bullet < PLAYERPLANEBULLET) {
                    m_bullet++;
                }
                break;
            }
        }
    }
}

void PlayerPlane::fall()
{
    setFlag(QGraphicsItem::ItemIsMovable, false);
    setFlag(QGraphicsItem::ItemIsFocusScope, false);
    setVisible(false);
    deleteLater();
    emit sig_fall();
}

void PlayerPlane::strike() {
    //m_blood--;
    //emit sig_blood(m_blood);
}

void PlayerPlane::shoot()
{
    BulletFactory::PlayerBullets bullets = BulletFactory::pcreator(m_bullet, scene());
    QPointF pos = scenePos();
    pos.rx() += m_w / 2 - PLAYERBULLETSIZE / 2;
    foreach (PlayerBullet* bullet, bullets) {
        bullet->setPos(pos);
    }
    if (bomb && m_bomb > 0) {
        bomb = false;
        //m_bomb--;
        BulletFactory::Bombs bombs = BulletFactory::bcreator(1, scene());
        pos = scenePos();
        pos.rx() += m_w / 2 - BOMBSIZE / 2;
        pos.ry() -= BOMBSIZE / 2;
        foreach (Bomb* bomb, bombs) {
            bomb->setPos(SCENEWIDTH / 2 - BOMBSIZE / 2, SCENEHEIGHT - BOMBSIZE);
        }
    }
}

void PlayerPlane::keyPressEvent(QKeyEvent *event)
{
    switch (event->key()) {
    case Qt::Key_W:
        event->accept();
        W = true;
        break;
    case Qt::Key_S:
        event->accept();
        S = true;
        break;
    case Qt::Key_A:
        event->accept();
        A = true;
        break;
    case Qt::Key_D:
        event->accept();
        D = true;
        break;
    case Qt::Key_Space:
        event->accept();
        bomb = true;
        break;
    default:
        event->ignore();
        break;
    }
}

void PlayerPlane::keyReleaseEvent(QKeyEvent *event) {
    switch (event->key()) {
    case Qt::Key_W:
        event->accept();
        W = false;
        break;
    case Qt::Key_S:
        event->accept();
        S = false;
        break;
    case Qt::Key_A:
        event->accept();
        A = false;
        break;
    case Qt::Key_D:
        event->accept();
        D = false;
        break;
    case Qt::Key_Space:
        event->accept();
        bomb = false;
        break;
    default:
        event->ignore();
        break;
    }
}      

敵機

#ifndef ENEMYPLANE_H
#define ENEMYPLANE_H

#include "flightvehicle.h"
#include "bulletfactory.h"

class EnemyPlane : public FlightVehicle
{
public:
    EnemyPlane(qreal angle, qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0);
    ~EnemyPlane();
    QRectF boundingRect() const;
    QPainterPath shape() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    int name() const;
    void posLost();
    void strike();
protected:
    qreal m_angle, x_speed, y_speed;
};

#endif // ENEMYPLANE_H      
#include "enemyplane.h"

EnemyPlane::EnemyPlane(qreal angle, qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent):
    FlightVehicle(blood, w, h, speed, pixs, scene, parent), m_angle(angle),
    x_speed(::cos(m_angle / ROTATE) * m_speed),
    y_speed(::sin(m_angle / ROTATE) * m_speed) {

}

EnemyPlane::~EnemyPlane() {

}

QRectF EnemyPlane::boundingRect() const
{
    return m_pixs.at(0).rect();
}

QPainterPath EnemyPlane::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

void EnemyPlane::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->drawPixmap(0, 0, m_pixs.at(m_pixpos));
}

int EnemyPlane::name() const
{
    return ENEMYPLANENAME;
}

void EnemyPlane::posLost()
{
    setVisible(false);
    deleteLater();
}

void EnemyPlane::strike() {
    m_blood--;
}      

敵機——雜魚

#ifndef FISH_H
#define FISH_H

#include "enemyplane.h"
#include "randomizer.h"

class Fish : public EnemyPlane
{
public:
    Fish(qreal angle, qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0);
    ~Fish();
    void advance(int);
    void doCollide();
    void fall();
    void shoot();
};

#endif // FISH_H      
#include "fish.h"

Fish::Fish(qreal angle, qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent):
    EnemyPlane(angle, blood, w, h, speed, pixs, scene, parent) {

}

Fish::~Fish() {

}

void Fish::advance(int)
{
    m_step++;
    if (m_step % FISHSHOOTSTEP == 0) {
        shoot();
    }
    if (m_step == FISHSHOOTSTEP * 10) m_step = 0;
    QPointF pos = scenePos();
    if (!checkPos(DOWN) || !checkPos(LEFT) || !checkPos(RIGHT)) {
        posLost();
        return;
    }
    if (!checkPos(UP)) {
        y_speed = -y_speed;
    }
    pos.ry() += y_speed;
    pos.rx() += x_speed;
    setPos(pos);
    doCollide();
}

void Fish::doCollide()
{
    foreach (QGraphicsItem *t, collidingItems()) {
        if (t->type() != UnFlyer::TYPE) {
            Flyer *flyer = static_cast<Flyer*>(t);
            if (flyer->name() == PLAYERBULLETNAME) {
                flyer->fall();
                strike();
                if (m_blood == 0) fall();
            }
            if (flyer->name() == BOMBNAME) {
                m_blood = 0;
                fall();
            }
        }
    }
}

void Fish::fall()
{
    setVisible(false);
    deleteLater();
    emit sig_score(FISHSCORE);
}

void Fish::shoot()
{
    int temp = Randomizer::creat(3);
    if (temp == 0) temp = 3;
    BulletFactory::EnemyBullets bullets = BulletFactory::ecreator(temp, scene());
    QPointF pos = scenePos();
    pos.rx() += m_w - FISHBULLETSIZE1;
    pos.ry() += m_h;
    foreach (EnemyBullet* bullet, bullets) {
        bullet->setPos(pos);
    }
}      

敵機——BOSS

#ifndef BOSS_H
#define BOSS_H

#include "enemyplane.h"

class Boss : public EnemyPlane
{
public:
    Boss(qreal angle, qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0);
    ~Boss();
    void advance(int);
    void doCollide();
    void fall();
    void shoot();
private:
    int flag;//特殊控制boss圖檔變換
};

#endif // BOSS_H      
#include "boss.h"

Boss::Boss(qreal angle, qreal blood, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent):
    EnemyPlane(angle, blood, w, h, speed, pixs, scene, parent), flag(1) {

}

Boss::~Boss() {

}

void Boss::advance(int)
{
    m_step++;
    if (m_step % BOSSPIXSTEP == 0) {
        if (flag == 1 && m_pixpos == m_pixs.size() - 1) flag = -1;
        else if (flag == -1 && m_pixpos == 0) flag = 1;
        m_pixpos += flag;
        m_step = 0;
    }
    if (m_step % BOSSSHOOTSTEP == 0) {
        shoot();
    }
    if (m_step == BOSSSHOOTSTEP * 1000) m_step = 0;
    QPointF pos = scenePos();
    if (!checkPos(UP) || !checkPos(DOWN)) {
        y_speed = -y_speed;
    }
    if (!checkPos(LEFT) || !checkPos(RIGHT)) {
        x_speed = -x_speed;
    }
    pos.ry() += y_speed;
    pos.rx() += x_speed;
    setPos(pos);
    doCollide();
}

void Boss::doCollide()//Boss免疫大招
{
    foreach (QGraphicsItem *t, collidingItems()) {
        if (t->type() != UnFlyer::TYPE) {
            Flyer *flyer = static_cast<Flyer*>(t);
            if (flyer->name() == PLAYERBULLETNAME) {
                flyer->fall();
                strike();
                if (m_blood == 0) fall();
            }
        }
    }
}

void Boss::fall()
{
    setVisible(false);
    deleteLater();
    emit sig_score(BOSSSCORE);
}

void Boss::shoot()
{
    int temp;//這裡temp的值應該在工廠中有宏定義,進而增強可讀性,懶得寫了
    if (1.0 * BOSSHP * 3 / 4 < m_blood && m_blood <= BOSSHP) temp = 4;
    else if (1.0 * BOSSHP * 2 / 4 < m_blood && m_blood <= 1.0 * BOSSHP * 3 / 4) temp = 5;
    else if (1.0 * BOSSHP * 1 / 4 < m_blood && m_blood <= 1.0 * BOSSHP * 2 / 4) temp = 6;
    else if (0 < m_blood && m_blood <= 1.0 * BOSSHP * 1 / 4) temp = 7;
    BulletFactory::EnemyBullets bullets = BulletFactory::ecreator(temp, scene());
    QPointF pos = scenePos();
    int bulletsize = BOSSBULLETSIZE1;
    if (temp == 5 || temp == 6 || temp == 7) bulletsize = BOSSSIZE / 2;
    pos.rx() += m_w / 2 - bulletsize / 2;
    pos.ry() += m_h / 2;
    foreach (EnemyBullet* bullet, bullets) {
        bullet->setPos(pos);
    }
}      

玩家子彈

#ifndef PLAYERBULLET_H
#define PLAYERBULLET_H

#include "bullet.h"

class PlayerBullet : public Bullet
{
public:
    PlayerBullet(qreal angle, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0);
    ~PlayerBullet();
    QRectF boundingRect() const;
    QPainterPath shape() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    int name() const;
    void advance(int);
    void posLost();
    void doCollide();
    void fall();
};

#endif // PLAYERBULLET_H      
#include "playerbullet.h"

PlayerBullet::PlayerBullet(qreal angle, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent)
    :Bullet(angle, w, h, speed, pixs, scene, parent) {

}

PlayerBullet::~PlayerBullet() {

}

QRectF PlayerBullet::boundingRect() const
{
    return m_pixs.at(0).rect();
}

QPainterPath PlayerBullet::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

void PlayerBullet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->drawPixmap(0, 0, m_pixs.at(0));
}

int PlayerBullet::name() const
{
    return PLAYERBULLETNAME;
}

void PlayerBullet::advance(int)
{
    if (!checkPos(UP) || !checkPos(DOWN) || !checkPos(LEFT) || !checkPos(RIGHT)) {
        posLost();
        return;
    }
    QPointF pos = scenePos();
    pos.rx() -= xSpeed;
    pos.ry() -= ySpeed;
    setPos(pos);
}

void PlayerBullet::posLost() {
    setVisible(false);
    deleteLater();
}

void PlayerBullet::doCollide() {

}

void PlayerBullet::fall() {
    setVisible(false);
    deleteLater();
}      

敵機子彈

#ifndef ENEMYBULLET_H
#define ENEMYBULLET_H

#include "bullet.h"
#include <math.h>

#define ROTATE 57.26

class EnemyBullet : public Bullet
{
public:
    EnemyBullet(qreal angle, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent = 0);
    ~EnemyBullet();
    QRectF boundingRect() const;
    QPainterPath shape() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
    int name() const;
    void posLost();
    void advance(int);
    void doCollide();
    void fall();
};

#endif // ENEMYBULLET_H      
#include "enemybullet.h"

EnemyBullet::EnemyBullet(qreal angle, qreal w, qreal h, qreal speed, const QPixmaps &pixs, QGraphicsScene *scene, QGraphicsItem *parent)
    :Bullet(angle, w, h, speed, pixs, scene, parent){

}

EnemyBullet::~EnemyBullet() {

}

QRectF EnemyBullet::boundingRect() const
{
    return m_pixs.at(0).rect();
}

QPainterPath EnemyBullet::shape() const
{
    QPainterPath path;
    path.addRect(boundingRect());
    return path;
}

void EnemyBullet::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(option);
    Q_UNUSED(widget);
    painter->drawPixmap(0, 0, m_pixs.at(0));
}

int EnemyBullet::name() const
{
    return ENEMYBULLETNAME;
}

void EnemyBullet::advance(int)
{
    if (!checkPos(UP) || !checkPos(DOWN) || !checkPos(LEFT) || !checkPos(RIGHT)) {
        posLost();
        return;
    }
    QPointF pos = scenePos();
    pos.rx() += xSpeed;
    pos.ry() += ySpeed;
    setPos(pos);
}

void EnemyBullet::posLost() {
    setVisible(false);
    deleteLater();
}

void EnemyBullet::doCollide() {

}

void EnemyBullet::fall() {
    setVisible(false);
    deleteLater();
}      

​飛機工廠:​

#ifndef PLANEFACTORY_H
#define PLANEFACTORY_H

#include "boss.h"
#include "fish.h"
#include "randomizer.h"

class PlaneFactory
{
public:
    typedef QList<Boss*> BossPlanes;
    typedef QList<Fish*> FishPlanes;
    static BossPlanes bcreator(uint flag, QGraphicsScene *scene);
    static FishPlanes fcreator(uint flag, QGraphicsScene *scene);
};

#endif // PLANEFACTORY_H      
#include "planefactory.h"

PlaneFactory::BossPlanes PlaneFactory::bcreator(uint flag, QGraphicsScene *scene)
{
    BossPlanes temp;
    switch (flag) {
    case 1:
        QPixmaps t;
        t.append(QPixmap(boss0));
        t.append(QPixmap(boss1));
        t.append(QPixmap(boss2));
        t.append(QPixmap(boss3));
        int angle = Randomizer::creat(180);
        temp.append(new Boss(angle, BOSSHP, BOSSSIZE, BOSSSIZE, BOSSSPEED, t, scene));
    }
    return temp;
}

PlaneFactory::FishPlanes PlaneFactory::fcreator(uint flag, QGraphicsScene *scene)
{
    FishPlanes temp;
    for (uint i = 0; i < flag; i++) {
        QPixmaps t;
        int x = Randomizer::creat(3);
        if (x == 1) t.append(QPixmap(fish0));
        else if (x == 2) t.append(QPixmap(fish1));
        else if (x == 0) t.append(QPixmap(fish2));
        int angle = Randomizer::creat(180);
        temp.append(new Fish(angle, FISHHP, FISHSIZE, FISHSIZE, FISHSPEED, t, scene));
    }
    return temp;
}      

子彈工廠:

#ifndef BULLETFACTORY_H
#define BULLETFACTORY_H

#include "bomb.h"
#include "playerbullet.h"
#include "enemybullet.h"

class BulletFactory
{
public:
    typedef QList<Bomb*> Bombs;
    typedef QList<PlayerBullet*> PlayerBullets;
    typedef QList<EnemyBullet*> EnemyBullets;
    static Bombs bcreator(uint flag, QGraphicsScene *scene);
    static PlayerBullets pcreator(uint flag, QGraphicsScene *scene);
    static EnemyBullets ecreator(uint flag, QGraphicsScene *scene);
};

#endif // BULLETFACTORY_H      
#include "bulletfactory.h"

BulletFactory::Bombs BulletFactory::bcreator(uint flag, QGraphicsScene *scene)
{
    Bombs temp;
    switch (flag) {
    case 0:
        break;
    case 1:
        QPixmaps t;
        t.append(QPixmap(bomb0));
        t.append(QPixmap(bomb1));
        temp.append(new Bomb(90, BOMBSIZE, BOMBSIZE, BOMBSPEED, t, scene));
        break;
    //可以繼續添加,實作各種大招
    }
    return temp;
}

BulletFactory::PlayerBullets BulletFactory::pcreator(uint flag, QGraphicsScene *scene)
{
    PlayerBullets temp;
    if (flag == 0) {//作弊模式
        QPixmaps t;
        t.append(QPixmap(playerbullet0));
        for (int i = 0; i < 12; i++) {
            temp.append(new PlayerBullet(0 + 30 * i, PLAYERBULLETSIZE, PLAYERBULLETSIZE, PLAYERBULLETSPEED, t, scene));
        }
    }
    if (flag >= 1) {
        QPixmaps t;
        t.append(QPixmap(playerbullet0));
        temp.append(new PlayerBullet(90, PLAYERBULLETSIZE, PLAYERBULLETSIZE, PLAYERBULLETSPEED, t, scene));
    }
    if (flag >= 2) {
        QPixmaps t;
        t.append(QPixmap(playerbullet1));
        for (int i = 0; i < 2; i++) {
            temp.append(new PlayerBullet(80 + 20 * i, PLAYERBULLETSIZE, PLAYERBULLETSIZE, PLAYERBULLETSPEED, t, scene));
        }
    }
    if (flag >= 3) {
        QPixmaps t;
        t.append(QPixmap(playerbullet2));
        for (int i = 0; i < 2; i++) {
            temp.append(new PlayerBullet(40 + 20 * i, PLAYERBULLETSIZE, PLAYERBULLETSIZE, PLAYERBULLETSPEED, t, scene));
        }
        for (int i = 0; i < 2; i++) {
            temp.append(new PlayerBullet(120 + 20 * i, PLAYERBULLETSIZE, PLAYERBULLETSIZE, PLAYERBULLETSPEED, t, scene));
        }
    }
    //可以繼續添加,讓玩家變得更變态
    return temp;
}

BulletFactory::EnemyBullets BulletFactory::ecreator(uint flag, QGraphicsScene *scene)
{
    EnemyBullets temp;
    QPixmaps t, t1, t2;
    switch (flag) {
    case 0:
        break;
    case 1:
        t.append(QPixmap(fishbullet0));
        temp.append(new EnemyBullet(90, FISHBULLETSIZE1, FISHBULLETSIZE1, FISHBULLETSPEED1, t, scene));
        break;
    case 2:
        t.append(QPixmap(fishbullet1));
        for (int i = 0; i < 3; i++) {
            temp.append(new EnemyBullet(80 + 10 * i, FISHBULLETSIZE2, FISHBULLETSIZE2, FISHBULLETSPEED2, t, scene));
        }
        break;
    case 3:
        t.append(QPixmap(fishbullet2));
        for (int i = 0; i < 4; i++) {
            temp.append(new EnemyBullet(0 + 90 * i, FISHBULLETSIZE3, FISHBULLETSIZE3, FISHBULLETSPEED3, t, scene));
        }
        break;
    case 4:
        t.append(QPixmap(bossbullet0));
        for (int i = 0; i < 8; i++) {
            temp.append(new EnemyBullet(0 + 45 * i, BOSSBULLETSIZE1, BOSSBULLETSIZE1, BOSSBULLETSPEED1, t, scene));
        }
        break;
    case 5:
        t1.append(QPixmap(bossbullet0));
        for (int i = 0; i < 12; i++) {
            temp.append(new EnemyBullet(0 + 30 * i, BOSSBULLETSIZE1, BOSSBULLETSIZE1, BOSSBULLETSPEED1, t1, scene));
        }
        t2.append(QPixmap(bossbullet1));
        temp.append(new EnemyBullet(90, BOSSBULLETWIDTH2, BOSSBULLETHEIGHT2, BOSSBULLETSPEED2, t2, scene));
        break;
    case 6:
        t1.append(QPixmap(bossbullet0));
        for (int i = 0; i < 12; i++) {
            temp.append(new EnemyBullet(0 + 30 * i, BOSSBULLETSIZE1, BOSSBULLETSIZE1, BOSSBULLETSPEED1, t1, scene));
        }
        t2.append(QPixmap(bossbullet2));
        temp.append(new EnemyBullet(90, BOSSBULLETSIZE3, BOSSBULLETSIZE3, BOSSBULLETSPEED3, t2, scene));
        break;
    case 7:
        t1.append(QPixmap(bossbullet0));
        for (int i = 0; i < 36; i++) {
            temp.append(new EnemyBullet(0 + 10 * i, BOSSBULLETSIZE1, BOSSBULLETSIZE1, BOSSBULLETSPEED1, t1, scene));
        }
        t2.append(QPixmap(bossbullet2));
        temp.append(new EnemyBullet(90, BOSSBULLETSIZE3, BOSSBULLETSIZE3, BOSSBULLETSPEED3, t2, scene));
        break;
    //推薦繼續添加子彈,尤其是可以根據時間變化的子彈(用m_step控制)
    }
    return temp;
}      

繼續閱讀