话不多说,先看效果图
(说明:这个是我学校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/