华清大作业 QT实现飞机大战小游戏

在学习完QT后,我尝试做了一下飞机大战这个小游戏。

首先是小游戏需要实现的功能:

        1.滚动的背景

        2.子弹的制作和射击

        3.敌人的制作

        4.爆炸效果

首先我们创建好项目后,我们开始创建新的头文件,用来配置所有的数据。这需要我们先把飞机大战需要的png图片素材先整理好,代码如下:

/**********  游戏配置数据 **********/
#define GAME_WIDTH  512  //宽度
#define GAME_HEIGHT 768  //高度
#define GAME_TITLE "飞机大战 sjn" //标题
#define GAME_RES_PATH":/resnew/app.ico"
#define GAME_ICON ":/res/app.ico"  //图标路径
#define GAME_RATE  10  //单位毫秒
 
/**********  地图配置数据 **********/
#define MAP_PATH  ":/resnew/img_bg_level_5.jpg" //地图图片路径
#define MAP_SCROLL_SPEED 2  //地图滚动速度
 
/**********  飞机配置数据 **********/
#define HERO_PATH ":/resnew/app.ico"
 
/**********  子弹配置数据 **********/
#define BULLET_PATH ":/resnew/bullet_1.png"   //子弹图片路径
#define BULLET_SPEED 5  //子弹移动速度
 
#define BULLET_NUM 30   //弹匣中子弹总数
#define BULLET_INTERVAL 20 //发射子弹时间间隔
 
/**********  敌机配置数据 **********/
#define ENEMY_PATH  ":/resnew/img-plane_1.png"  //敌机资源图片
#define ENEMY_SPEED 5  //敌机移动速度
#define ENEMY_NUM   20  //敌机总数量
#define ENEMY_INTERVAL  30  //敌机出场时间间隔
 
/**********  爆炸配置数据 **********/
#define BOMB_PATH  ":/resnew/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

然后我们要开始写地图功能的编写

        为了实现地图的滚动,也就是需要循环,这时可以调用游戏运行定时器,启动定时器,监听定时器信号实现游戏循环,看起来就是地图的滚动,代码如下

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

map.cpp:

#include "map.h"
#include "config.h"
 
Map::Map()
{
    //初始化加载地图对象
    m_map1.load(MAP_PATH);
    m_map2.load(MAP_PATH);
 
    //设置坐标
    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.在MainScene.h中添加新的成员函数以及成员对象

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

在MainScene.cpp中实现成员函数,代码如下:

// 启动定时器
    m_Timer.start();
 
    //监听定时器的信号
    connect(&m_Timer , &QTimer::timeout,[=](){
        //敌机出场
        enemyToScene();
        //更新游戏中所有元素的坐标
        updatePosition();
        //游戏中的元素 绘制到屏幕中
        update(); //再调用paintEvent函数

做好了这些,飞机大战游戏的大框架已经构建好了,之后我们来添加自己想要的素材和功能,

首先是我们自己操控的飞机,,步骤如下:

        1.创建文件和类

        2.添加成员函数和成员属性

        3.实现成员函数

        4.创建飞机对象并显示

        5.可以拖拽

 当我们创建好文件和类后,需要添加属性代码:

 //发射子弹
    void shoot();
    //设置飞机位置
    void setPosition(int x, int y);
 
public:
    //飞机资源 对象
    QPixmap m_Plane;
 
    //飞机坐标
    int m_X;
    int m_Y;
 
    //飞机的矩形边框
    QRect m_Rect;

成员函数实现

        因为上边config.h中,我展示的是所有代码,所以就不展示此处的飞机配置参数设置的代码了,

所以在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() - 100;
 
    //矩形边框 碰撞检测用
    m_Rect.setWidth(m_Plane.width());
    m_Rect.setHeight(m_Plane.height());
    m_Rect.moveTo(m_X,m_Y);
 
    //初始化间隔记录变量
    m_recorder = 0;
}
 
void HeroPlane::setPosition(int x, int y)
{
    m_X = x;
    m_Y = y;
    m_Rect.moveTo(m_X,m_Y);
}

加入素材,在MainScene.h中添加代码如下:

//创建飞机对象
    HeroPlane m_hero;

MainScene.cpp中:

 //绘制飞机
    painter.drawPixmap(m_hero.m_X,m_hero.m_Y,m_hero.m_Plane);

最后我们编写拖拽飞机的指令

MainScene.h:

void mouseMoveEvent(QMouseEvent *);

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

然后就是子弹的功能,创建好子弹的文件和类后,我们需要在bullet.h中添加代码如下:

#ifndef BULLET_H
#define BULLET_H
#include "config.h"
#include <QPixmap>
 
class Bullet
{
public:
    Bullet();
 
    //更新子弹坐标
    void updatePosition();
 
public:
    //子弹资源对象
    QPixmap m_Bullet;
    //子弹坐标
    int m_X;
    int m_Y;
    //子弹移动速度
    int m_Speed;
    //子弹是否闲置
    bool m_Free;
    //子弹的矩形边框(用于碰撞检测)
    QRect m_Rect;
};
 
#endif // BULLET_H

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.setHeight(m_Bullet.height());
    m_Rect.moveTo(m_X,m_Y);
}
 
void Bullet::updatePosition()
{
    //如果子弹是空闲状态,不需要计算坐标
    if(m_Free)
    {
        return;
    }
 
    //子弹向上移动
    m_Y -=m_Speed;
    m_Rect.moveTo(m_X,m_Y);
 
    if(m_Y <= - m_Rect.height())
    {
        m_Free = true;
    }
 
}

此时子弹的代码已经编写完成,我们需啊使子弹从飞机的位置发射出,这时我们在heroplane.cpp中添加代码:

void HeroPlane::shoot()
{
    //累加事件间隔记录的变量
    m_recorder++;
 
 
    //如果记录数字 未达到发射间隔,直接return
    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 - 25 ;
            break;
        }
 
    }
}

当然别忘了,在主场景中也实现添加子弹

MainScene.cpp:

//发射子弹
    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].updatePosition();
        }
 
    }

然后我们开始敌人飞机的功能实现

首先我们需要创建好文件和类,然后在.h文件中添加成员函数,和成员属性

#ifndef ENEMYPLANE_H
#define ENEMYPLANE_H
 
 
#include <QPixmap>
 
class EnemyPlane
{
public:
    EnemyPlane();
 
    //更新坐标
    void updatePosition();
public:
    //敌机资源对象
    QPixmap m_enemy;
 
    //位置
    int m_X;
    int m_Y;
 
    //敌机的矩形边框(碰撞检测)
    QRect m_Rect;
 
    //状态
    bool m_Free;
 
    //速度
    int m_Speed;
};
 
#endif // ENEMYPLANE_H

然后在.cpp中实现成员函数:

#include "enemyplane.h"
#include "config.h"
 
EnemyPlane::EnemyPlane()
{
    //敌机资源加载
    m_enemy.load(ENEMY_PATH);
 
    //敌机位置
    m_X = 0;
    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_Free = true;
    }
}

在MainScene.h中实行敌人飞机的出场

//敌机出场
    void enemyToScene();
    //敌机数组
    EnemyPlane m_enemys[ENEMY_NUM];
    //敌机出场间隔记录
    int m_recorder;
实现成员函数 enemyToScene
 
void MainScene::enemyToScene()
{
    //累加出场间隔
    m_recorder++;
    if(m_recorder < ENEMY_INTERVAL)
    {
        return;
    }
 
    m_recorder = 0;
 
    for(int i = 0 ; i< ENEMY_NUM;i++)
    {
        if(m_enemys[i].m_Free)
        {
            //敌机空闲状态改为false
            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::collisionDetection()
{
    //遍历所有非空闲的敌机
    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))
            {
                //播放音效
                QSound::play(SOUND_BOMB);
 
 
                m_enemys[i].m_Free = true;
                m_hero.m_bullets[j].m_Free = true;
最后我们添加爆炸的效果,

bomb.h:

#ifndef BOMB_H
#define BOMB_H
 
#include "config.h"
#include <QPixmap>
#include <QVector>
 
class Bomb
{
public:
    Bomb();
 
    //更新信息(播放图片下标、播放间隔)
    void updateInfo();
 
public:
 
    //放爆炸资源数组
    QVector<QPixmap> m_pixArr;
 
    //爆炸位置
    int m_X;
    int m_Y;
 
    //爆炸状态
    bool m_Free;
 
    //爆炸切图的时间间隔
    int m_Recoder;
 
    //爆炸时加载的图片下标
    int m_index;
};
 
#endif // BOMB_H

bomb.cpp:

#include "bomb.h"
 
Bomb::Bomb()
{
    //初始化爆炸图片数组
    for(int i = 1 ;i <= BOMB_MAX ;i++)
    {
 
        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()
{
    //空闲状态
    if(m_Free)
    {
        return;
    }
 
    m_Recoder++;
    if(m_Recoder < BOMB_INTERVAL)
    {
        //记录爆炸间隔未到,直接return,不需要切图
        return;
    }
    //重置记录
    m_Recoder = 0;
 
    //切换爆炸播放图片
    m_index++;
    //注:数组中的下标从0开始,最大是6
    //如果计算的下标大于6,重置为0
    if(m_index > BOMB_MAX-1)
    {
        m_index = 0;
        m_Free = true;
    }
}

MainScene.h中添加爆炸数组:

//爆炸数组
    Bomb m_bombs[BOMB_NUM];
在碰撞检测成员函数中,当发生碰撞时,设置爆炸对象的信息:
for(int k = 0 ; k < BOMB_NUM;k++)
              {
                  if(m_bombs[k].m_Free)
                  {
                      //爆炸状态设置为非空闲
                      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;
                  }
              }
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]);
        }
    }

至此,简易的飞机大战小游戏已经完成

最终结果展示:

未能实现功能:

        其实还可以有更多的功能实现,比如做一个背景音乐,做一个闯关系统,血量,以及BOSS的挑战,

总结:

        学习完QT后,听老师上课讲会发现可以听懂,但自己动手做项目,发现很多知识需要反复去看课堂回放才可以慢慢理解,不懂的知识夜学会了百度来解决问题,很多逻辑性的代码构建需要长时间理解。 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
 
 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值