更新時間:2020-01-17 來源:黑馬程序員 瀏覽量:
目錄
飛機(jī)大戰(zhàn)是我們大家所熟知的一款小游戲,本教程就是教大家如何制作一款自己的飛機(jī)大戰(zhàn)。
首先我們看一下效果圖
玩家控制一架小飛機(jī),然后自動發(fā)射子彈,如果子彈打到了飛下來的敵機(jī),則射殺敵機(jī),并且有爆炸的特效。接下來再說明一下案例的需求,也就是我們需要實現(xiàn)的內(nèi)容。
·滾動的背景地圖
·飛機(jī)的制作和控制
·子彈的制作和射擊
·敵機(jī)的制作
·碰撞檢測
·爆炸效果
·音效添加
創(chuàng)建項目步驟如下:
·打開Qt
·跟著向?qū)?chuàng)建項目
基類選擇 QWidget空窗口
第一個場景為主場景 MainScene
不帶UI界面
2.1 打開Qt
找到你安裝的Qt Creator,打開它。
如果安裝時,沒有選擇在桌面上建立快捷方式,那么你的Qt軟件位置如下
C:\qt\Qt5.x.x\Tools\QtCreator\bin
在這個路徑下找到 qtcreator.exe 雙擊打開即可
2.2 按照向?qū)?chuàng)建項目
2.2.1 新建項目
點擊菜單 中的文件 -> 新建文件或項目 或者 在首頁面中點擊New Project
2.2.2 選擇模板
模板選擇 Application -> Qt Widget Application
2.2.3 項目名稱和位置
給項目起個名稱以及選中項目要保存的地方
這一步選擇后在Kits 構(gòu)建套件中直接點擊下一步即可
2.2.4 類信息
基類選擇 QWidget
類名也就是我們第一個窗口場景的名稱,這里我起名為 MainScene 代表游戲中的主場景
取消創(chuàng)建界面中的內(nèi)容
2.2.5 完成創(chuàng)建
在匯總頁面中點擊完成,我們就邁開了項目的第一步!
主場景設(shè)置的步驟如下:
·添加配置文件,保存游戲中所有配置數(shù)據(jù)
·初始化主場景窗口大小、標(biāo)題
3.1 配置文件添加
創(chuàng)建新的頭文件為 config.h 主要記錄程序中所有的配置數(shù)據(jù),方便后期修改
添加窗口寬度、高度的配置信息,依據(jù)背景圖大小進(jìn)行設(shè)置
/********** 游戲配置數(shù)據(jù) **********/
define GAME_WIDTH 512 //寬度
define GAME_HEIGHT 768 //高度
define GAME_TITLE "飛機(jī)大戰(zhàn) v1.0" //標(biāo)題
3.2 主場景基本設(shè)置
在mainScene.h中添加新的成員函數(shù)initScene 用來初始化游戲場景
void initScene();
在mainScene.cpp中實現(xiàn)如下代碼
void MainScene::initScene()
{
//初始化窗口大小
setFixedSize(GAMEWIDTH,GAMEHEIGHT);
//設(shè)置窗口標(biāo)題
setWindowTitle(GAME_TITLE);
}
在構(gòu)造函數(shù)MainScene中調(diào)用該函數(shù) initScene
MainScene::MainScene(QWidget *parent)
: QWidget(parent)
{
//初始化場景
initScene();
}
測試運(yùn)行效果如圖:
在主場景中其實還有一個配置項沒有實現(xiàn),也就是窗口左上角的那個圖標(biāo)資源。那么接下來我們將游戲中的資源進(jìn)行導(dǎo)入并且設(shè)置游戲圖標(biāo)。
資源導(dǎo)入步驟
·生成qrc文件
·項目同級目錄下創(chuàng)建res文件夾并將資源粘貼過來
·編輯qrc,加入前綴和文件
·利用qrc生成二進(jìn)制文件 rcc
·rcc文件放入到debug同級目錄下
·注冊二進(jìn)制文件
·添加圖標(biāo)資源
4.1 qrc文件生成
右鍵項目,點擊添加新文件
選擇Qt -> Qt Resource File
資源文件起名 如:res
生成res.qrc文件
4.2 創(chuàng)建res文件夾
項目的同級目錄下創(chuàng)建文件夾res,并將準(zhǔn)備好的資源粘貼進(jìn)去
4.3 編輯qrc文件
右鍵qrc文件,選中Open in Editor
添加前綴為 '' \ ''
添加文件 將res下所有文件選中即可
4.4 qrc生成 rcc二進(jìn)制文件
由于資源過大,會提示錯誤:
這個錯誤也就是“編譯器的堆空間不足”。
由于資源文件qrc過大,超出分配的內(nèi)存范圍
因此我們需要利用二進(jìn)制資源,而生成二進(jìn)制資源就需要我們剛剛的qrc文件
利用cmd打開終端,定位到res.qrc的目錄下,輸入命令
rcc -binary .\res.qrc -o plane.rcc
4.5 復(fù)制rcc文件
將生成好的rcc文件,放入到debug同級目錄中一份
4.6 注冊二進(jìn)制文件
在config.h中追加配置數(shù)據(jù)
#define GAMERESPATH "./plane.rcc" //rcc文件路徑
在main.cpp中修改代碼
#include "mainscene.h"
#include
#include
#include "config.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//注冊外部的二進(jìn)制資源文件
QResource::registerResource(GAME_RES_PATH);
MainScene w;
w.show();
return a.exec();
}
此時,qrc文件已經(jīng)沒用了,刪除即可!
最簡單的刪除方式就是 .pro工程文件中刪除代碼,與工程無瓜葛
刪除以下代碼:
RESOURCES += \ res.qrc
4.7 添加圖標(biāo)資源
配置文件config.h中追加代碼
虛擬資源路徑語法如下:
" : + 前綴名 + 文件路徑 "
#define GAME_ICON ":/res/app.ico"
在mainScene.cpp的 initScene函數(shù)中追加代碼:
//設(shè)置圖標(biāo)資源
setWindowIcon(QIcon( GAME_ICON)); //加頭文件 #include <QIcon>
運(yùn)行測試:
步驟:
·創(chuàng)建地圖文件和類
·添加成員函數(shù)和成員屬性 實現(xiàn)成員函數(shù)
·游戲運(yùn)行調(diào)用定時器
·啟動定時器,監(jiān)聽定時器信號實現(xiàn)游戲循環(huán)
-計算游戲內(nèi)元素坐標(biāo)
-繪制到屏幕中
5.1 創(chuàng)建地圖文件和類
右鍵項目,添加新文件
選擇C++ -> C++ Class
修改類名為map,點擊下一步,直到創(chuàng)建完畢
至此,地圖Map的文件和類創(chuàng)建完畢
5.2 地圖的成員函數(shù)和成員屬性
在map.h中添加如下代碼
#ifndef MAP_H
#define MAP_H
#include(Qpixmap)
class Map {
public:
//構(gòu)造函數(shù)
Map();
//地圖滾動坐標(biāo)計算
void mapPosition();
public:
//地圖圖片對象
QPixmap m_map1;
QPixmap m_map2;
//地圖Y軸坐標(biāo)
int m_map1_posY;
int m_map2_posY;
//地圖滾動幅度
int m_scroll_speed;
};
endif // MAP_H
5.3 實現(xiàn)成員函數(shù)
在config.h中添加新的配置數(shù)據(jù)
/********** 地圖配置數(shù)據(jù) **********/
define MAPPATH ":/res/imgbglevel1.jpg" //地圖圖片路徑
define MAPSCROLLSPEED 2 //地圖滾動速度
在map.cpp中實現(xiàn)成員函數(shù)
#include "map.h"
#include "config.h"
Map::Map()
{
//初始化加載地圖對象
mmap1.load(MAPPATH);
mmap2.load(MAPPATH);
//設(shè)置地圖其實y軸坐標(biāo)
m_map1_posY = -GAME_HEIGHT;
m_map2_posY = 0;
//設(shè)置地圖滾動速度
m_scroll_speed = MAP_SCROLL_SPEED;
}
void Map::mapPosition()
{
//處理第一張圖片滾動
mmap1posY += MAPSCROLLSPEED;
if(mmap1posY >= 0)
{
mmap1posY =-GAME_HEIGHT;
}
//處理第二張圖片滾動
m_map2_posY += MAP_SCROLL_SPEED;
if(m_map2_posY >= GAME_HEIGHT )
{
m_map2_posY =0;
}
}
5.4 定時器添加
在mainScene.h中添加新的定時器對象
QTimer m_Timer;
在 config.h中添加 屏幕刷新間隔
#define GAME_RATE 10 //刷新間隔,幀率 單位毫秒
在MainScene.cpp的initScene中追加代碼
//定時器設(shè)置
m_Timer.setInterval(GAME_RATE);
5.5 啟動定時器實現(xiàn)地圖滾動
在MainScene.h中添加新的成員函數(shù)以及成員對象
//啟動游戲 用于啟動定時器對象
void playGame();
//更新坐標(biāo)
void updatePosition();
//繪圖事件
void paintEvent(QPaintEvent *event);
//地圖對象
Map m_map;
在MainScene.cpp中實現(xiàn)成員函數(shù)
void MainScene::playGame()
{
//啟動定時器
m_Timer.start();
//監(jiān)聽定時器
connect(&m_Timer,&QTimer::timeout,[=](){
//更新游戲中元素的坐標(biāo)
updatePosition();
//重新繪制圖片
update();
});
}
void MainScene::updatePosition()
{
//更新地圖坐標(biāo)
m_map.mapPosition();
}
void MainScene::paintEvent(QPaintEvent *event)
{
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);
}
測試運(yùn)行游戲,實現(xiàn)地圖滾動
步驟如下:
·創(chuàng)建英雄文件和類
·添加成員函數(shù)和成員屬性
·實現(xiàn)成員函數(shù)
·創(chuàng)建飛機(jī)對象并顯示
·拖拽飛機(jī)
6.1 創(chuàng)建英雄文件和類
創(chuàng)建HeroPlane類以及生成對應(yīng)的文件
和創(chuàng)建地圖的步驟一樣,這里就不在詳細(xì)截圖了
創(chuàng)建好后生成HeroPlane.h 和 HeroPlane.cpp兩個文件
6.2 飛機(jī)的成員函數(shù)和成員屬性
在HeroPlane.h中添加代碼
class HeroPlane
{
public: HeroPlane();
//發(fā)射子彈
void shoot();
//設(shè)置飛機(jī)位置
void setPosition(int x, int y);
public:
//飛機(jī)資源 對象
QPixmap m_Plane;
//飛機(jī)坐標(biāo)
int m_X;
int m_Y;
//飛機(jī)的矩形邊框
QRect m_Rect;
};
6.3 成員函數(shù)實現(xiàn)
這里飛機(jī)有個發(fā)射子彈的成員函數(shù),由于我們還沒有做子彈
因此這個成員函數(shù)先寫成空實現(xiàn)即可
在config.h中追加飛機(jī)配置參數(shù)
/********** 飛機(jī)配置數(shù)據(jù) **********/
define HERO_PATH ":/res/hero2.png"
heroPlane.cpp中實現(xiàn)成員函數(shù)代碼:
#include "heroplane.h"
#include "config.h"
HeroPlane::HeroPlane()
{
//初始化加載飛機(jī)圖片資源
mPlane.load(HEROPATH);
//初始化坐標(biāo)
m_X = GAME_WIDTH * 0.5 - m_Plane.width()*0.5;
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);
}
void HeroPlane::setPosition(int x, int y)
{
mX = x;
mY = y;
m_Rect.moveTo(mX,mY);
}
void HeroPlane::shoot()
{
}
6.4 創(chuàng)建飛機(jī)對象并顯示
在MainScene.h中追加新的成員屬性
//飛機(jī)對象
HeroPlane m_hero;
在MainScene.cpp的paintEvent中追加代碼
//繪制英雄
painter.drawPixmap(m_hero.m_X,m_hero.m_Y,m_hero.m_Plane);
測試飛機(jī)顯示到屏幕中
6.5 拖拽飛機(jī)
在MainScene.h中添加鼠標(biāo)移動事件
//鼠標(biāo)移動事件
void mouseMoveEvent(QMouseEvent *event);
重寫鼠標(biāo)移動事件
void MainScene::mouseMoveEvent(QMouseEvent event)
{
int x = event->x() - mhero.mRect.width()0.5; //鼠標(biāo)位置 - 飛機(jī)矩形的一半
int y = event->y() - mhero.mRect.height()*0.5;
//邊界檢測
if(x <= 0 )
{
x = 0;
}
if(x >= GAME_WIDTH - m_hero.m_Rect.width())
{
x = GAME_WIDTH - m_hero.m_Rect.width();
}
if(y <= 0)
{
y = 0;
}
if(y >= GAME_HEIGHT - m_hero.m_Rect.height())
{
y = GAME_HEIGHT - m_hero.m_Rect.height();
}
m_hero.setPosition(x,y);
}
測試飛機(jī)可以拖拽
制作步驟如下:
·創(chuàng)建子彈文件和類
·添加子彈類中的成員函數(shù)和成員屬性
·實現(xiàn)成員函數(shù)
·測試子彈
7.1 創(chuàng)建子彈文件和類
創(chuàng)建Bullet類以及生成對應(yīng)的文件
創(chuàng)建好后生成bullet.h 和 bullet.cpp兩個文件
7.2 子彈的成員函數(shù)和成員屬性
在Bullet.h中添加代碼
#ifndef BULLET_H
#define BULLET_H
#include "config.h"
#include<QPixamp>
class Bullet {
public: Bullet();
//更新子彈坐標(biāo)
void updatePosition();
public:
//子彈資源對象
QPixmap mBullet;
//子彈坐標(biāo)
int mX;
int mY;
//子彈移動速度
int mSpeed;
//子彈是否閑置
bool mFree;
//子彈的矩形邊框(用于碰撞檢測)
QRect mRect;
};
#endif // BULLET_H
7.3 子彈類成員函數(shù)實現(xiàn)
在config.h中追加子彈配置信息
/********** 子彈配置數(shù)據(jù) **********/
#define BULLETPATH ":/res/bullet11.png" //子彈圖片路徑
#define BULLET_SPEED 5 //子彈移動速度
在bullet.cpp中實現(xiàn)成員函數(shù),代碼如下:
#include "bullet.h"
Bullet::Bullet() {
//加載子彈資源
mBullet.load(BULLETPATH);
//子彈坐標(biāo) 初始坐標(biāo)可隨意設(shè)置,后期會重置
m_X = GAME_WIDTH*0.5 - m_Bullet.width()*0.5;
m_Y = GAME_HEIGHT;
//子彈狀態(tài)
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() {
//如果子彈是空閑狀態(tài),不需要坐標(biāo)計算
//玩家飛機(jī)可以控制子彈的空閑狀態(tài)為false
if(m_Free) {
return;
}
//子彈向上移動
m_Y -= m_Speed;
m_Rect.moveTo(m_X,m_Y);
if(m_Y <= -m_Rect.height());
{
m_Free = true;
}
}
7.4 測試子彈
子彈本身應(yīng)該由飛機(jī)發(fā)射,測試階段我們寫一段輔助代碼,看看效果即可
測試過后,這些代碼可以刪除掉
在MainScene.h中添加測試代碼
//測試子彈代碼
Bullet temp_bullet;
在MainScene.cpp中的updatePosition里添加測試代碼
//測試子彈代碼
temp_bullet.m_Free = false;
temp_bullet.updatePosition();
在MainScene.cpp中的paintEvent里添加測試代碼
//測試子彈代碼
painter.drawPixmap(temp_bullet.m_X,temp_bullet.m_Y,temp_bullet.m_Bullet);
運(yùn)行程序,此時會有一發(fā)子彈從屏幕中射出
測試完畢后,測試代碼刪除或注釋即可
玩家發(fā)射子彈制作步驟如下:
·英雄飛機(jī)添加新的成員屬性
·實現(xiàn)發(fā)射成員函數(shù)
·主場景控制子彈發(fā)射
8.1 飛機(jī)添加新成員屬性
在config.h中添加新的配置數(shù)據(jù)
#define BULLET_NUM 30 //彈匣中子彈總數(shù)
#define BULLET_INTERVAL 20 //發(fā)射子彈時間間隔
在HeroPlane.h中新增成員屬性如下:
//彈匣
Bullet mbullets[BULLETNUM];
//發(fā)射間隔記錄
int m_recorder;
8.2 成員函數(shù)補(bǔ)充
在構(gòu)造函數(shù) HeroPlane 中初始化發(fā)生間隔記錄
//初始化發(fā)射間隔記錄
m_recorder = 0;
之前在英雄飛機(jī)類中預(yù)留的一個shoot函數(shù)我們進(jìn)行實現(xiàn),代碼如下:
void HeroPlane::shoot() {
//累加時間間隔記錄變量
mrecorder++;
//判斷如果記錄數(shù)字,未達(dá)到發(fā)射間隔,直接
return if(mrecorder < BULLETINTERVAL) { return; }
//到達(dá)發(fā)射時間處理
//重置發(fā)射時間間隔記錄
mrecorder = 0;
//發(fā)射子彈
for(int i = 0 ; i < BULLET_NUM;i++)
{
//如果是空閑的子彈進(jìn)行發(fā)射
if(m_bullets[i].m_Free)
{
//將改子彈空閑狀態(tài)改為假
m_bullets[i].m_Free = false;
//設(shè)置發(fā)射的子彈坐標(biāo)
m_bullets[i].m_X = m_X + m_Rect.width()*0.5 - 10;
m_bullets[i].m_Y = m_Y - 25 ;
break;
}
}
}
8.3 主場景中實現(xiàn)發(fā)射子彈
在MainScene.cpp的updatePosition成員函數(shù)中追加如下代碼
//發(fā)射子彈
m_hero.shoot();
//計算子彈坐標(biāo)
for(int i = 0 ;i < BULLET_NUM;i++)
{
//如果子彈狀態(tài)為非空閑,計算發(fā)射位置
if(!m_hero.m_bullets[i].m_Free)
{
m_hero.m_bullets[i].updatePosition();
}
}
在MainScene.cpp的paintEvent成員函數(shù)中追加如下代碼:
//繪制子彈
for(int i = 0 ;i < BULLET_NUM;i++)
{
//如果子彈狀態(tài)為非空閑,計算發(fā)射位置
if(!m_hero.m_bullets[i].m_Free)
{
painter.drawPixmap(m_hero.m_bullets[i].m_X,m_hero.m_bullets[i].m_Y,m_hero.m_bullets[i].m_Bullet);
}
}
測試運(yùn)行,玩家可以發(fā)射子彈
敵機(jī)制作與子彈制作原理類似,也是每隔一定的時間讓敵機(jī)出場
制作步驟如下:
·創(chuàng)建敵機(jī)文件和類
·添加敵機(jī)類中的成員函數(shù)和成員屬性
·實現(xiàn)成員函數(shù)
·敵機(jī)出場
·測試敵機(jī)
9.1 創(chuàng)建敵機(jī)文件和類
創(chuàng)建EnemyPlane類以及生成對應(yīng)的文件
創(chuàng)建好后生成enemyPlane.h 和 enemyPlane.cpp兩個文件
9.2 敵機(jī)成員函數(shù)和成員屬性
在enemyPlane.h中添加如下代碼:
#ifndef ENEMYPLANE_H
#define ENEMYPLANE_H
#include<QPixmap>
class EnemyPlane {
public:
EnemyPlane();
//更新坐標(biāo)
void updatePosition();
public:
//敵機(jī)資源對象
QPixmap m_enemy;
//位置
int m_X;
int m_Y;
//敵機(jī)的矩形邊框(碰撞檢測)
QRect m_Rect;
//狀態(tài)
bool m_Free;
//速度
int m_Speed;
};
#endif // ENEMYPLANE_H
9.3 敵機(jī)成員函數(shù)實現(xiàn)
在config.h中追加敵機(jī)配置信息
/********** 敵機(jī)配置數(shù)據(jù) **********/
#define ENEMYPATH ":/res/img-plane5.png" //敵機(jī)資源圖片
#define ENEMY_SPEED 5 //敵機(jī)移動速度
#define ENEMY_NUM 20 //敵機(jī)總數(shù)量
#define ENEMY_INTERVAL 30 //敵機(jī)出場時間間隔
在enemyPlane.cpp中實現(xiàn)成員函數(shù),代碼如下:
EnemyPlane::EnemyPlane()
{
//敵機(jī)資源加載
menemy.load(ENEMYPATH);
//敵機(jī)位置
m_X = 0;
m_Y = 0;
//敵機(jī)狀態(tài)
m_Free = true;
//敵機(jī)速度
m_Speed = ENEMY_SPEED;
//敵機(jī)矩形
m_Rect.setWidth(m_enemy.width());
m_Rect.setHeight(m_enemy.height());
m_Rect.moveTo(m_X,m_Y);
}
void EnemyPlane::updatePosition()
{
//空閑狀態(tài),不計算坐標(biāo) 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;
}
}
9.4 敵機(jī)出場
在MainScene.h中追加敵機(jī)出場的成員函數(shù)
在MainScene.h中追加敵機(jī)數(shù)組 和 敵機(jī)出場間隔記錄 的成員屬性
//敵機(jī)出場
void enemyToScene();
//敵機(jī)數(shù)組
EnemyPlane m_enemys[ENEMY_NUM];
//敵機(jī)出場間隔記錄
int m_recorder;
初始化間隔記錄屬性,在MainScene.cpp的 initScene 成員函數(shù)中追加
#m_recorder = 0;
實現(xiàn)成員函數(shù) enemyToScene
void MainScene::enemyToScene()
{
mrecorder++;
if(mrecorder < ENEMY_INTERVAL)
{
return;
}
m_recorder = 0;
for(int i = 0 ; i< ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free)
{
//敵機(jī)空閑狀態(tài)改為false
m_enemys[i].m_Free = false;
//設(shè)置坐標(biāo)
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;
}
}
}
在PlayGame成員函數(shù)的timeout信號發(fā)送時候,槽函數(shù)中首先追加 enemyToScene
//敵機(jī)出場
enemyToScene();
更新敵機(jī)坐標(biāo),在updatePosition成員函數(shù)中追加代碼
//敵機(jī)坐標(biāo)計算
for(int i = 0 ; i< ENEMY_NUM;i++)
{
//非空閑敵機(jī) 更新坐標(biāo)
if(m_enemys[i].m_Free == false)
{
m_enemys[i].updatePosition();
}
}
繪制敵機(jī),在paintEvent成員函數(shù)中追加繪制敵機(jī)代碼
//繪制敵機(jī)
for(int i = 0 ; i< ENEMY_NUM;i++)
{
if(m_enemys[i].m_Free == false)
{
painter.drawPixmap(m_enemys[i].m_X,m_enemys[i].m_Y,m_enemys[i].m_enemy);
}
}
添加隨機(jī)數(shù)種子
在MainScene.cpp中 initScene 成員函數(shù)里添加隨機(jī)數(shù)種子
//隨機(jī)數(shù)種子
srand((unsigned int)time(NULL));
//頭文件
#include <ctime>
運(yùn)行測試敵機(jī)出場效果
實現(xiàn)碰撞檢測步驟如下:
·添加并實現(xiàn)碰撞檢測成員函數(shù)
·調(diào)用并測試函數(shù)
10.1 添加并實現(xiàn)碰撞檢測函數(shù)
在MainScene.h中添加新的成員函數(shù)
void collisionDetection();
在MainScene.cpp中實現(xiàn)該成員函數(shù)
void MainScene::collisionDetection()
{
//遍歷所有非空閑的敵機(jī)
for(int i = 0 ;i < ENEMYNUM;i++)
{
if(menemys[i].m_Free)
{
//空閑飛機(jī) 跳轉(zhuǎn)下一次循環(huán)
continue;
}
//遍歷所有 非空閑的子彈
for(int j = 0 ; j < BULLET_NUM;j++)
{
if(m_hero.m_bullets[j].m_Free)
{
//空閑子彈 跳轉(zhuǎn)下一次循環(huán)
continue;
}
//如果子彈矩形框和敵機(jī)矩形框相交,發(fā)生碰撞,同時變?yōu)榭臻e狀態(tài)即可
if(m_enemys[i].m_Rect.intersects(m_hero.m_bullets[j].m_Rect))
{
m_enemys[i].m_Free = true;
m_hero.m_bullets[j].m_Free = true;
}
}
}
}
10.2 調(diào)用并測試函數(shù)
在MainScene.cpp中 playGame成員函數(shù)里,追加碰撞檢測代碼
運(yùn)行查看效果,子彈和敵機(jī)碰撞后會同時消失
爆炸效果功能實現(xiàn)步驟如下:
·創(chuàng)建爆炸文件和類
·添加爆炸類中的成員函數(shù)和成員屬性
·實現(xiàn)成員函數(shù)
·調(diào)用并測試效果
11.1 創(chuàng)建爆炸文件和類
創(chuàng)建Bomb類以及生成對應(yīng)的文件
創(chuàng)建好后生成bomb.h 和 bomb.cpp兩個文件
11.2 爆炸成員函數(shù)和成員屬性
在config.h中加入爆炸配置數(shù)據(jù)
define BOMB_PATH ":/res/bomb-%1.png" //爆炸資源圖片
define BOMB_NUM 20 //爆炸數(shù)量
define BOMB_MAX 7 //爆炸圖片最大索引
define BOMB_INTERVAL 20 //爆炸切圖時間間隔
在bomb.h中添加如下代碼:
#ifndef BOMB_H
#define BOMB_H
#include "config.h"
#include <QPixmap>
#include <QVector>
class Bomb
{
public:
Bomb();
//更新信息(播放圖片下標(biāo)、播放間隔)
void updateInfo();
public:
//放爆炸資源數(shù)組
QVector<QPixmap> m_pixArr;
//爆炸位置
int m_X;
int m_Y;
//爆炸狀態(tài)
bool m_Free;
//爆炸切圖的時間間隔
int m_Recoder;
//爆炸時加載的圖片下標(biāo)
int m_index;
};
#endif // BOMB_H
11.3 實現(xiàn)成員函數(shù)
Bomb::Bomb()
{
//初始化爆炸圖片數(shù)組
for(int i = 1 ;i <= BOMBMAX ;i++)
{
//字符串拼接,類似 ":/res/bomb-1.png"
QString str = QString(BOMBPATH).arg(i);
mpixArr.pushback(QPixmap(str));
}
//初始化坐標(biāo)
m_X = 0;
m_Y = 0;
//初始化空閑狀態(tài)
m_Free = true;
//當(dāng)前播放圖片下標(biāo)
m_index = 0;
//爆炸間隔記錄
m_Recoder = 0;
}
void Bomb::updateInfo()
{
//空閑狀態(tài)
if(m_Free) { return;
}
m_Recoder++;
if(m_Recoder < BOMB_INTERVAL)
{
//記錄爆炸間隔未到,直接return,不需要切圖
return;
}
//重置記錄
m_Recoder = 0;
//切換爆炸播放圖片
m_index++;
//注:數(shù)組中的下標(biāo)從0開始,最大是6
//如果計算的下標(biāo)大于6,重置為0
if(m_index > BOMB_MAX-1)
{
m_index = 0;
m_Free = true;
}
}
11.4 加入爆炸數(shù)組
在MainScene.h中加入爆炸數(shù)組 成員屬性
//爆炸數(shù)組
Bomb m_bombs[BOMB_NUM];
在碰撞檢測成員函數(shù)中,當(dāng)發(fā)生碰撞時,設(shè)置爆炸對象的信息
//播放爆炸效果
for(int k = 0 ; k < BOMBNUM;k++) { if(mbombs[k].m_Free) { //爆炸狀態(tài)設(shè)置為非空閑 mbombs[k].mFree = false; //更新坐標(biāo)
m_bombs[k].m_X = m_enemys[i].m_X;
m_bombs[k].m_Y = m_enemys[i].m_Y;
break;
}
}
在 MainScene.cpp的updatePosition中追加代碼
//計算爆炸播放的圖片
for(int i = 0 ; i < BOMB_NUM;i++)
{
if(m_bombs[i].m_Free == false)
{
m_bombs[i].updateInfo();
}
}
在 MainScene.cpp的paintEvent 中追加繪制爆炸代碼
//繪制爆炸圖片
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]);
}
}
測試,實現(xiàn)爆炸效果
音效添加步驟如下:
·添加多媒體模塊
·播放音效
12.1 添加多媒體模塊
在工程文件planeWar.pro 中修改代碼
QT += core gui multimedia
12.2 播放音效
在config.h中 添加音效的配置路徑
#define SOUND_BACKGROUND ":/res/bg.wav"
#define SOUND_BOMB ":/res/bomb.wav"
注: QSound使用時候要包含頭文件 #include<QSound>
在PlayGame中添加背景音樂
//啟動背景音樂
QSound::play(SOUND_BACKGROUND);
在爆炸時候添加爆炸音效
//播放音效
QSound::play(SOUND_BOMB);
測試音效
1、確定環(huán)境變量配置好 PATH: C:\Qt\Qt5.x.x\5.x.x\mingw53_32\bin
2、在QT中把運(yùn)行模式切換成 release 模式, 編譯。 在外層目錄中會有 release 版本的目錄.
3、將目錄中的 rcc 二進(jìn)制資源文件、可執(zhí)行程序文件(.exe) 拷貝到另外一個單獨的文件夾中.
4、進(jìn)入 cmd 命令模式,切換到可執(zhí)行程序所在的目錄. 執(zhí)行以下命令,將可執(zhí)行程序所需的庫文件拷貝到當(dāng)前目錄:
windeployqt PlaneWar.exe
5、額外可以將 ico 圖標(biāo)也拷貝到當(dāng)前可執(zhí)行程序所在的目錄。
6、啟動 HM NIS EDIT 軟件,在軟件中選擇: 文件->新建腳本向?qū)В?接下來跟著向?qū)Р僮?
7、為了讓安裝包安裝軟件也有快捷方式圖標(biāo),在生成的腳本里。進(jìn)行修改:
CreateShortCut "$DESKTOP\飛機(jī)大戰(zhàn).lnk" "$INSTDIR\PlaneWar.exe"
CreateShortCut "$DESKTOP\飛機(jī)大戰(zhàn).lnk" "$INSTDIR\PlaneWar.exe" "" "$INSTDIR\app.ico"
8、點擊菜單欄的 NSIS ,然后選擇編譯,在桌面生成安裝包。
猜你喜歡:
C++ 培訓(xùn)課程