效果圖:
這篇文章記錄了我用一周的時間從零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;
}