引言
这个项目我是作为Qt入门的练手项目,整个项目做下来只能不停感慨:对于Qt的库太不熟了,这么多繁杂的库,对于我这个新手来说根本不知道怎么办,就算是查文档也不知道从哪下手。网上去搜大佬的建议,也就两个字多敲,此篇就作为我Qt入门项目的第一篇,是跟着b站教程敲的,后面我在有一定水平之后会再上传完全自己写的Qt项目于Qt专栏中
运行情况概述
- 效果图:
- 鼠标拖拽移动战机,敌方战机随机刷新,具有背景音效和敌人击杀音效,同时具有击败敌机的爆炸特效
源代码
- PlaneWar.pro
#-------------------------------------------------
#
# Project created by QtCreator 2023-03-24T12:18:41
#
#-------------------------------------------------
QT += core gui
#用来包含QSound模块
QT += core gui multimedia
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = PlaneWar
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
SOURCES += \
main.cpp \
mainscene.cpp \
map.cpp \
heroplane.cpp \
bullet.cpp \
enemyplane.cpp \
bomb.cpp
HEADERS += \
mainscene.h \
config.h \
map.h \
heroplane.h \
bullet.h \
enemyplane.h \
bomb.h
- bomb.h
#ifndef BOMB_H
#define BOMB_H
#include<QVector>
#include<QPixmap>
#include"config.h"
#include<QString>
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;
};
#endif // BOMB_H
- bullet.h
#ifndef BULLET_H
#define BULLET_H
#include<QPixmap>
#include<QRect>
#include"config.h"
#include<QPainter>
class Bullet
{
public:
Bullet();
//更新子弹坐标
void updataPosition();
//子弹资源对象
QPixmap m_Bullet;
//子弹坐标
int m_X;
int m_Y;
//子弹移动速度
int m_Speed;
//子弹是否闲置
bool m_Free;
//子弹的矩形边框(用于碰撞检测)
QRect m_Rect;
};
#endif // BULLET_H
- config.h
#ifndef CONFIG_H
#define CONFIG_H
/**************游戏配置数据***************/
#define GAME_HEIGHT 768 //高度
#define GAME_WIDTH 512 //宽度
#define GAME_TITLE "飞机大战 V1.0" //标题
#define GAME_RES_PATH "./plane.rcc" //rcc文件路径
#define GAME_ICON ":res/app.ico"
#define GAME_RATE 10 //定时器刷新时间间隔,单位是毫秒
/**************地图配置数据***************/
#define MAP_PATH ":/res/img_bg_level_1.jpg" //地图路径
#define MAP_SCROLL_SPEED 2 //地图滚动速度
/**************飞机配置属性**************/
#define HERO_PATH ":/res/hero2.png" //飞机路径
/***********子弹配置数据**************/
#define BULLET_PATH ":/res/bullet_1.png" //子弹图片路径
#define BULLET_SPEED 5 //子弹移动速度
#define BULLET_NUM 30 //弹匣中子弹的数量
#define BULLET_INTERVAL 20 //子弹发射间隔
/***********敌机配置文件***************/
#define ENEMY_PATH ":res/img-plane_3.png" //敌机资源图片
#define ENEMY_SPEED 5 //敌机移动速度
#define ENEMY_NUM 20 //敌机总数量
#define ENEMY_INTERVAL 30 //敌机出场时间间隔
/************爆炸配置数据***************/
#define BOMB_PATH ":/res/bomb-%1.png" //爆炸资源图片,百分号%是占位符参数,是不确定的数
#define BOMB_NUM 20 //爆炸数量
#define BOMB_MAX 7 //爆炸图片最大索引
#define BOMB_INTERVAL 20 //爆炸切图时间间隔
/*************音效配置数据****************/
#define SOUND_BACKGROUND ":/res/bg.wav" //背景音乐
#define SOUND_BOMB ":/res/bomb.wav" //爆炸音效
#endif // CONFIG_H
- enemyplane.h
#ifndef ENEMYPLANE_H
#define ENEMYPLANE_H
#include<QPixmap>
#include<QRect>
#include"config.h"
class EnemyPlane
{
public:
EnemyPlane();
//更新坐标
void updatePosition();
//敌机资源对象
QPixmap m_enemy;
//位置
int m_X;
int m_Y;
//敌机的矩形边框(碰撞检测)
QRect m_Rect;
//状态
bool m_Free;
//速度
int m_Speed;
};
#endif // ENEMYPLANE_H
- heroplane.h
#ifndef HEROPLANE_H
#define HEROPLANE_H
#include<QRect>
#include<QPixmap>
#include"bullet.h"
#include<QPainter>
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;
};
#endif // HEROPLANE_H
- mainscene.h
#ifndef MAINSCENE_H
#define MAINSCENE_H
#include <QWidget>
#include"config.h"
#include<QIcon>
#include<QTimer>//定时器
#include<QPaintEvent>
#include"map.h"
#include<QPAinter>
#include"heroplane.h"
#include<QMouseEvent>
#include"bullet.h"
#include"enemyplane.h"
#include<ctime>
#include<bomb.h>
#include<QSound>
class MainScene : public QWidget
{
Q_OBJECT
public:
MainScene(QWidget *parent = 0);
~MainScene();
//初始化场景
void initScene();
//启动游戏
void playGame();
//更新所有游戏中元素的坐标
void updataPosition();
//绘制到屏幕中 (函数名称不能改,是个Qt绘图事件)
void paintEvent(QPaintEvent *);
//重写鼠标移动事件 (这个函数名是个系统函数,函数名不可改)
void mouseMoveEvent(QMouseEvent *);
//地图对象
Map m_map;
//飞机对象
HeroPlane m_Hero;
//定时器
QTimer m_Timer;
// //测试子弹对象
// Bullet temp_Bullet;
//敌机出场
void enemyToScene();
//敌机数组
EnemyPlane m_enemys[ENEMY_NUM];
//敌机出场间隔记录
int m_recorder = 0;
//随机数种子
srand( unsigned int time = 0 );
//碰撞检测
void colisionDetection();
//爆炸数组
Bomb m_bombs[BOMB_NUM];
};
#endif // MAINSCENE_H
- map.h
#ifndef MAP_H
#define MAP_H
#include<QPixmap>
#include<config.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;
};
#endif // MAP_H
- bomb.cpp
#include "bomb.h"
#include"config.h"
Bomb::Bomb()
{
//将所有爆炸的Pixmap放入数组中
for(int i=1;i<BOMB_MAX;i++){
//str类似于":/res/bomb-1.png"
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()
{
//空闲状态下的爆炸效果直接return
if(m_Free){
return;
}
m_Recoder++;
//如果爆炸的时间未到达爆炸间隔,不需要切图,直接return
if(m_Recoder<BOMB_INTERVAL){
return;
}
//重置记录
m_Recoder = 0;
//切换爆炸播放的图片的下标
m_index++;
//数组中下标从0开始计算,最后一个图片的下标为6
//计算的下标大于6,重置为0,爆炸的效果重置为空闲
if(m_index>=BOMB_MAX-1){
m_index=0;
m_Free=true;
}
}
- bullet.cpp
#include "bullet.h"
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.setWidth(m_Bullet.height());
m_Rect.moveTo(m_X,m_Y);
}
void Bullet::updataPosition(){
//空闲状态下的子弹,不需要计算坐标
if(m_Free){
return;
}
//子弹向上移动
m_Y -= m_Speed;
m_Rect.moveTo(m_X,m_Y);
//子弹位置超出屏幕,重新变为空闲状态
if(m_Y <= -m_Rect.height()){
m_Free = true;
}
}
- enemyplane.cpp
#include "enemyplane.h"
EnemyPlane::EnemyPlane()
{
//敌机资源加载
m_enemy.load(ENEMY_PATH);
//敌机位置
m_X = -m_enemy.width();
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;
}
}
- heroplane.cpp
#include "heroplane.h"
#include"config.h"
HeroPlane::HeroPlane()
{
//初始化加载飞机图片资源
m_Plane.load(HERO_PATH);
//初始化飞机坐标
m_X = (GAME_WIDTH - 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){
m_X = x;
m_Y = y;
m_Rect.moveTo(m_X,m_Y);
}
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 - 10;
m_Bullets[i].m_Y = m_Y - 10;
break;
}
}
}
- main.cpp
#include "mainscene.h"
#include <QApplication>
#include<QResource>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QResource::registerResource("./plane.rcc");
MainScene w;
w.show();
return a.exec();
}
- mainscene.cpp
#include "mainscene.h"
MainScene::MainScene(QWidget *parent)
: QWidget(parent)
{
//调用初始化场景
initScene();
//启动游戏
playGame();
}
MainScene::~MainScene()
{
}
void MainScene::initScene()
{
//设置窗口的固定尺寸
setFixedSize(GAME_WIDTH,GAME_HEIGHT);
//设置标题
setWindowTitle(GAME_TITLE);
//加载图标
setWindowIcon(QIcon(GAME_ICON));
//定时器设置
m_Timer.setInterval(GAME_RATE);//设置刷新间隔
//敌机出场时间间隔,初始化
m_recorder = 0;
}
void MainScene::playGame(){
//启动背景音乐
QSound::play(SOUND_BACKGROUND);
//启动定时器
m_Timer.start();
//监听定时器发送的信号
connect(&m_Timer,&QTimer::timeout,[=](){
//敌机出场
enemyToScene();
//更新游戏中元素的坐标
updataPosition();
//绘制到屏幕中
update();//系统函数
//碰撞检测的调用
colisionDetection();
});
}
void MainScene::updataPosition(){
//更新地图的坐标
m_map.mapPosition();
// //测试
// temp_Bullet.m_Free = false;
// temp_Bullet.updataPosition();
//发射子弹
m_Hero.shoot();
//计算所有非空闲子弹的当前坐标
for(int i=0;i<BULLET_NUM;i++){
//如果非空闲,计算发射的位置
if(m_Hero.m_Bullets[i].m_Free == false){
m_Hero.m_Bullets[i].updataPosition();
}
}
//敌机出场
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();
}
}
}
void MainScene::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);
//绘制英雄飞机
painter.drawPixmap(m_Hero.m_X,m_Hero.m_Y,m_Hero.m_Plane);
// //测试子弹
// painter.drawPixmap(temp_Bullet.m_X,temp_Bullet.m_Y,temp_Bullet.m_Bullet);
//绘制子弹
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);
}
}
//绘制爆炸
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]);
}
}
//绘制敌机
for(int i=0;i<ENEMY_NUM;i++){
painter.drawPixmap(m_enemys[i].m_X,m_enemys[i].m_Y,m_enemys[i].m_enemy);
}
}
void MainScene::mouseMoveEvent(QMouseEvent *event){
int x = event->x() - m_Hero.m_Rect.width() * 0.5;
int y = event->y() - m_Hero.m_Rect.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);
}
void MainScene::enemyToScene()
{
m_recorder++;
//未到达出场间隔,直接return
if(m_recorder<ENEMY_INTERVAL){
return;
}
m_recorder = 0;
for(int i=0;i<ENEMY_INTERVAL;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;
}
}
}
void MainScene::colisionDetection()
{
//遍历所有非空闲的敌机
for(int i=0;i<ENEMY_NUM;i++){
//如果时空闲的飞机,执行下一次循环
if(m_enemys[i].m_Free){
continue;
}
//便利所有的非空闲的子弹
for(int j=0;j<BULLET_NUM;j++){
if(m_Hero.m_Bullets[j].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[j].m_Free = true;
}
}
//播放爆炸效果
for(int k=0;k<BOMB_NUM;k++){
if(m_bombs[k].m_Free){
//播放爆炸的音效
QSound::play(SOUND_BOMB);
//空闲的爆炸,可以播放
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;
}
}
}
}
- map.cpp
#include "map.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 += m_scroll_speed;
if(m_map1_posY>=0){
m_map1_posY = -GAME_HEIGHT;
}
//处理第二张图片的滚动位置
m_map2_posY += m_scroll_speed;
if(m_map2_posY>= GAME_HEIGHT){
m_map2_posY = -0;
}
}
总结
- 需要图片等资源的可以留下联系方式,我发给你
- 注意:Qt会由于资源文件太大无法解析而报错,需要改用导入rcc文件解决,解决方法在我另一篇文章中有写Qt报错之OutOfMemoryAllocating
- 这个项目的难度不大,主要是能够多了解很多Qt的数据类型,提高自己的熟练度,对于以后自主进行Qt项目开发有着较大的帮助
- 但是这个项目也有很多不足:没有串口间通信等内容,就是说这个技术范围仅仅在Qt,很显然这种东西是没法写进简历的,一个有水平的项目应该需要更为复杂、多样的技能。