Qt实战——飞机大战1.0(全注释)

引言

这个项目我是作为Qt入门的练手项目,整个项目做下来只能不停感慨:对于Qt的库太不熟了,这么多繁杂的库,对于我这个新手来说根本不知道怎么办,就算是查文档也不知道从哪下手。网上去搜大佬的建议,也就两个字多敲此篇就作为我Qt入门项目的第一篇,是跟着b站教程敲的,后面我在有一定水平之后会再上传完全自己写的Qt项目于Qt专栏中

运行情况概述

  • 效果图:在这里插入图片描述
  • 鼠标拖拽移动战机,敌方战机随机刷新,具有背景音效和敌人击杀音效,同时具有击败敌机的爆炸特效

源代码

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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

  1. 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


  1. 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;
    }
}

  1. 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;
    }
}

  1. 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;
    }
}

  1. 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;
        }
    }
}

  1. 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();
}

  1. 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;
            }
        }
    }
}

  1. 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;
    }
}

总结

  1. 需要图片等资源的可以留下联系方式,我发给你
  2. 注意:Qt会由于资源文件太大无法解析而报错,需要改用导入rcc文件解决,解决方法在我另一篇文章中有写Qt报错之OutOfMemoryAllocating
  3. 这个项目的难度不大,主要是能够多了解很多Qt的数据类型,提高自己的熟练度,对于以后自主进行Qt项目开发有着较大的帮助
  4. 但是这个项目也有很多不足:没有串口间通信等内容,就是说这个技术范围仅仅在Qt,很显然这种东西是没法写进简历的,一个有水平的项目应该需要更为复杂、多样的技能。
评论 45
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

默示MoS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值