目录

1.项目简介

2.创建项目

3.设置主场景导入资源

3.1配置文件

3.2主场景的初始化

3.3资源导入

4.地图滚动的实现

4.1创建地图文件和类

4.2添加地图类的成员函数和成员属性

5.创建我们的飞机

5.1创建我们的飞机文件和类

5.2添加成员函数和成员属性

6.子弹的绘制

6.1创建飞机子弹文件和类

6.2添加子弹成员函数和成员属性

6.3成员函数的实现

7.敌方飞机的绘制

7.1添加敌方飞机文件和类

7.2添加成员函数和成员属性

7.3成员函数的实现

8.boss类的制作

8.1 boss类文件的添加

8.2 添加成员函数和成员属性

8.3 成员函数的实现

9.boss子弹类的制作

10.碰撞检测

11.爆炸类的制作

11.1 创建爆炸文件和类

11.2 添加爆炸成员和成员属性

11.3 实现成员函数


1.项目简介

飞机大战相信大家都曾玩过,今天我们来制作一款我们自己的飞机大战,鼠标控制我们自己的飞机移动发射子弹,消灭上方的敌机和boss,我先把我们最终的效果给大家看一下

11

基础的功能基本都实现了,后续还可以加入更多的玩法进行升级,比如给飞机升级,添加关卡,以及不同的boss战等。

从视频中可以看到我们的一些需要实现的功能:

1.我们的地图背景是滚动的

2.飞机的绘制和控制移动

3.子弹的绘制和发射

4.敌机的绘制

5.碰撞检测,通过检测我们图片方框是否相交进行碰撞检测来判断子弹是否击中或者自己是否被击毁

6.坠毁的爆炸效果

2.创建项目

打开QT创建我们的项目,主窗口基类我们选择QWidget,我们的主场景我们取名mainsence,之后的游戏场景功能等实现都要在这个窗口进行,这里博主没有创建开始游戏的界面,直接就进入游戏,可以再创建一个主窗口游戏开始界面,通过点击开始按钮进入到游戏中。

3.设置主场景导入资源

我们需要先对游戏需要配置的资源,和界面大小,游戏图标进行设置

3.1配置文件

为了之后的程序设计方便我们先创建了一个配置数据头文件config.h来保存资源配置数据,方便后期进行修改,先将游戏窗口的大小进行设置,这里注意窗口大小取决于你选择的地图图片大小

/*****游戏配置信息******/
#define GAME_HEIGHT 768//游戏界面高度
#define GAME_WIDTH 512 //游戏界面宽度
#define GAME_TITLE "雷霆战机1.0" //游戏标题

3.2主场景的初始化

在我们的主场景类中添加一个初始化界面的成员函数initsence来初始化我们的游戏场景

void initscene();//场景初始化函数

(alt键+回车键可以快速在mainsence.cpp文件中进行该成员函数的定义)在cpp文件中我们进行该成员函数的实现

void mainsence::initscene()
{//固定游戏窗口大小setFixedSize(GAME_WIDTH,GAME_HEIGHT);//设置窗口标题setWindowTitle(GAME_TITLE);//加载图标setWindowIcon(QIcon(GAME_ICON));//定时器的设置m_Timer.setInterval(GAME_RATE);//敌机出场时间间隔初始化m_recorder=0;//boss出场时间初始化boss_recorder=0;//随机数种子srand((unsigned int)time(NULL));//加载游戏结束图片gameover.load(GAME_OVER);}

随后在构造函数中我们调用该成员函数进行界面的初始化:

mainsence::mainsence(QWidget *parent): QWidget(parent)
{initscene();//初始化场景playGame();//启动游戏
}

3.3资源导入

由于我们游戏中需要用到许多的图片和音频资源,所以我们需要对这些资源进行整理并放在我们的工程文件中。我们右键点击工程添加新文件选择下图中我们用箭头标注的选项

过后会生成一个qrc文件,我们点击编辑将我们的资源全部添加进去,但如果资源过大,我们可以将qrc文件变成rcc二进制文件这会极大减少资源的所占的内存大小。

我们在资源所在文件夹打开cmd命令窗口:输入以下指令将qrc文件转换成rcc文件,这样一可以极大减少资源所占空间还可以防止我们游戏内的图片资源被人随意替换!!

随后我们需要在配置文件config.h中加入我们的rcc资源路径

#define GAME_RES_PATH "./plane.rcc" //rcc文件路径
#define GAME_ICON ":/res/app_ico" //图标加载路径

并且需要我们在主函数main.c中加载我们的rcc文件:

int main(int argc, char *argv[])
{QApplication a(argc, argv);QResource::registerResource("./plane.rcc");mainsence w;w.show();return a.exec();
}

4.地图滚动的实现

4.1创建地图文件和类

点击我们的工程文件右键添加新文件,将我们的地图类取名为map

4.2添加地图类的成员函数和成员属性

我们要如何实现地图的滚动效果呢,首先我们需要两张一样的地图背景图,将他们拼接起来,将地图Y轴坐标进行改变实现滚动效果,就如下面这样背景图两张拼接起来,滚动起来不会有割裂感

 map.h

class Map
{
public://构造函数Map();//地图滚动坐标计算void mapPosition();//地图图片对象QPixmap m_map1;QPixmap m_map2;//地图Y轴坐标int m_map1_posY;int m_map2_posY;//地图滚动速度int m_scroll_speed;};

因为需要实现滚动效果所以我们需要一段时间对地图进行重新绘制,所以成员函数中我们设计了一个更新地图滚动坐标计算的函数,同时我们需要将地图的配置信息添加到config.h中

/*****地图配置路径******/
#define MAP_PATH ":/res/img_bg_level_1" //地图1路径
#define MAP_SCROLL_SPEED 2//地图滚动速度

我们在map.cpp文件中实现该成员函数:

#include "map.h"
#include "config.h"
Map::Map()
{//初始化加载地图对象m_map1.load(MAP_PATH);m_map2.load(MAP_PATH);//初始化Y轴坐标m_map1_posY=-GAME_HEIGHT;m_map2_posY=0;//地图滚动速度m_scroll_speed=MAP_SCROLL_SPEED;}void Map:: mapPosition()
{//处理第一张图片的滚动位置m_map1_posY+=MAP_SCROLL_SPEED;if(m_map1_posY>=0){m_map1_posY=-GAME_HEIGHT;}//处理第二章图片的滚动位置m_map2_posY+=MAP_SCROLL_SPEED;if(m_map2_posY>=GAME_HEIGHT){m_map2_posY=0;}}

需要注意的是两张地图在滚动过程中如果第一张图的Y轴坐标为0,第二张图Y轴坐标已经越过游戏窗口界面的高度,那么我们需要对两张图的坐标进行重置,来达到循环播放滚动的效果。同时我们需要在主界面类中添加一个定时器对象来决定我们主窗口的刷新时间

QTimer m_Timer;//定时器1

并且我们需要重写绘图事件来绘制我们需要的地图资源

  void mainsence::paintEvent(QPaintEvent *)
{QPainter painter(this);painter.drawPixmap(0,m_map.m_map1_posY,m_map.m_map1);painter.drawPixmap(0,m_map.m_map2_posY,m_map.m_map2);
}

mainscence.h

#ifndef MAINSENCE_H
#define MAINSENCE_H
#include <QTimer>
#include <QWidget>
#include <map.h>
#include <heroPlane.h>
#include <QMouseEvent>
#include <bullet.h>
#include <enemyplane.h>
#include <boss.h>
#include <bomb.h>
#include <QMediaPlayer>
class mainsence : public QWidget
{Q_OBJECTpublic:mainsence(QWidget *parent = 0);~mainsence();void initscene();//场景初始化函数void playGame();//启动游戏void updatePosition();//更新游戏资源坐标void paintEvent(QPaintEvent *);//绘制资源到屏幕void mouseMoveEvent(QMouseEvent*);//重写鼠标移动事件方便飞机移动void enemyToScene();//敌机小兵出场void collisionDetection();//碰撞检测void BossToScene();//BOSS出场void playmusic();//播放音乐bomb m_bombs[BOMB_NUM];//爆炸数组QTimer m_Timer;//定时器1Map m_map;//地图对象heroPlane m_hero;//飞机对象boss m_boss;//boss对象enemyPlane m_enemys[ENEMY_NUM];//敌机对象数组int m_recorder;//敌机出场间隔记录int boss_recorder;//boss出场记录时间QPixmap gameover;//结束游戏对象};#endif // MAINSENCE_H

加载了资源后我们可以在主界面类的成员函数playgame中来实现游戏的启动:当定时器信号达到我们就会进行一次资源的刷新,包括敌人的数量位置,我方的飞机子弹位置,是否有飞机坠毁等等

void mainsence::playGame()
{//启动背景音乐m_Timer.start();//启动定时器//监听定时器connect(&m_Timer,&QTimer::timeout,[=](){//敌机出场enemyToScene();//boss出场BossToScene();//更新游戏中元素坐标updatePosition();//绘制到屏幕update();//碰撞检测collisionDetection();});}

5.创建我们的飞机

5.1创建我们的飞机文件和类

还是一样我们需要在工程文件中添加新文件,也就是我们的飞机文件类,然后将我们的飞机配置资源添加到config.h头文件中,方便我们进行修改

/*****飞机配置资源*******/
#define HERO_PATH ":/res/hero_3"//飞机模型路径

5.2添加成员函数和成员属性

随后我们需要在我们的飞机类中添加我们的成员函数和成员属性,由于我们的飞机需要发射子弹,并且需要移动,需要一个参数来记录飞机当前状态,如果被击毁则将状态变为空闲,以免绘图事件在飞机坠毁后继续绘制

heroplane.h

class heroPlane
{
public:heroPlane();//发射子弹函数void shoot();//更改飞机位置的函数void setPosition(int x,int y);//飞机资源对象QPixmap m_Plane;//飞机坐标位置int m_x;int m_y;//飞机边框方便检测碰撞事件QRect m_Rect;//弹匣bullet m_bullets[BULLET_NUM];//发射冷却时间int m_recorder;//飞机状态bool m_Free;};

在cpp文件中进行初始化和成员函数的实现

heroplane.cpp

#include "heroplane.h"
#include "config.h"
#include "bullet.h"
heroPlane::heroPlane()
{//加载飞机模型资源m_Plane.load(HERO_PATH);//初始化飞机初始位置m_x=(GAME_WIDTH-m_Plane.width())/2;m_y=GAME_HEIGHT-m_Plane.height();//初始化边框m_Rect.setWidth(m_Plane.width());m_Rect.setHeight(m_Plane.height());m_Rect.moveTo(m_x,m_y);//初始化飞机状态m_Free=false;//表示飞机正在运作,如果后面被击中则变成闲置状态m_recorder=0;
}
void heroPlane::shoot()
{//累加时间间隔记录变量m_recorder++;//如果记录的数字,未达到发射间隔时间则不发射子弹if(m_recorder<BULLET_INTERVAL){return ;}//达到发射时间,重置时间以便下次发射子弹m_recorder=0;//发射子弹for(int i=0;i<BULLET_NUM;i++){//如果该子弹为空闲状态则发射if(m_bullets[i].m_Free){//将空闲状态改为假m_bullets[i].m_Free=false;//设置子弹坐标m_bullets[i].m_x=m_x+m_Rect.width()*0.5-40 ;m_bullets[i].m_y=m_y;break;}}}
//更新飞机位置
void heroPlane::setPosition(int x, int y)
{m_x=x;m_y=y;m_Rect.moveTo(m_x,m_y);
}

随后我们也需要在绘图事件中添加绘制飞机:前提条件是飞机不处于闲置状态

void mainsence::paintEvent(QPaintEvent *)
{QPainter painter(this);
//绘制飞机if(m_hero.m_Free==false){painter.drawPixmap(m_hero.m_x,m_hero.m_y,m_hero.m_Plane);}//飞机被击毁游戏结束绘制游戏结束else{painter.drawPixmap(GAME_WIDTH/2-100,GAME_HEIGHT/2-20,gameover);}
}

当然我们需要用鼠标控制飞机的移动那么就需要我们对鼠标移动事件进行重写,并且我们的飞机不能飞出游戏窗口外所以还需要进行边界检测来将飞机移动的位置固定下来:

void mainsence::mouseMoveEvent(QMouseEvent *event)
{int x=event->x()-m_hero.m_Rect.width()/2;int y=event->y()-m_hero.m_Rect.height()/2;//边界检测以免飞机飞出边框if(x<=0){x=0;}if(x>=GAME_WIDTH-m_hero.m_Rect.width()){x=GAME_WIDTH-m_hero.m_Rect.width();}if(y>=GAME_HEIGHT-m_hero.m_Rect.height()){y=GAME_HEIGHT-m_hero.m_Rect.height();}if(y<=0){y=0;}//设置飞机当前位置m_hero.setPosition(x,y);}

6.子弹的绘制

6.1创建飞机子弹文件和类

首先和前面一样我们需要将子弹的文件类添加到工程文件中 ,随后将子弹的配置资源添加到config.h头文件中方便使用


/*****飞机子弹资源配置*******/
#define BULLET_PATH ":/res/bullet_5" //子弹图片路径
#define BULLET_SPEED 10 //子弹移动速度#define BULLET_NUM 50 //子弹总数
#define BULLET_INTERVAL 20 //发射子弹间隔

6.2添加子弹成员函数和成员属性

bullet.h

#include <QPixmap>
#include <QRect>
class bullet
{
public:bullet();//子弹坐标void updatePosition();//子弹资源对象QPixmap m_Bullet;//子弹坐标int m_x;int m_y;//子弹速度int m_speed;//子弹是否闲置,飞出游戏界面就要回收bool m_Free;//子弹的边框方便进行碰撞检测QRect m_Rect;
};

6.3成员函数的实现

bullet.cpp

#include "bullet.h"
#include "map.h"
#include "config.h"
#include "QPixmap"bullet::bullet()
{//加载子弹资源m_Bullet.load(BULLET_PATH);//子弹坐标m_x=GAME_WIDTH*0.5 - m_Bullet.width() *0.5;m_y=GAME_HEIGHT;//子弹状态m_Free=true;//子弹速度m_speed=BULLET_SPEED;//子弹边框m_Rect.setWidth(m_Bullet.width());m_Rect.setHeight(m_Bullet.height());m_Rect.moveTo(m_x,m_y);}void bullet::updatePosition()
{//判断子弹状态,子弹空闲不需要计算坐标if(m_Free){return;}//子弹不处于空闲,子弹要向上移动m_y-=m_speed;m_Rect.moveTo(m_x,m_y);//子弹位置超出屏幕,需要把子弹状态变成空闲进行回收if(m_y<=-m_Rect.height())m_Free=true;}
}

为了让子弹有顺序的发射我们给子弹添加了状态属性,当子弹发射间隔时间到了我们就将子弹组中空闲的子弹置为运行,然后利用绘图事件将其绘制到屏幕,并根据她坐标的实时变化来完成子弹的发射移动,当子弹飞出边框或者发生碰撞时我们就需要将其置为空闲状态,来达到子弹的循环使用,在飞机的类中heroplane.cpp我们已经实现了子弹发射的成员函数。

绘制子弹:

void mainsence::paintEvent(QPaintEvent *)
{QPainter painter(this);//绘制子弹,只有在飞机是运行状态才需要绘制子弹if(m_hero.m_Free==false){for(int i=0;i<BULLET_NUM;i++){if(m_hero.m_bullets[i].m_Free==false){painter.drawPixmap(m_hero.m_bullets[i].m_x,m_hero.m_bullets[i].m_y,m_hero.m_bullets[i].m_Bullet);}}}
}

7.敌方飞机的绘制

7.1添加敌方飞机文件和类

首先我们需要在工程文件中添加敌方飞机的类,并将配置资源放到config.h头文件中

/*****敌机配置资源*****/
#define ENEMY_PATH ":/res/img-plane_1" //敌机资源路径
#define ENEMY_SPEED 5 //敌机移动速度
#define ENEMY_NUM 30 //敌机数量
#define ENEMY_INTERVAL 30 //敌机出现间隔

7.2添加成员函数和成员属性

enemyplane.h

#include <QPixmap>
#include <QRect>
class enemyPlane
{
public:enemyPlane();//更新敌机坐标void updatePosition();//敌机对象QPixmap m_enemy;//敌机坐标位置int m_x;int m_y;//敌机的边框,便于碰撞检测QRect m_Rect;//状态bool m_Free;//敌机速度int m_speed;};

同样我们需要实时获取敌机的坐标来完成敌机的绘制,以及敌机的状态,如果敌机与子弹发生碰撞则要将其状态设置为空闲,这样来达成我们敌机资源的循环使用

7.3成员函数的实现

enemyplane.cpp

#include "enemyplane.h"
#include "config.h"
enemyPlane::enemyPlane()
{//加载敌机资源m_enemy.load(ENEMY_PATH);//敌机初始位置m_x=0;m_y=0;//敌机状态m_Free=true;//敌机速度m_speed=ENEMY_SPEED;//敌机边框m_Rect.setWidth(m_enemy.width());m_Rect.setHeight(m_enemy.height());m_Rect.moveTo(m_x,m_y);
}
//更新敌机坐标
void enemyPlane::updatePosition()
{//空闲状态的敌机不需要计算坐标if(m_Free)return ;m_y+=m_speed;m_Rect.moveTo(m_x,m_y);//如果超出屏幕 重置空闲状态if(m_y>=GAME_HEIGHT+m_Rect.height())m_Free=true;
}

敌机的出场也需要我们成员函数来实现所以我们需要在主界面类中进行敌机出场的函数实现来控制敌人的出现间隔:

//敌机出场
void mainsence::enemyToScene()
{m_recorder++;//未到出场时间if(m_recorder<ENEMY_INTERVAL)return ;//达到出场时间,重置记录m_recorder=0;for(int i=0;i<ENEMY_NUM;i++){if(m_enemys[i].m_Free){m_enemys[i].m_Free=false;m_enemys[i].m_x=rand()%(GAME_WIDTH-m_enemys[i].m_Rect.width());m_enemys[i].m_y=-m_enemys[i].m_Rect.height();break;}}}

8.boss类的制作

8.1 boss类文件的添加

首先我们添加一个 boss类的文件到工程文件中,并将boss的配置资源加入到config.h头文件中

/******BOSS资源配置********/
#define BOSS_PATH ":/res/boss_1"  //boss飞机模型路径
#define BOSS_BULLET_PATH ":/res/bulletboss_2"  //boss子弹图片路径
#define BOSS_BULLET_NUM 100  //boss子弹数量
#define BOSS_BULLET_SPEED 2  //boss子弹速度
#define BOSS_INTERVAL 1000 //BOSS出现时间
#define BOSS_BULLET_INTERVAL 50 //boss发射子弹间隔

8.2 添加成员函数和成员属性

boss.h

#include "config.h"
#include <QPixmap>
#include <QRect>
#include <bossbullet.h>
class boss
{
public:boss();//发射子弹函数void shoot();//更改BOSS位置的函数//void setPosition(int x,int y);//BOSS资源对象QPixmap m_Bossplane;//BOSS坐标位置int m_x;int m_y;//BOSS边框方便检测碰撞事件QRect m_Rect;//boss子弹发射冷却时间int m_recorder;//boss状态bool m_Free;//boss血量int blood;//boss子弹弹匣bossbullet boss_bullets[BOSS_BULLET_NUM];
};

这里我设置了boss的坐标,状态,血量等,当血量为0时将boss状态设置为闲置等待下次出场,boss的位置我将其固定在了上方。

8.3 成员函数的实现

#include "boss.h"
#include "config.h"
#include <QDebug>
boss::boss()
{//加载BOSS模型资源m_Bossplane.load(BOSS_PATH);//初始化BOSS初始位置m_x=(GAME_WIDTH-m_Bossplane.width())/2;m_y=0;//初始化边框m_Rect.setWidth(m_Bossplane.width());m_Rect.setHeight(m_Bossplane.height());// m_Rect.moveTo(m_x,m_y);//初始化飞机状态m_Free=true;//初始化血量blood=100;m_recorder=0;}void boss::shoot()
{//累加时间间隔记录变量m_recorder++;//如果记录的数字,未达到发射间隔时间则不发射子弹if(m_recorder<BOSS_BULLET_INTERVAL){return ;}//达到发射时间,重置时间以便下次发射子弹m_recorder=0;//发射子弹for(int i=0;i<BOSS_BULLET_NUM;i++){//如果该子弹为空闲状态则发射if(boss_bullets[i].m_Free){qDebug()<<"4";//将空闲状态改为假boss_bullets[i].m_Free=false;break;}}
}

boss子弹的发射和我方飞机子弹发射逻辑相同,当发射冷却时间到便将其中一个空闲的子弹置为运作状态。

boss的绘制同样在绘图事件中进行,所以我们需要在主界面添加一个boss出场的成员函数BossTosence

void mainsence::BossToScene()
{boss_recorder++;//未到出场时间if(boss_recorder<BOSS_INTERVAL)return ;//达到出场时间boss_recorder=0;m_boss.m_Free=false;m_boss.blood=1000;}

当出场时间达到则在绘图事件中进行绘制:

void mainsence::paintEvent(QPaintEvent *)
{QPainter painter(this);
//绘制bossif(m_boss.m_Free==false)painter.drawPixmap(m_boss.m_x,m_boss.m_y,m_boss.m_Bossplane);
}

9.boss子弹类的制作

boss子弹类制作和我方飞机步骤相同

bossbullet.h

class bossbullet
{
public:bossbullet();//boss子弹坐标void updatePosition();//子弹资源对象QPixmap boss_Bullet;//子弹坐标int m_x;int m_y;//子弹速度int m_speed;//子弹是否闲置,飞出游戏界面就要回收bool m_Free;//子弹的边框方便进行碰撞检测QRect m_Rect;
};

bossbullet.cpp

#include "bossbullet.h"
#include "map.h"
#include "config.h"
#include "QPixmap"
#include "boss.h"
bossbullet::bossbullet()
{//加载子弹资源boss_Bullet.load(BOSS_BULLET_PATH);//子弹坐标m_x=GAME_WIDTH*0.5-boss_Bullet.width()*0.5 ;m_y=GAME_HEIGHT*0.5-60;//子弹状态m_Free=true;//子弹速度m_speed=BOSS_BULLET_SPEED;//子弹边框m_Rect.setWidth(boss_Bullet.width());m_Rect.setHeight(boss_Bullet.height());m_Rect.moveTo(m_x,m_y);
}void bossbullet::updatePosition()
{//判断子弹状态,子弹空闲不需要计算坐标if(m_Free){return;}//子弹不处于空闲,子弹要向下移动m_y+=m_speed;m_Rect.moveTo(m_x,m_y);//子弹位置超出屏幕,需要把子弹状态变成空闲进行回收if(m_y>=GAME_HEIGHT)m_Free=true;
}

同样在子弹状态为运作时我们需要在绘图事件中进行绘制:

void mainsence::paintEvent(QPaintEvent *)
{//绘制boss子弹if(m_boss.m_Free==false){qDebug()<<"1";for(int i=0;i<BOSS_BULLET_NUM;i++){//qDebug()<<m_boss.boss_bullets[i].m_Free;if(m_boss.boss_bullets[i].m_Free==false){//qDebug()<<"3";painter.drawPixmap(m_boss.boss_bullets[i].m_x,m_boss.boss_bullets[i].m_y,m_boss.boss_bullets[i].boss_Bullet);}}}
}

10.碰撞检测

我们还需要在主界面类中实现我们的碰撞检测,主要是利用一个函数来判断子弹飞机边框是否相交

首先我们将运行状态的子弹和敌机都循环遍历出来,随后循环判断他们边框是否相交,相交则将子弹和敌机状态都变为空闲等待下一次出场

//碰撞检测
void mainsence::collisionDetection()
{//遍历出场敌机for(int i=0;i<ENEMY_NUM;i++){//空闲飞机则执行下一次循环if(m_enemys[i].m_Free){continue;}//如果飞机与敌机相撞则飞机坠毁,将状态设置为空闲if(m_enemys[i].m_Rect.intersects(m_hero.m_Rect)){m_hero.m_Free=true;for(int k=0;k<BOMB_NUM;k++){if(m_bombs[k].m_Free){//播放爆炸音效//空闲的爆炸,可以播放m_bombs[k].m_Free=false;//更新爆炸坐标m_bombs[k].m_x=m_hero.m_x;m_bombs[k].m_y=m_hero.m_y;break;}}}//遍历所有非空闲子弹for(int j=0;j<BULLET_NUM;j++){//空闲子弹则执行下一次循环if(m_hero.m_bullets[i].m_Free){continue;}//如果子弹边框和敌机边框相交则说明发生碰撞,则将他们状态设置为空闲if(m_enemys[i].m_Rect.intersects(m_hero.m_bullets[j].m_Rect)){m_enemys[i].m_Free=true;m_hero.m_bullets[i].m_Free=true;//播放爆炸效果for(int k=0;k<BOMB_NUM;k++){if(m_bombs[k].m_Free){//播放爆炸音效//空闲的爆炸,可以播放m_bombs[k].m_Free=false;//更新爆炸坐标m_bombs[k].m_x=m_enemys[i].m_x;m_bombs[k].m_y=m_enemys[i].m_y;break;}}}//判断子弹边框和boss边框是否相交,相交则扣除boss血量直到为0,boss状态设置为闲置if(m_boss.m_Free==false){if(m_hero.m_bullets[j].m_Rect.intersects(m_boss.m_Rect)){m_boss.blood--;//qDebug()<<m_boss.blood;if(m_boss.blood==0){m_boss.m_Free=true;//播放爆炸效果for(int k=0;k<BOMB_NUM;k++){if(m_bombs[k].m_Free){qDebug()<<"bomb";//空闲的爆炸,可以播放m_bombs[k].m_Free=false;//更新爆炸坐标m_bombs[k].m_x=m_boss.m_x;m_bombs[k].m_y=m_boss.m_y;break;}}}}}}}}

11.爆炸类的制作

11.1 创建爆炸文件和类

首先我们需要创建一个爆炸类的文件,并将爆炸的资源配置加入到config.h头文件中:由于我们的爆炸效果是一个动态的过程,我这里使用了7张图片来实现这个动态过程,这里路径中我们用了特殊的写法,这样可以让我们的路径可以显示多张图片的路径

/*****爆炸资源配置*******/
#define BOMB_PATH ":/res/bomb_%1" //爆炸资源图片
#define BOMB_NUM 32 //爆炸数量
#define BOMB_MAX 7 //爆炸图片最大索引
#define BOMB_INTERVAL 10 //爆炸图片显示间隔

11.2 添加爆炸成员和成员属性

bomb.h

#include "config.h"
#include <QPixmap>
#include <QVector>
class bomb
{
public:bomb();//更新爆炸信息void updateInfo();//放置爆炸资源的数组QVector<QPixmap> m_pixArr;//爆炸坐标int m_x;int m_y;//爆炸状态bool m_Free;//爆炸切图的时间间隔int m_Recoder;//爆炸时加载的图片下标int m_index;};

这里我们将7张图片放进一个容器中,类似数组,后面我们可以根据下标来取出我们需要的对应图片。更新爆炸信息的成员函数表示每一张图片播放间隔之后我们会切换到下一张图片进行播放,直到七张播放完毕,在碰撞检测中,只要检测到碰撞我们就会调用爆炸对象,循环遍历爆炸组中哪个对像在空闲状态,将该对象进行播放,完毕后将该爆炸对象置为空闲方便循环使用爆炸资源。

11.3 实现成员函数

bomb.cpp

#include "bomb.h"
#include "config.h"
bomb::bomb()
{//将所有的爆炸资源放入数组中for(int i=1;i<=BOMB_MAX;i++){QString str=QString(BOMB_PATH).arg(i);m_pixArr.push_back(QPixmap(str));}//坐标m_x=0;m_y=0;//空闲状态m_Free=true;//当前播放图片下标m_index=0;//播放爆炸间隔记录m_Recoder=0;
}void bomb::updateInfo()
{if(m_Free)return;m_Recoder++;//如果记录爆炸的实际未达到爆炸间隔,不需要切图,直接returnif(m_Recoder<BOMB_INTERVAL)return;//重置记录m_Recoder=0;//切换爆炸播放的图片下标m_index++;if(m_index>BOMB_MAX-1){m_index=0;m_Free=true;//播放爆炸效果完成}}

同时我们也需要根据爆炸对象状态来决定是否进行绘制,所以我们还需要在绘制事件中进行判断绘制:

void mainsence::paintEvent(QPaintEvent *)
{
//绘制爆炸for(int i=0;i<BOMB_NUM;i++){if(m_bombs[i].m_Free==false){painter.drawPixmap(m_bombs[i].m_x,m_bombs[i].m_y,m_bombs[i].m_pixArr[m_bombs[i].m_index]);}}
}

在游戏中我们需要随时更新界面的资源坐标所以在主界面类中我们还需要一个更新资源位置的函数来更新坐标,包括飞机子弹boss敌机等等资源:

void mainsence::updatePosition()
{//更新地图坐标m_map.mapPosition();//发射子弹m_hero.shoot();if(m_boss.m_Free==false){  //boss发射子弹m_boss.shoot();}//计算所有非空闲状态子弹的当前坐标for(int i=0;i<BULLET_NUM;i++){if(m_hero.m_bullets[i].m_Free==false){m_hero.m_bullets[i].updatePosition();}}//计算boss所欲非空闲状态子弹的当前坐标for(int i=0;i<BOSS_BULLET_NUM;i++){if(m_boss.boss_bullets[i].m_Free==false){m_boss.boss_bullets[i].updatePosition();}}//更新敌机坐标for(int i=0;i<ENEMY_NUM;i++){if(m_enemys[i].m_Free==false){m_enemys[i].updatePosition();}}//计算爆炸的播放图片for(int i=0;i<BOMB_NUM;i++){if(m_bombs[i].m_Free==false){m_bombs[i].updateInfo();}}}

到此我们的项目基本完成,但是还是比较简陋,后续可以进行更新升级更多的功能,其次在程序编写中,我意识到在必要的地方加上打印信息对后续的程序调试帮助巨大,可以让我们明白问题有可能出现在什么地方!!!希望大家在写程序过程中,多使用Debug来进行调试打印。

QT练手小项目:飞机大战相关推荐

  1. QT练手小项目-——天气播报小狗(ui展示分析,构造实现,json格式数据分析,界面交互,天气图标处理,小狗语音)

    前言 经过之前一段时间的QT学习,做出一个小软件来总结自己掌握关于qt的知识点.网络上有许多免费的天气接口(api),有xml格式的,也有json格式的.具体xml和json有什么区别,这里我就不去深 ...

  2. qt练手小项目之打地鼠

    项目简介:总共有4*4(16)块的区域(item),该区域是地鼠出没的地方,每隔800毫秒出现1-2只地鼠,然后用锤子去敲击,在地鼠消失之前可以无限敲打,敲中一次得分面板加1分 未敲中得分面板减一  ...

  3. ssm练手小项目_20 个 JavaScript+Html+CSS 练手的小项目

    前言: 最近在 GitHub 上发现了一个 vanillawebprojects[1] 开源仓库,里面收集了 20 个 JavaScript+Html+CSS的练手项目,没有使用任何框架,可以让你从基 ...

  4. 台式小风扇(HTML+CSS+JS练手小项目)

    台式小风扇(HTML+CSS+JS练手小项目) 功能介绍 外观展示 HTML代码 CSS代码 JS代码 总结 功能介绍 前段时间看到这样的风扇特效,感觉还挺好玩,就自己也写一个练练手. 风扇有四个档位 ...

  5. 爬虫练手小项目:豆瓣高分图书TOP100

    爬虫练手小项目:豆瓣高分图书TOP100 import requests import re from requests.exceptions import RequestException impo ...

  6. 数据结构练手小项目(AVL树、哈希表、循环链表、MySQL数据库)

    文章目录 前言 正文(无删减) 我的想法(删减修改版) 数据导入与数据存储 功能实现 数据结构 用户结构 SIM卡结构 AVL树数据结构 哈希表结构 数据表 用户表 SIM卡表 时间安排 前言 本月主 ...

  7. html+css+js之20个练手小项目(一)

    html+css+js之20个练手小项目(一)--Hangman 前言 一.HTML 二.CSS 三.JS 前言 前端新手练习,记录不迷失. 主要练习html和CSS布局以及JS. 来源github, ...

  8. 练手小项目,爬取3DM图片

    博客原文:https://weweweha.com 1. 概述 ​ 爬取3DM指定网页的游戏壁纸,并且通过多线程来加速爬取图片的速度. 2.使用库 ​ request库用来1解析指定网页,re库用来搜 ...

  9. c语言模拟器怎么打程序,C语言初学者练手小项目——万花模拟器

    原标题:C语言初学者练手小项目--万花模拟器 还记得小时候玩的万花尺么?好好玩,各种不同的点距能画出各种各样形状图形. C语言程序万花尺模拟 函数功能:每隔5秒随机生成万花图形 并自动保存作图参数以及 ...

最新文章

  1. 获取namespace
  2. python小白——进阶之路——day6天---字符串相关相关函数,格式化(format)
  3. IDEA不能下载插件
  4. ConcurrentHashMap是如何保证线程安全的,你知道么?
  5. Ros学习笔记(二)创建功能包
  6. 单片机C语言基础知识篇
  7. IOS逆向-ASLR
  8. 2019年前端大事件回顾:流年笑掷,未来可期
  9. ajax dojo deferred,Dojo学习-14:Ajax with dojo/request
  10. 想杀死某个端口进程,但在服务列表中却找不到,可以之间通过命令行找到这个进程并杀死该进程,减少重启电脑和找到问题根源。
  11. Proxy Server源码及分析(TCP Proxy源码 Socket实现端口映射)
  12. 成功解决windows10连上了wifi但是没网的问题
  13. c语言中入口参数是什么,C语言中入口参数是什么
  14. python apache benchmark_Python cudnn.benchmark方法代码示例
  15. C#实现共享并且建立账号(指定账号登录操作,如果是 1219错误则清除用户数据)
  16. 如何配置XenDesktop使用Mirror数据库
  17. DevCloud加持下的青软,让教育“智”上云端
  18. GeekPwn嘉年华:黑客操控POS机 银行卡轻松“易主”
  19. 柱形图和折线图在一个坐标轴ECharts
  20. 【总结】1577- Web3.0前端工程师需要具备哪些技术?

热门文章

  1. oracle 如何查看表分区,ORACLE中如何查看分区表信息
  2. 如何化“熵增”为“熵减”,数据质量管理尤为重要
  3. Datastage 入门示例
  4. [echarts] 案例大全 chartlib chartsdev ppchart madeapie MCChart
  5. 来自影视中的警言:What a lie is when we see it.
  6. 正则表达式严格验证身份证号
  7. Oracle系列-简介及安装
  8. 关闭树莓派的电源指示灯(红)和状态指示灯(黄)
  9. 卓越音质高续航,游戏专属低延迟真无线蓝牙耳机测评推荐
  10. 【Unity 风格化】水墨风渲染01:从总结实现方法开始