看吴健吴老师的视频做了一个打地鼠的小游戏,主要利用了qt中的2D显示器QGraphicsView,
QGraphicsScence场景,以及图元QgraphicsItem下的像素图元QGraphicsPixmapItem。
关系为一个个图元构成场景,场景显示在显示器中。
##效果如下(手机)
图元类
图元类主要设置每个图元上的图片是什么,该图元的状态(开始状态和暂停、停止状态),以及该图元上是否有老鼠(通过一个bool类型的成员变量来决定);图元类还需要设置置鼠标的状态为一个锤子;图元类还需要重写父类的基类QGraphicsItem中的鼠标按压事件和鼠标抬起事件。当鼠标按下时,如果在开始状态下该图元的mouse变量为true(有老鼠),则 说明打到了老鼠,此时利用获取到的单例对象向mainwindow发送一个beat信号,更新lcdnumber上的分数。
#include "myitem.h"
#include <QDebug>
myitem::myitem()
{
this->setPixmap(QPixmap(":/pic/2"));//默认背景图片
mouse = false;//没有老鼠
start = false;//没开始
this->setCursor(QCursor(QPixmap(":/pic/up1.png")));//鼠标为锤子抬起形态
}
void myitem::setpic(QString path) //用于设置该图元的图片
{
this->setPixmap(QPixmap(path));
}
void myitem::setmouse(bool m) //设置该图元上是否有老鼠
{
this->mouse = m;
}
bool myitem::ismouse()//返回该图元上是否有老鼠
{
return this->mouse;
}
void myitem::mousePressEvent(QGraphicsSceneMouseEvent *event)//鼠标点击事件重写,继承自父类QGraphicItem
{
this->setCursor(QCursor(QPixmap(":/pic/down.png")));//鼠标点击锤子落下
if(this->start)//判断是否是开始状态
{
if(ismouse())//判断是否有老鼠
{
this->setPixmap(QPixmap(":/pic/3"));//设置该图元为击中效果
score* s = score::getscore();//获取单例对象
s->addscore();//通知加分
}
}
}
void myitem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)//鼠标释放事件,锤子抬起
{
this->setCursor(QCursor(QPixmap(":/pic/up1.png")));
}
void myitem::setstart(bool m)//设置是否为开始状态
{
this->start = m;
}
bool myitem::isstart()
{
return this->start;
}
场景类
场景类首先要进行背景的布局,将图元添加进场景并且依次排列成4*4的矩阵。然后需要设置定时器让游戏动起来。然后设置开始、暂停、停止三个按钮的槽函数(主要是改变定时器的状态)。
#include "myscene.h"
myScenr::myScenr(QObject *parent) : QGraphicsScene(parent)
{
int i = 0;
for(i = 0;i<16;i++)//在该场景上添加(4*4)个图元
{
itm[i] = new myitem;
itm[i]->setPos(i/4 * itm[i]->boundingRect().width(),i%4 * itm[i]->boundingRect().height());
this->addItem(itm[i]);//依次添加进场景
}
this->life_time = 2000;//初始化老鼠刷新时间
this->mouse_count = 4;//初始化每次刷新的最大数量
pt = new QTimer(this);//刷新老鼠的定时器
ppt = new QTimer(this);//更新老鼠刷新速度和数量定时器
connect(pt,SIGNAL(timeout()),this,SLOT(showmouse()));
connect(ppt,SIGNAL(timeout()),this,SLOT(change_time()));
}
void myScenr::showmouse()//老鼠出动
{
int count = rand()%this->mouse_count+1;//随机数获取出现几只老鼠
int i = 0;
for( i = 0;i<16;i++)//老鼠出现之前上一波老鼠回洞
{
itm[i]->setpic(":/pic/2");
itm[i]->setmouse(false);
}
for(i = 0;i < count;i++)
{
int pos = rand()%16;//随机数获取老鼠出现在那个图元
itm[pos]->setpic(":/pic/4");
itm[pos]->setmouse(true);
}
}
void myScenr::change_time()//改变刷新速度和数量
{
this->life_time -= 500;//每30s将刷新时间减小0.5s,一局游戏控制在2min
this->mouse_count += 2;
if(this->life_time == 0)//如果时间耗尽,发送finshed信号
{
emit finshed();
}
}
void myScenr::start()//开始
{
int i = 0;
for( i = 0;i<16;i++)
{
itm[i]->setstart(true);//设置每一个图元的start状态
}
ppt->start(30000);//启动定时器
pt->start(this->life_time);
}
void myScenr::spause()//暂停
{
pt->stop();
ppt->stop();
int i = 0;
for( i = 0;i<16;i++)
{
itm[i]->setstart(false);
}
}
void myScenr::stop()//停止
{
pt->stop();
ppt->stop();
this->life_time = 2000;
this->mouse_count = 4;
int i = 0;
for( i = 0;i<16;i++)
{
itm[i]->setpic(":/pic/2");
itm[i]->setmouse(false);
itm[i]->setstart(false);
}
emit clear();//向mainwindow发送停止清空分数信号
}
#单例
因为是否打到老鼠是在图元上判断的,而分数的显示则在mainwindow里。所以设计了一个单例对象让图元和mainwindow都可以获取到同一个对象。图元类利用该对象发送beat信号,mainwindow则关联beat信号和用来更新分数的槽函数。
#include <QObject>
//单例
class score : public QObject
{
Q_OBJECT
public:
static score* getscore();
void addscore();
private:
explicit score(QObject *parent = nullptr);
signals:
void beat();//砸到老鼠后通知mainwindow加分
public slots:
private:
static score *s;
};
#mainwindow
mainwindow主要将场景加入显示器、并设置弹窗(重写关闭事件的函数、和一局游戏结束后的弹窗)、以及背景音乐的播放。
#源码地址
https://gitee.com/hou_yuan_hao/whacamole