效果图:

Qt游戏编程——飞机大战_游戏编程

Qt游戏编程——飞机大战_源码_02


这篇文章记录了我用一周的时间从零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;
}