[原创]基于C++/Qt的附带图形界面的飞机大战小游戏

本文详细介绍了如何使用QT库开发一款2D飞行射击游戏,包括游戏画面的实现、游戏逻辑如飞机和子弹的移动、碰撞检测,以及使用定时器控制游戏流程。通过键盘事件处理玩家操作,动态刷新敌机和子弹位置,实现游戏交互。同时,游戏包含特殊飞机(彩蛋)设计,增加了游戏趣味性。
摘要由CSDN通过智能技术生成

话不多说,先看效果图

 

 (说明:这个是我学校C++课的作业,所以图一加了一个学校的logo,>A<)

下面我先说一下代码思路:

1.画面的实现

        这个实际上只需要用到QT的绘图库函数就可以了

        具体操作如下:

//widget构造函数中
QPixmap pix;//创建对象
pix.load(":/pic/back2.jpg");//加载图片
pix.scaled(this->width(),this->height());//限定图片尺寸

//paintEvent函数中
painter->drawPixmap(0,0,this->width(),this->height(),pix);//显示图片

         说明:我们在widget构造函数中先实例化对象,然后在paintEvent函数中调用函数显示图片就可以了。paintEvent函数是Qt的一个特殊的函数,它会在创建窗口和刷新窗口时自动调用。我们只要在调用过paintEvent函数之后更改显示飞机图片的坐标,就能实现飞机的移动。

2.游戏逻辑

这个大概包括了刷新敌方飞机,敌方飞机向下移动,我方飞机移动,发射子弹,碰撞检测五个方面。

我方飞机移动:当按下键盘时修改我方飞机坐标值。这里涉及到了一个键盘事件函数,当按下按键时会自动执行

void Widget::keyPressEvent(QKeyEvent *event)
{//包含边界判断,只能在限定区域移动
    if(x>0){
    if(event->key()==Qt::Key_Left)x=x-11; }
    if(x<510)
    if(event->key()==Qt::Key_Right)x=x+11;

    if(event->key()==Qt::Key_Space)//发射子弹
    {
    //QSound::play(":/pic/kaipao.wav");
     ex.x=x+45;
     ex.y=780;

     b.push_back(ex);
    }
    this->update();//刷新窗口
}

敌方飞机和子弹移动:在调用paintEvent函数之后更改显示飞机和子弹图片的坐标,给y坐标加上一定的数值就可以了。

下面就要用到容器的使用了:

创建两个容器,分别用来装“敌方飞机”和“子弹”,刷新敌方飞机:这个只需要用随机数就可以实现了,每隔一段时间在顶部的随机位置刷新一个飞机就可以了。具体操作就是每隔一段时间往飞机容器中增加一个数据。

发射子弹:按下键盘时往容器中加入一个数据。

而在paintEvent函数,每次调用时要显示两个容器中的所有内容。

碰撞检测:比对坐标信息,当发送碰撞时将飞机和子弹都擦除。

上述操作都是在timerEvent函数中完成的,该函数也是Qt中一个特殊函数,每隔指定的时间就会自动调用。

void Widget::timerEvent(QTimerEvent *event)
{
     if(event->timerId()==this->id1)
     {
      for(p=b.begin();p!=b.end();)
      {
          p->y-=15;//子弹向上移动
          if(p->y<0)p=b.erase(p);//子弹到达上界时自动擦除
          else ++p;

      }
      for(it=am.begin();it!=am.end();)
      {//这里利用x坐标的不同区分飞机,使之拥有不同的移动速度
          if(it->x%59==0)it->y+=10;
         else
          {
              switch (it->x%4) {
              case 0:it->y+=2;

                  break;
              case 1:it->y+=3;

                  break;
              case 2:it->y+=4;break;
              case 3:it->y+=5;
                  break;

              }

          }
          if(it->y>800)
          {//飞机到达底部,游戏结束
              this->killTimer(id1);
              this->killTimer(id2);
             it=am.erase(it);
             gamevoer=true;




          }
          else
          {
              xit=it;

              ++it;
          }
          for(xp=b.begin();xp!=b.end();)//碰撞检测
          {
              if(xp->x-xit->x<97&&xp->x-xit->x>-29&&xp->y<xit->y+75)
              {

                  xit=am.erase(xit);
                  xp=b.erase(xp);
                  sco++;
                  if(xit->x%79==0)
                  {if(bb)bb=false;
                      else bb=true;
}
              }
              ++xp;
          }
      }
     }
     else if(event->timerId()==this->id2)//刷新敌方飞机
     {
       em.x=rand()%530+10;

       em.y=0;

       am.push_back(em);
     }
     this->update();//刷新窗口
}

最后就是在绘图函数之中画出我们的飞机和子弹

void Widget::paintEvent(QPaintEvent *event)
{

    QPainter* painter=new QPainter(this);


   painter->drawPixmap(0,0,this->width(),this->height(),pix);
    painter->drawPixmap(x,780,120,100,pix1);//画己方飞机

    for(p=b.begin();p!=b.end();++p)//画子弹
    {
        if(bb)
       painter->drawPixmap(p->x,p->y,30,30,bulle);
        else  painter->drawPixmap(p->x,p->y,30,90,bulle2);

    }
    for(it=am.begin();it!=am.end();++it)
    {//根据x坐标的不同画不同种类的飞机
        if(it->x%59==0) painter->drawPixmap(it->x,it->y,97,75,jiaran);//彩蛋飞机
        else if(it->x%3==0)painter->drawPixmap(it->x,it->y,97,75,amy2);
         else if(it->x%5==0)painter->drawPixmap(it->x,it->y,97,75,amy3);
         else if(it->x%7==0)painter->drawPixmap(it->x,it->y,97,75,amy4);
         else if(it->x%11==0)painter->drawPixmap(it->x,it->y,97,75,amy5);
       else painter->drawPixmap(it->x,it->y,97,75,amy);
        if(it->x%4==1)painter->drawPixmap(it->x,it->y,97,75,hd);

    }
    if(gamevoer)painter->drawPixmap(195,30,250,250,gv);


painter->drawText(300,700,"得分:"+QString::number(sco));//显示得分
    painter->end();

}

最后是一开始就要做的准备工作,在头文件里面声明变量和函数,添加资源文件,以及在构造函数里加载图像,启动计时器(使timerEvent函数开始工作)

文件结构:需要有我画横线的三个文件(其他的是用来显示开始界面和按钮的,我在下篇文章介绍),以及图片文件,需要放在Resourse目录下

 

模块的全部代码:

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <list>
#include <QSound>
#include<QLineEdit>
using namespace std;
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
struct bullet
{
    int x;
    int y;

};
class Widget : public QWidget
{
    Q_OBJECT

public:

    int x=320,sco=0;
    list<bullet>b;
    list<bullet>::iterator p,xp;
    list<bullet>am;
    list<bullet>::iterator it,xit;
    bullet ex,em,ed;
    QPixmap pix,pix1,bulle,amy,jiaran,gv;
     QPixmap amy2,amy3,amy4,amy5,hd,bulle2;
     bool gamevoer,bb;


int id1,id2;
    Widget(QWidget *parent = nullptr);
    ~Widget();
virtual void Widget::paintEvent(QPaintEvent *event);
virtual void Widget::keyPressEvent(QKeyEvent *event);
virtual void Widget::timerEvent(QTimerEvent *event);

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

 widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QPainter>
#include <QPaintEvent>
#include <QKeyEvent>
#include <qmovie.h>
#include <ctime>
#include <QSound>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
gamevoer=false;
    this->setFixedSize(640,900);
    this->id1=this->startTimer(40);
     this->id2=this->startTimer(1000);
    pix.load(":/pic/back2.jpg");
    pix1.load(":/pic/plane1.png");
    jiaran.load(":/pic/jiaran.png");
    gv.load(":/pic/gameover.png");
bulle.load(":/pic/bullet3.png");
amy.load(":/pic/amany.png");
amy2.load(":/pic/anany3.png");
amy3.load(":/pic/amany2.png");
amy4.load(":/pic/amany6.png");
amy5.load(":/pic/amany9.png");
hd.load(":/pic/hudun.png");
bulle2.load(":/pic/zd.png");
    pix.scaled(this->width(),this->height());
    pix1.scaled(120,100);
    jiaran.scaled(97,75);
bulle.scaled(30,30);
amy.scaled(97,75);
srand(time(0));
bb=true;
}

void Widget::paintEvent(QPaintEvent *event)
{

    QPainter* painter=new QPainter(this);


   painter->drawPixmap(0,0,this->width(),this->height(),pix);
    painter->drawPixmap(x,780,120,100,pix1);

    for(p=b.begin();p!=b.end();++p)
    {
        if(bb)
       painter->drawPixmap(p->x,p->y,30,30,bulle);
        else  painter->drawPixmap(p->x,p->y,30,90,bulle2);

    }
    for(it=am.begin();it!=am.end();++it)
    {
        if(it->x%59==0) painter->drawPixmap(it->x,it->y,97,75,jiaran);
        else if(it->x%3==0)painter->drawPixmap(it->x,it->y,97,75,amy2);
         else if(it->x%5==0)painter->drawPixmap(it->x,it->y,97,75,amy3);
         else if(it->x%7==0)painter->drawPixmap(it->x,it->y,97,75,amy4);
         else if(it->x%11==0)painter->drawPixmap(it->x,it->y,97,75,amy5);
       else painter->drawPixmap(it->x,it->y,97,75,amy);
        if(it->x%4==1)painter->drawPixmap(it->x,it->y,97,75,hd);

    }
    if(gamevoer)painter->drawPixmap(195,30,250,250,gv);


painter->drawText(300,700,"得分:"+QString::number(sco));
    painter->end();

}
void Widget::keyPressEvent(QKeyEvent *event)
{
    if(x>0){
    if(event->key()==Qt::Key_Left)x=x-11; }
    if(x<510)
    if(event->key()==Qt::Key_Right)x=x+11;

    if(event->key()==Qt::Key_Space)
    {
    //QSound::play(":/pic/kaipao.wav");
     ex.x=x+45;
     ex.y=780;

     b.push_back(ex);
    }
    this->update();
}
 void Widget::timerEvent(QTimerEvent *event)
{
     if(event->timerId()==this->id1)
     {
      for(p=b.begin();p!=b.end();)
      {
          p->y-=15;
          if(p->y<0)p=b.erase(p);
          else ++p;

      }
      for(it=am.begin();it!=am.end();)
      {
          if(it->x%59==0)it->y+=10;
         else
          {
              switch (it->x%4) {
              case 0:it->y+=2;

                  break;
              case 1:it->y+=3;

                  break;
              case 2:it->y+=4;break;
              case 3:it->y+=5;
                  break;

              }

          }
          if(it->y>800)
          {
              this->killTimer(id1);
              this->killTimer(id2);
             it=am.erase(it);
             gamevoer=true;




          }
          else
          {
              xit=it;

              ++it;
          }
          for(xp=b.begin();xp!=b.end();)
          {
              if(xp->x-xit->x<97&&xp->x-xit->x>-29&&xp->y<xit->y+75)
              {

                  xit=am.erase(xit);
                  xp=b.erase(xp);
                  sco++;
                  if(xit->x%79==0)
                  {if(bb)bb=false;
                      else bb=true;
}
              }
              ++xp;
          }
      }
     }
     else if(event->timerId()==this->id2)
     {
       em.x=rand()%530+10;

       em.y=0;

       am.push_back(em);
     }
     this->update();
}
Widget::~Widget()
{
    delete ui;
}

main.cpp

#include "widget.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    return a.exec();
}

找好图片素材,建立好上面三个文件,就可以运行了(最上面图2)。

彩蛋说明:会刷出特殊飞机,击中后子弹会变大。

 原创文章,转载请注明出处。

游戏素材来源:https://www.aigei.com/

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值