0 项目简介
飞机大战是我们大家所熟知的一款小游戏,本教程就是教大家如何制作一款自己的飞机大战
首先我们看一下效果图
玩家控制一架小飞机,然后自动发射子弹,如果子弹打到了飞下来的敌机,则射杀敌机,并且有爆炸的特效
接下来再说明一下案例的需求,也就是我们需要实现的内容
-
滚动的背景地图
-
飞机的制作和控制
-
子弹的制作和射击
-
敌机的制作
-
碰撞检测
-
爆炸效果
-
音效添加
1 设置主场景
(1)创建工程,class name为MainScene
(2)定义一个配置文件,专门定义一些相关的参数(config.h);
(3)主场景目前只需要固定界面的大小和标题即可,即config.h中定义以下数据:
cpp
/***********************游戏数据配置***********************************/
#define GAME_WIDTH 512 // 宽度
#define GAME_HEIGHT 768 // 高度
#define GAME_TITLE "飞机大战 v1.0" // 标题
(4)在MainScene.h中定义初始化游戏场景的函数initScene,并在mainScene.cpp实现
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的目录下,输入命令
bash
rcc -binary .\res.qrc -o plane.rcc
(3)将生成好的rcc文件,放入到debug同级目录中一份;
(4)注册二进制文件。在config.h中追加
cpp
#define GAME_RES_PATH "./plane.rcc" // 资源路径
在main.cpp中修改代码
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工程文件中删除代码
cpp
删除以下代码:
RESOURCES += \
res.qrc
(5)添加图标资源。配置文件config.h中追加代码
虚拟资源路径语法如下:
" : + 前缀名 + 文件路径 "
cpp
#define GAME_ICON ":/res/app.ico" // 图标路径
在mainScene.cpp的 initScene函数中追加代码:
cpp
// 加载图片
setWindowIcon(QIcon(GAME_ICON)); // 加头文件 #include <QIcon>
运行测试:
3 地图滚动
(1)创建地图类Map;
(2)在Map.h中添加函数和成员属性
cpp
#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中添加参数
cpp
/*************************地图信息***********************************/
#define MAP_PATH ":/res/img_bg_level_3.jpg" // 地图1图片路径
#define MAP_SCROLL_SPEED 2 // 地图滚动速度
(4)在Map.cpp中实现成员函数(这里就涉及了业务逻辑)
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中添加新的定时器对象
cpp
QTimer m_Timer;
在 config.h中添加 屏幕刷新间隔
cpp
#define GAME_RATE 10 //刷新间隔,帧率 单位毫秒
在MainScene.cpp的initScene中追加代码
cpp
// 定时器设置
m_timer.setInterval(GAME_RATE);
(6)启动定时器实现地图滚动
在MainScene.h中添加新的成员函数以及成员对象
cpp
// 启动游戏 用于启动定时器对象
void playGame();
// 更新坐标
void updatePosition();
// 绘图事件
void paintEvent(QPaintEvent *event);
// 地图对象
Map m_map;
在MainScene.cpp中实现成员函数
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的构造函数中调用启动游戏函数
cpp
MainScene::MainScene(QWidget *parent)
: QWidget(parent)
{
//初始化场景
initScene();
// 启动游戏
playGame();
}
测试运行游戏,实现地图滚动
4 创建英雄飞机
(1)创建英雄飞机类HeroPlane;
(2)在英雄飞机中添加成员函数和属性
HeroPlane.h
cpp
#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中添加参数
cpp
/************************飞机配置数据***********************************/
#define HERO_PATH ":/res/hero2.png" // 飞机图片路径
(3)在heroPlane.cpp中实现函数
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中追加
cpp
// 飞机对象
HeroPlane m_hero;
在MainScene.cpp的paintEvent中追加代码
cpp
// 绘制英雄飞机
painter.drawPixmap(m_hero.m_x, m_hero.m_y, m_hero.m_plane);
此时测试,飞机已经可以显示在屏幕中了
(5)利用鼠标拖拽飞机。在MainScene.h中添加鼠标事件
cpp
// 鼠标移动事件
void mouseMoveEvent(QMouseEvent*);
重写鼠标移动事件
cpp
// 鼠标移动事件
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);
}
测试飞机可以拖拽