【QT】飞机大战

0 项目简介

飞机大战是我们大家所熟知的一款小游戏,本教程就是教大家如何制作一款自己的飞机大战

首先我们看一下效果图

玩家控制一架小飞机,然后自动发射子弹,如果子弹打到了飞下来的敌机,则射杀敌机,并且有爆炸的特效

接下来再说明一下案例的需求,也就是我们需要实现的内容

  • 滚动的背景地图

  • 飞机的制作和控制

  • 子弹的制作和射击

  • 敌机的制作

  • 碰撞检测

  • 爆炸效果

  • 音效添加

1 设置主场景

(1)创建工程,class name为MainScene

(2)定义一个配置文件,专门定义一些相关的参数(config.h);

(3)主场景目前只需要固定界面的大小和标题即可,即config.h中定义以下数据:

/***********************游戏数据配置***********************************/
#define GAME_WIDTH  512                             // 宽度
#define GAME_HEIGHT 768                             // 高度
#define GAME_TITLE "飞机大战 v1.0"                   // 标题

(4)在MainScene.h中定义初始化游戏场景的函数initScene,并在mainScene.cpp实现

// 初始化游戏场景
void MainScene::initScene()
{
    //初始化窗口大小
    setFixedSize(GAME_WIDTH,GAME_HEIGHT);

    //设置窗口标题
    setWindowTitle(GAME_TITLE);
}

(5)在MainScene的构造函数中调用initScene函数。运行程序,此时界面出现

2 资源导入

(1)资源导入见:【QT】资源文件导入_复制其他项目中的文件到qt项目中_StudyWinter的博客-CSDN博客

(2)将qrc文件生成rcc二进制文件,利用cmd打开终端,定位到res.qrc的目录下,输入命令

rcc -binary .\res.qrc -o plane.rcc

(3)将生成好的rcc文件,放入到debug同级目录中一份;

(4)注册二进制文件。在config.h中追加

#define GAME_RES_PATH "./plane.rcc"                 // 资源路径

在main.cpp中修改代码

#include "mainscene.h"
#include <QResource>
#include <QApplication>
#include "config.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    // 注册外部的二进制资源文件,注意加入头文件
    QResource::registerResource(GAME_RES_PATH);
    MainScene w;
    w.show();
    return a.exec();
}

此时,qrc文件已经没用了,删除即可!最简单的删除方式就是 .pro工程文件中删除代码

删除以下代码:
RESOURCES += \
    res.qrc

(5)添加图标资源。配置文件config.h中追加代码

虚拟资源路径语法如下:

" : + 前缀名 + 文件路径 "

#define GAME_ICON ":/res/app.ico"                   // 图标路径

在mainScene.cpp的 initScene函数中追加代码:

// 加载图片
setWindowIcon(QIcon(GAME_ICON));    // 加头文件 #include <QIcon>

运行测试:

3 地图滚动

(1)创建地图类Map;

(2)在Map.h中添加函数和成员属性

#ifndef MAP_H
#define MAP_H
#include <QPixmap>


class Map
{
public:
    // 构造函数
    Map();

    // 地图滚动坐标计算
    void mapPosition();

public:
    // 地图图片对象
    QPixmap m_map1;
    QPixmap m_map2;

    // 地图Y轴坐标
    int m_map1_posY;
    int m_map2_posY;

    // 地图滚动幅度
    int m_scroll_speed;
};

#endif // MAP_H

(3)在配置文件config.h中添加参数

/*************************地图信息***********************************/
#define MAP_PATH ":/res/img_bg_level_3.jpg"        // 地图1图片路径
#define MAP_SCROLL_SPEED 2                          // 地图滚动速度

(4)在Map.cpp中实现成员函数(这里就涉及了业务逻辑)

#include "map.h"
#include "config.h"

Map::Map()
{
    // 加载地图对象,两张图无缝衔接
     m_map1.load(MAP_PATH);
     m_map2.load(MAP_PATH);

     // 设置地图起始y坐标
     // 窗口是(0.0)点,第一张图在上面,所以y轴的坐标是-GAME_HEIGHT
     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;
    }
}

(5)添加定时器

关于定时器详见:【QT】定时器_qt 定时器-CSDN博客

在mainScene.h中添加新的定时器对象

QTimer m_Timer;

在 config.h中添加 屏幕刷新间隔

#define GAME_RATE  10                               //刷新间隔,帧率 单位毫秒

在MainScene.cpp的initScene中追加代码

  // 定时器设置
  m_timer.setInterval(GAME_RATE);

(6)启动定时器实现地图滚动

在MainScene.h中添加新的成员函数以及成员对象

    // 启动游戏  用于启动定时器对象
    void playGame();
    // 更新坐标
    void updatePosition();
    // 绘图事件
    void paintEvent(QPaintEvent *event);

    // 地图对象
    Map m_map;

在MainScene.cpp中实现成员函数

// 启动游戏  用于启动定时器对象
void MainScene::playGame()
{
    // 启动定时器
    m_timer.start();

    // 监听定时器
    connect(&m_timer, &QTimer::timeout, this, [=]() {
        // 更新游戏中元素的坐标
        updatePosition();
        // 重新绘制地图
        update();
    });
}

// 更新坐标
void MainScene::updatePosition()
{
    // 更新地图坐标
    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);
}

在MainScene的构造函数中调用启动游戏函数

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

测试运行游戏,实现地图滚动

4 创建英雄飞机

(1)创建英雄飞机类HeroPlane;

(2)在英雄飞机中添加成员函数和属性

HeroPlane.h

#ifndef HEROPLANE_H
#define HEROPLANE_H
#include <QPixmap>
#include <QRect>
#include "bullet.h"
#include "config.h"


class HeroPlane
{
public:
    HeroPlane();
    // 发射子弹
    void shoot();
    // 设置飞机位置
    void setPosition(int x, int y);

public:
    // 飞机资源对象
    QPixmap m_plane;

    // 飞机坐标
    int m_x;
    int m_y;

    // 飞机的矩形边框
    QRect m_rect;
};

#endif // HEROPLANE_H

(2)在配置文件config.h中添加参数

/************************飞机配置数据***********************************/
#define HERO_PATH ":/res/hero2.png"                 // 飞机图片路径

(3)在heroPlane.cpp中实现函数

#include "heroplane.h"
#include "config.h"

HeroPlane::HeroPlane()
{
    // 加载飞机资源
    m_plane.load(HERO_PATH);

    // 初始化飞机坐标
    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::shoot()
{
    
}

// 设置飞机位置
void HeroPlane::setPosition(int x, int y)
{
    m_x = x;
    m_y = y;
    m_rect.moveTo(m_x, m_y);
}

(4)创建飞机并显示。MainScene.h中追加

    // 飞机对象
    HeroPlane m_hero;

在MainScene.cpp的paintEvent中追加代码

  // 绘制英雄飞机
  painter.drawPixmap(m_hero.m_x, m_hero.m_y, m_hero.m_plane);

此时测试,飞机已经可以显示在屏幕中了

(5)利用鼠标拖拽飞机。在MainScene.h中添加鼠标事件

 // 鼠标移动事件
 void mouseMoveEvent(QMouseEvent*);

重写鼠标移动事件

// 鼠标移动事件
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);
}

测试飞机可以拖拽

5 子弹制作

6 玩家发射子弹

7 敌机制作

8 碰撞检测

9 爆炸效果

10 音效添加

11 打包发布

资源来自:01 飞机大战项目演示以及需要分析_哔哩哔哩_bilibili

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/140940.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言--假设共有鸡、兔30只,脚90只,求鸡、兔各有多少只​

一.题目描述 假设共有鸡、兔30只&#xff0c;脚90只&#xff0c;求鸡、兔各有多少只&#xff1f; 二.思路分析 本题是一个典型的穷举法例题&#xff0c;而穷举法&#xff0c;最重要的就是条件判断。⭐⭐ 本题中的条件很容易发现&#xff1a; 假设鸡有x只&#xff0c;兔有y只…

基于php+thinphp+vue的教材管理系统

运行环境 开发语言&#xff1a;PHP 数据库:MYSQL数据库 应用服务:apache服务器 使用框架:ThinkPHPvue 开发工具:VScode/Dreamweaver/PhpStorm等均可 项目简介 教材管理系统&#xff0c;主要的模块包括首页、个人中心、学生管理、老师管理、教材征订管理、教师教材退订管理、…

电源基础元件

文章目录 电源基础元件理想电压源理想电流源受控电源 电源基础元件 理想电压源 定义 其两端电压总能保持定值或一定的时间函数&#xff0c;其值与流过它的电流i无关的元件叫理想电压源 理想电压源的电压、电流关系 1.电源两端电压由电源本身决定&#xff0c;与外电路无关&…

【兔子王赠书第7期】机器学习与人工智能实战:基于业务场景的工程应用

文章目录 写在前面机器学习推荐图书写给读者前言本书面向的读者我为什么要写这本书运行本书的示例代码本书导航本书采用的约定使用代码示例 推荐理由粉丝福利写在后面 写在前面 新的一周开始啦&#xff0c;本周博主给大家带来《机器学习与人工智能实战&#xff1a;基于业务场景…

五分钟利用Vite创建Vue项目

1.准备工具 Vite是尤雨溪团队开发的&#xff0c;官方称是下一代新型前端构建工具&#xff0c;能够显著提升前端开发体验。 上面称是下一代&#xff0c;当前一代当然是我们熟悉的webpack Vite 优势 开发环境中&#xff0c;无需打包操作&#xff0c;可快速的冷启动。轻量快速…

初始MySQL(五)(自我复制数据,合并查询,外连接,MySQL约束:主键,not null,unique,foreign key)

目录 表复制 自我复制数据(蠕虫复制) 合并查询 union all(不会去重) union(会自动去重) MySQL表的外连接 左连接 右连接 MySQL的约束 主键 not null unique(唯一) foreign key(外键) 表复制 自我复制数据(蠕虫复制) #为了对某个sql语句进行效率测试,我们需要海量…

【GEE学习日记】GEE下载ERA5指定小时数据

1 背景 ERA5数据集提供了逐小时的气象产品&#xff0c;最近做实验需要用到指定日期的14点的气象数据&#xff0c;所以学习了一下。 我的目的&#xff1a;获取2003年每月5&#xff0c;15&#xff0c;25日 14点的空气温度 2 代码 var roi table.geometry(); // table是我上传…

【计算机网络】VRRP协议理论和配置

目录 1、VRRP虚拟路由器冗余协议 1.1、协议作用 1.2、名词解释 1.3、简介 1.4、工作原理 1.5、应用实例 2、 VRRP配置 2.1、配置命令 2.2、拓扑与配置&#xff1a; 1、VRRP虚拟路由器冗余协议 1.1、协议作用 虚拟路由冗余协议(Virtual Router Redundancy Protocol&am…

11.13 牛客刷题8/10

11.13 信号完整性 指针地址 的加减&#xff0c;注意 最后转为16进制

插件式换肤框架原理解析

作者&#xff1a;ak 插件换肤实现原理概述 收集到需要换肤的控件确定控件中需要换肤的属性和资源ID加载插件APK&#xff0c;构造AssetManager并生成插件的Resource类&#xff0c;就可以加载插件包中的资源执行换肤&#xff1a;通过ID加载插件包中的资源&#xff0c;然后再通过…

C# 并发编程

C# 并发编程 前言 对于现在很多编程语言来说&#xff0c;多线程已经得到了很好的支持&#xff0c; 以至于我们写多线程程序简单&#xff0c;但是一旦遇到并发产生的问题就会各种尝试。 因为不是明白为什么会产生并发问题&#xff0c;并发问题的根本原因是什么。 接下来就让…

基于ssm课堂考勤系统-计算机毕设 附源码 50546

ssm课堂考勤系统 摘 要 21世纪的今天&#xff0c;随着社会的不断发展与进步&#xff0c;人们对于信息科学化的认识&#xff0c;已由低层次向高层次发展&#xff0c;由原来的感性认识向理性认识提高&#xff0c;管理工作的重要性已逐渐被人们所认识&#xff0c;科学化的管理&am…

Go的优雅退出

Go优雅退出/停机以前主要通过signal来实现&#xff0c;当然现在也是通过signal来实现&#xff0c;只是从go 1.16开始&#xff0c;新增了更加友好的API: func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) 该…

北邮22级信通院数电:Verilog-FPGA(9)第九周实验(2)实现下降沿触发的JK触发器(带异步复位和置位功能)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 JK.v module JK (input clk,input J,input K,input…

【C++类和对象下:解锁面向对象编程的奇妙世界】

【本节目标】 1. 再谈构造函数 2. Static成员 3. 友元 4. 内部类 5.匿名对象 6.拷贝对象时的一些编译器优化 7. 再次理解封装 1. 再谈构造函数 1.1 构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。…

CRM系统的销售管理功能,你了解多少?

很多时候&#xff0c;CRM系统直接被当做是销售管理系统&#xff0c;其实无可厚非&#xff0c;因为CRM系统本身是围绕销售逻辑来实现的&#xff0c;不仅可以有效的优化商机&#xff0c;对商机的把握也是十分精确。下面我们就来详细说说&#xff0c;CRM系统都有哪些销售管理功能&…

2023年数维杯国际大学生数学建模挑战赛A题

当大家面临着复杂的数学建模问题时&#xff0c;你是否曾经感到茫然无措&#xff1f;作为2022年美国大学生数学建模比赛的O奖得主&#xff0c;我为大家提供了一套优秀的解题思路&#xff0c;让你轻松应对各种难题。 cs数模团队在数维杯前为大家提供了许多资料的内容呀&#xff0…

2023.11.13【读书笔记】丨生物信息学与功能基因组学(第六章 多重序列比对 下)

目录 6.4 多重序列比对数据库6.5 基因组区域的多重序列比对6.6 展望6.7 常见问题总结 6.4 多重序列比对数据库 Pfam&#xff1a;基于谱隐马尔可夫模型构建的蛋白质家族数据库 SMART&#xff1a;简易分子构型研究工具&#xff0c;与细胞信号传导、细胞外结构域以及染色质功能…

炸裂!Sklearn 的 10 个宝藏级使用方法!

大家好&#xff0c;本次给大家介绍10个Sklearn方法&#xff0c;比较小众但非常好用。 1️.FunctionTransformer 虽然Sklearn中有很多内置的预处理操作可以放进pipeline管道&#xff0c;但很多时候并不能满足我们的需求。 如果是比较简单并且通过一个函数可以实现需求的情况&…

CSDN每日一题学习训练——Java版(字符串相乘、子集、删除链表的倒数第 N 个结点)

版本说明 当前版本号[20231112]。 版本修改说明20231112初版 目录 文章目录 版本说明目录字符串相乘题目解题思路代码思路补充说明参考代码 子集题目解题思路代码思路参考代码 删除链表的倒数第 N 个结点题目解题思路代码思路参考代码 字符串相乘 题目 给定两个以字符串形…