用qt开发捕鱼达人
前言
整理电脑文件的时候发现了自己以前的作业,顺便就整理传了一下。在那之前我只用过unity开发过游戏,对qt要怎么做游戏还不是很了解,想要系统的学习也找不到特别多的资源,所以学习的过程花费了比较多的时间。
做出来了两个不一样的关卡,鱼有游泳的动画,发射炮弹可以捕捉鱼,并统计分数。
一、游戏效果
游戏主界面
关卡选择
游戏界面
发射子弹和渔网展开
游戏中,玩家可以用鼠标控制大炮的方向,点击鼠标左键,即可朝着鼠标方向发射子弹,子弹在接触到鱼后会展开渔网,渔网中的鱼的生命中会减少,当鱼的生命值为0时,鱼的位置会刷新,出现在窗口之外,以达到消灭鱼的效果,然后重新进入窗口,同时玩家的分数会增加,在屏幕右上方显示分数。不同鱼的生命不同,可以取得的分数也不同。
二、用户交互
1.进入游戏界面
在主窗体上点击“开始游戏”按钮,进入关卡选择界面。点击“退出按钮”,退出游戏。
2.关卡选择界面
在关卡选择界面,共有三个按钮,其中有一个返回按钮,两个选择关卡的按钮。点击返回,则会回到上一级界面,点击不同的关卡,即可进入不同的海域开始捕鱼。共有两个关卡,一个是“绿色珊瑚海”,一个是“蓝色鲸落地”。两个场景有不同的背景图片和鱼供玩家抓捕。
3.捕鱼界面
在这个界面,玩家可以用鼠标移动来控制大炮方向,并点击鼠标左键来发射子弹,如果子弹触碰到了鱼,则会展开渔网,被触碰到的鱼会重新刷新位置,供玩家继续捕捉。点击返回按钮,玩家会回到关卡选择界面。
三、UI设计和实现
这个项目用户界面所用UI的资源,均采用的是爱给网(捕鱼达人 - 游戏 免费下载 - 爱给网 (aigei.com))上的免费资源,尽量符合原游戏,但有些资源难以找到,或者原游戏没有,所以用其他图片代替。
本项目下载的资源
共设计了两个窗口,一个mainwindow和一个levelselect窗口,以及level1和level2两个QGraphicsView场景。
1.Mainwindow窗口
Mainwindow继承自QMainWindow,用代码生成界面。有一个label,两个Qpushbutton,并为窗口和它们设置了背景图片,让界面更加生动美观。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPalette>
#include <QBrush>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
//https://www.aigei.com/view/63321.html 图片资源网站,本程序的图片来源
//设置软件窗口的小图标
ui->setupUi(this);
this->setFixedSize(1280,720);
this->setWindowTitle("捕鱼达人");
this->setWindowIcon(QIcon("../getFish/images/net.png"));
//设置背景
QPixmap pixmap = QPixmap(QString("../getFish/images/map4.jpg")).scaled(this->size());
QPalette palette;
palette.setBrush(backgroundRole(), QBrush(pixmap));
setPalette(palette);
//创建标签
this->label=new QLabel(this);
label->setPixmap(QPixmap("../getFish/images/logo.png"));
this->label->setFixedSize(319,142);
this->label->move(1280/2-319/2,100);
//添加开始按钮
this->levelSelectbtn=new QPushButton(this);
this->levelSelectbtn->setIcon(QIcon("../getFish/images/start.png"));
this->levelSelectbtn->setFixedSize(234,82);
this->levelSelectbtn->setIconSize(QSize(234,82));
this->levelSelectbtn->setFlat(true);
this->levelSelectbtn->setFocusPolicy(Qt::NoFocus);
this->levelSelectbtn->move(1280/2-234/2,340);
//用代码创建的按钮,所以要用connect关联,只用按钮名字命名槽函数无效
connect(this->levelSelectbtn, &QPushButton::clicked, this, &MainWindow::levelSelectbtn_clicked);
//创建退出按钮
this->quitbtn=new QPushButton(this);
this->quitbtn->setIcon(QIcon("../getFish/images/quit.png"));
this->quitbtn->setFixedSize(187,83);
this->quitbtn->setIconSize(QSize(187,83));
this->quitbtn->setFlat(true);
this->quitbtn->setFocusPolicy(Qt::NoFocus);
this->quitbtn->move(1280/2-187/2,540);
connect(this->quitbtn, &QPushButton::clicked, this, &MainWindow::close);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::levelSelectbtn_clicked()
{
emit showLevelSelect(); /* 显示主窗口 */
this->hide(); /* 隐藏登录对话框 */
}
void MainWindow::showThis()
{
this->show();
}
void MainWindow::dohelp()
{
}
2.levelselect窗口
levelselect继承自QWidget,主要用ui文件编辑样式,并使用了部分代码。
#include "levelselect.h"
#include "ui_levelselect.h"
levelSelect::levelSelect(QWidget *parent) :
QWidget(parent),
ui(new Ui::levelSelect)
{
ui->setupUi(this);
//设置背景
this->setFixedSize(1280,720);
this->setWindowTitle("捕鱼达人");
this->setWindowIcon(QIcon("../getFish/images/net.png"));
QPixmap pixmap = QPixmap(QString("../getFish/images/map4.jpg")).scaled(this->size());
QPalette palette;
palette.setBrush(backgroundRole(), QBrush(pixmap));
setPalette(palette);
timer=new QTimer;
connect(timer,SIGNAL(timeout()),this,SLOT(remake()));
timer->start(1);
}
levelSelect::~levelSelect()
{
delete ui;
}
void levelSelect::on_toLevel1btn_clicked()
{
level1=new Level1;
level1needremake=level1->islevle1live;
QObject::connect ( level1, SIGNAL (showSelect()), this, SLOT (showThis()) );
level1->show();
//emit showLevel1(); /* 显示主窗口 */
this->hide(); /* 隐藏登录对话框 */
}
void levelSelect::on_toLevel2btn_clicked()
{
level2=new Level2;
level2needremake=level2->islevle2live;
QObject::connect ( level2, SIGNAL (showSelect()), this, SLOT (showThis()) );
level2->show();
//emit showLevel1(); /* 显示主窗口 */
this->hide(); /* 隐藏登录对话框 */
}
void levelSelect::showThis()
{
this->show();
}
void levelSelect::on_quitToMainbtn_clicked()
{
emit quitToMain();
this->hide();
}
void levelSelect::remake()
{
if(level1->islevle1live==0&&level1needremake==1)
{
this->show();
level1needremake=0;
}
}
3.两个QGraphicsView场景
由于游戏中枪需要移动、要点击发射子弹,还要判断渔网和鱼是否有接触,用原本的weiget制作很困难,并且鱼在游动时会出现锯齿,因此使用GraphicsView框架来实现。
Graphics View框架主要特点:
1、 GraphicsView框架结构中可以利用QT绘图系统的反锯齿、OpenGL工具改善绘图性能。
2、GraphicsView框架支持事件传播体系结构,使场景内的图元交互能力提高一倍。图元处理鼠标键盘事件,如鼠标按下、移动、释放、点击和双击事件,也跟踪鼠标移动。
3、在GraphicsView框架中通过二元空间划分树,提供快速的图元查找,这样能实时的显示大场景。
在这两个场景中实现了游戏的主要功能,分别代表了两个不同的关卡。这里以一个为例。
#include "level1.h"
Level1::Level1()
{
//设置大小
//this->resize(1280,720);
this->setFixedSize(1280,720);
this->setWindowTitle("捕鱼达人");
this->setWindowIcon(QIcon("../getFish/images/net.png"));
//设置背景
this->setAutoFillBackground(true);
this->setBackgroundBrush(QBrush(QPixmap("../getFish/images/map2.png")));
//添加返回按钮
this->returnbtn=new QPushButton(this);
this->returnbtn->setIcon(QIcon("../getFish/images/return.png"));
this->returnbtn->setFixedSize(234,82);
this->returnbtn->setIconSize(QSize(234,82));
this->returnbtn->setFlat(true);
this->returnbtn->setFocusPolicy(Qt::NoFocus);
this->returnbtn->move(0,0);
//用代码创建的按钮,所以要用connect关联,只用按钮名字命名槽函数无效
connect(this->returnbtn, &QPushButton::clicked, this, &Level1::returnbtn_clicked);
//创建分数显示框
this->moneytext=new QTextEdit(this);
this->moneytext->setFixedSize(130,50);
this->moneytext->setFontPointSize(18);
this->moneytext->move(1150,0);
this->setMouseTracking(true);
scence=new QGraphicsScene;
//场景的坐标系统以中心为原点,图片无法正常显示,因此需要重新设置原点
scence->setSceneRect(0,0,this->width()-2,this->height()-2);
this->setScene(scence);
//new一个大炮
gun=new Gun("../getFish/images/gun1.png",scence);
gun->setPos(this->width()/2,this->height());
//类里始终在改变图片,因此外部的图片可以随便设置
//参数分别是初始图片,场景,是否在左侧,生命值和价格
sardine1=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine2=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine3=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine4=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine5=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine6=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine7=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine8=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine9=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine10=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine11=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
sardine12=new Sardine("../getFish/images/bluefish.png",scence,1,3,1);
redfish1=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish2=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish3=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish4=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish5=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish6=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish7=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish8=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish9=new Redfish("../getFish/images/bluefish.png",scence,-1,6,2);
redfish10=new Redfish("../getFish/images/bluefish.png",scence,-1,2,2);
shark1=new Shark("../getFish/images/bluefish.png",scence,-1,9,3);
shark2=new Shark("../getFish/images/bluefish.png",scence,-1,9,3);
shark3=new Shark("../getFish/images/bluefish.png",scence,-1,9,3);
shark4=new Shark("../getFish/images/bluefish.png",scence,-1,9,3);
shark5=new Shark("../getFish/images/bluefish.png",scence,-1,9,3);
//定时器
timer=new QTimer;
connect(timer,SIGNAL(timeout()),scence,SLOT(advance()));
connect(timer,&QTimer::timeout,this,&Level1::upmoney);
//connect(this->returnbtn, &QPushButton::clicked, this, &Level1::returnbtn_clicked);
timer->start(50);
}
void Level1::resizeEvent(QResizeEvent *event)
{
this->setBackgroundBrush(QBrush(QPixmap("../getFish/images/map2.png").scaled(event->size())));
}
void Level1::mouseMoveEvent(QMouseEvent *event)
{
QPoint p;
p=event->pos(); //获取鼠标位置
//画线
QLine line(this->width()/2,this->height(),p.x(),p.y());
QLineF linef(line);
//将大炮角度设置为线的角度
gun->setRotation(90-linef.angle()); //角度要进行特定的变换,才能跟随鼠标旋转
}
void Level1::mousePressEvent(QMouseEvent *event)
{
QPoint p;
p=event->pos(); //获取鼠标位置
//画线
//首先找到子弹起始位置,在大炮口,是大炮原点加上半径
QLine line(this->width()/2,this->height(),p.x(),p.y());
QLineF linef(line);
if(this->canshoot==1)
{
Bullet *bullet=new Bullet("../getFish/images/bullet.png",scence,linef.angle(),this);
this->canshoot=0;
}
//bullet->setPos(this->width()/2,this->height());
//将子弹角度设置为线的角度
}
void Level1::returnbtn_clicked()
{
islevle1live=0;
delete this;
//this->hide();
//emit showSelect(); /* 显示主窗口 */
}
void Level1::upmoney()
{
this->bullettime+=1;
int a=this->bullettime;
qDebug()<<a;
if(this->bullettime==15)
{
this->canshoot=1;
this->bullettime=0;
}
QString s1="分数:";
QString s2=QString::number(this->money);
s1.append(s2);
// int a=this->money;
// qDebug()<<a<<"!";
this->moneytext->setText(s1);
}
在这两个场景,除了设置背景,还添加了显示分数的文本框,返回按钮,并添加了自己设计的各种鱼、炮,还完成了定时器和槽函数的关联,鼠标点击的响应、分数更新等操作。
4.窗口间的跳转
主窗口和关卡选择的跳转主要用信号槽机制实现,点击按钮可以相互跳转。
#include "mainwindow.h"
#include <QTextCodec>
#include <QApplication>
#include <levelselect.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
levelSelect levelSelect;
QObject::connect ( &w, SIGNAL ( showLevelSelect() ), &levelSelect, SLOT ( showThis() ) );
QObject::connect ( &levelSelect, SIGNAL ( quitToMain() ), &w, SLOT ( showThis() ) );
w.show();
return a.exec();
}
在关卡选择,本来打算继续使用信号槽机制实现关卡选择和关卡间的切换,但总是报错,修改多次仍然无法解决,推测可能和关卡继承自QGraphicsView有关,于是切换了思路。在关卡里增加两个变量,一个用来判断是否生成过关卡,一个用来判断关卡是否还存在,如果都满足,则重新显示关卡选择界面,实现从关卡返回到关卡选择。
四、游戏图元设计和实现
本项目设计了四种鱼类,沙丁鱼(Sardine),蓝鱼(Bluefish),红鱼(Redfish),和鲨鱼(Shark),继承自父类鱼类(Fishs)。和三种其他类,枪(Gun),子弹(Bullet)和渔网(Net),继承自父类QpixmapItem。Fishs也继承自QpixmapItem,QpixmapItem继承自QGraphicdItem。
1.QGraphicsltem类
QGraphicsltem类是视图框架的一部分,是在一个QGraphicsScene中最基本的图形类,它为绘制你自己的item提供了一个轻量级的窗口,包括声明item的位置,碰撞检测,绘制重载和item之间的相互作用通过事件处理。
写自己的item图形,首先应该继承QGraphicsItem,然后重写他的两个纯虚公共函数,boundingRect()和paint(),第一个函数返回绘制item大概的区域,第二个函数用来绘制item内容。
boundingRect()函数有很多用处,场景在boundingRect()来建立它的item的index,视图view使用它来剪切可见的item,在重新绘制item时候,来决定相互重叠的部分,此外,item的碰撞检测机制也使用的boundingRect()来提供一个高效的定点,在collidesWithItem()更好的碰撞算法建立在调用函数shape(),shape()函数以QpainterPath类型返回item的精准的轮廓。
2. QpixmapItem类
自己的item图形,继承自QGraphicsltem,构造函数的参数有文件名称和场景,重写了两个纯虚公共函数,boundingRect()和paint(),用来绘图和判断碰撞。
#ifndef QPIXMAPITEM_H
#define QPIXMAPITEM_H
#include <QGraphicsItem>
#include <QPainter>
#include <QGraphicsScene>
class QpixmapItem:public QGraphicsItem
{
public:
QpixmapItem(const QString & fileName,QGraphicsScene *scence);
//重载纯虚函数
virtual QRectF boundingRect() const;
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);
protected:
QPixmap pixmap;
};
#endif // QPIXMAPITEM_H
#include "qpixmapitem.h"
QpixmapItem::QpixmapItem(const QString & fileName,QGraphicsScene *scence)
{
pixmap.load(fileName);
scence->addItem(this);
}
QRectF QpixmapItem::boundingRect() const
{
return QRectF(-pixmap.width()/2,-pixmap.height(),pixmap.width(),pixmap.height());
}
//
void QpixmapItem::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
//位置设置为-pixmap.width()/2,-pixmap.height(),以便在场景绘图时不在场景之外
painter->drawPixmap(-pixmap.width()/2,-pixmap.height(),pixmap);
}
3.枪(Gun)类
继承自QpixmapItem类,主要用来在场景生成枪的图像,没有特殊的函数。
#ifndef GUN_H
#define GUN_H
#include <QGraphicsItem>
#include <QPainter>
#include <QPixmap>
#include <qpixmapitem.h>
#include <QGraphicsScene>
class Gun:public QpixmapItem
{
public:
Gun(const QString & fileName,QGraphicsScene *scence);
};
#endif // GUN_H
#include "gun.h"
Gun::Gun(const QString & fileName,QGraphicsScene *scence):QpixmapItem(fileName,scence)
{
}
4.子弹(Bullet)类
继承自QpixmapItem类。构造函数可以接受鼠标角度angle和生成的场景level,来决定子弹生成时方向和位置,由于设计了两关,level可以在net消灭了鱼后,改变当前关卡得到的分数。
docolliding()用来进行碰撞处理,在子弹(bullet)触碰到鱼的时候生成一张渔网(net),并delete掉子弹(bullet)。
advance()重载,每个timeout都会执行,如果子弹(bullet)的位置没有超过我设定的边界,就继续朝着子弹方向移动,(方向在构造函数中就已经得到),否则delete,以免占用内存。
paint()重载,重新绘图,并在这里判断子弹是否产生碰撞,如果碰撞,运行碰撞处理docolliding()。
#ifndef BULLET_H
#define BULLET_H
#include "qpixmapitem.h"
#include <QLine>
#include <QLineF>
#include <QtGui>
#include "net.h"
#include <QGraphicsScene>
#include "level.h"
class Bullet:public QpixmapItem
{
public:
Bullet(const QString & fileName,QGraphicsScene *scence,qreal angle,Level *level);
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);
void advance(int phase);
void docolliding(); //碰撞处理
Level *level; //level,传给neu,用来给场景返回分数使用
int bullettime=0; //用与设定子弹的发射间隔
private:
QGraphicsScene *scence; //让渔网可以加入到场景
};
#endif // BULLET_H
#include "bullet.h"
Bullet::Bullet(const QString & fileName,QGraphicsScene *scence,qreal angle,Level *level):QpixmapItem(fileName,scence)
{
this->level=level;
this->scence=scence;
qreal dx,dy;
qreal rad;
rad=angle*3.14/180;
dx=90*cos(rad);
dy=90*sin(rad);
this->setPos(scence->width()/2+dx,scence->height()-dy);
this->setRotation(90-angle); //角度要进行特定的变换,才能跟随鼠标旋转
}
void Bullet::advance(int phase)
{
if(mapToScene(0,0).x()<=0||mapToScene(0,0).x()>=1200||mapToScene(0,0).y()<=0)
{
//超出边界删除自己,以防占用内存
delete this;
}
else
{
//没有超出则继续前进
this->setPos(mapToScene(0,-10));
}
}
void Bullet::docolliding()
{
Net *net=new Net("../getFish/images/net.png",this->scence,this->level);
net->setPos(mapToScene(0,0));//设置渔网生成坐标,为子弹当前坐标
delete this;
}
void Bullet::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
//位置设置为-pixmap.width()/2,-pixmap.height(),以便在场景绘图时不在场景之外
painter->drawPixmap(-pixmap.width()/2,-pixmap.height(),pixmap);
//检测子弹是否有碰撞
if(this->collidingItems().count()>0)
{
//运行碰撞函数
docolliding();
}
}
5 渔网(net)类
继承自QpixmapItem类。构造函数中接收了渔网(net)产生的场景,用来修改对应场景的分数。
advance()重载,每个timeout都执行一次advance()。渔网(net)接触到的所有Item都被放在了链表里,每执行一次advance()都遍历一次链表,让链表中的鱼类(fishes)生命值减少1,当鱼(fishes)的当前生命值(life)为0时,场景level的money增加鱼类(fishs)的price,以实现分数增加的效果,然后执行场景level的upmoney()函数,实现分数显示的更新,最后执行鱼类(fishs)的死亡函数。
在这里,如果直接delete掉渔网(net),渔网会一闪而过,看起来很突兀,为了让渔网多存在一会再消失,每次执行advance()时让变量time+1,加到3时,再delete渔网。这样造成的后果是后面进入渔网的鱼也会减少生命值,但是这样的情况出现的比较少,而且也没有那么不合理。为了适应渔网(net)存在时间的改变,我在创建(鱼类)fishs时让生命增加两倍,这样不影响消灭鱼类需要的打击次数。
#ifndef NET_H
#define NET_H
#include <qpixmapitem.h>
#include "fishs.h"
#include "level.h"
#include <QDebug>
class Net:public QpixmapItem
{
public:
Net(const QString & fileName,QGraphicsScene *scence,Level *level);
void advance(int phase);
int time=0; //设置一个time,让渔网存在3个advance后再消失
Level *level;
};
#endif // NET_H
#include "net.h"
Net::Net(const QString & fileName,QGraphicsScene *scence,Level *level):QpixmapItem(fileName,scence)
{
this->level=level;
}
void Net::advance(int phase)
{
if(this->collidingItems().count()>0) //collidingItems返回所有被碰到的鱼,放入链表
{
//让接触到渔网的鱼的当前生命值减1,如果生命值为0,分数增加,运行死亡函数,重新刷新位置。
QList<QGraphicsItem *>list=this->collidingItems();
QList<QGraphicsItem *>::Iterator i;
Fishs *fishs;
i=list.begin();
while(i!=list.end())
{
fishs=(Fishs *)(*i);
fishs->life-=1; //减少一点当前生命值
if(fishs->life==0)
{
this->level->money+=fishs->price; //level的分数增加
this->level->upmoney();
// int a=this->level->money;
// qDebug()<<a<<"!";
fishs->fish_death(); //运行鱼死亡函数
}
i++;
}
}
time+=1;
if(time==3) //存在3个advace后才消失,让渔网多存在一会,不会太突兀
{
this->hide();
delete this;
}
}
6. 鱼(fishs)类
继承自QpixmapItem类,同时是其他所有鱼的父亲类。构造函数的参数有isLeft,life和price,分别是鱼类是否生成在左侧,鱼类的最大生命值和鱼类死亡后的价格。
advance()重载,用来判断鱼类是否超出了设定的边界值,如果超出就刷新它的位置,isLeft是1就刷新在左侧,是-1就刷新在右侧,如果没有超过就继续移动。为了让鱼的游动速度不一样,生成了随机数,让水平移动的速度不一样,上下移动的速度和方向不一样。
paint()没有什么功能,因为不同的鱼动画不同,要在子类里实现,所以之后重构时解决。
fish_death(),死亡函数,在鱼的生命值降到0时会执行,但不是直接delete掉,而是重新刷新在窗口外,然后将当前生命值变为最大生命值,让玩家看起来消灭了鱼,但其实没有,这样我可以不用再生成新的鱼。
#ifndef FISHS_H
#define FISHS_H
#include "qpixmapitem.h"
#include <QGraphicsScene>
class Fishs:public QpixmapItem
{
public:
Fishs(const QString & fileName,QGraphicsScene *scence,int isLeft,int life,int price);
void advance(int phase);
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget);
void fish_death(); //鱼死亡函数
int price; //鱼的价格
int sumprice; //一条鱼总共产生的价格
int isLeft; //判断是否出生在左边
int fulllife; //鱼的完整生命值,用来重置死亡后的当前生命值
int life; //现在的生命值
int upordown=rand()%2==1? 1:-1;
};
#endif // FISHS_H
#include "fishs.h"
Fishs::Fishs(const QString & fileName,QGraphicsScene *scence,int isLeft,int life,int price):QpixmapItem(fileName,scence)
{
//初始化最大生命值和当前生命值
this->fulllife=life;
this->life=life;
this->price=price;
//初始化位置
this->isLeft=isLeft;
if(isLeft==1) //如果是左侧的鱼,出生点设置在左侧
{
setPos(-200+rand()%800,0+rand()%500); //用随机数增加出生点的随机性,让开始游戏时鱼分布的比较散
}
if(isLeft==-1) //如果是右侧的鱼,出生点设置在右侧
{
setPos(100+rand()%900,0+rand()%500);
}
}
void Fishs::advance(int phase) //随着时间的增加,刷新鱼的位置,制造出动画的效果
{
//超出边界,则重新刷新位置,刷新位置要在屏幕之外,否则凭空出现在屏幕中央会很突然
if(mapToScene(0,0).y()<=-400||mapToScene(0,0).x()>=1400)
{
if(this->isLeft==1) //如果是左侧的鱼,刷新在左侧
{
setPos(-50-rand()%300,200+rand()%500); //用随机数增加刷新点的随机性,让鱼分开出现
}
if(isLeft==-1) //如果是右侧的鱼,刷新在右侧
{
setPos(1400+rand()%100,200+rand()%400);
}
}
int speed=rand()%10; //让速度具有随机性,各个鱼移动速度不同
this->setPos(mapToScene(this->isLeft*speed/2.0,upordown*1.0)); //用isLeft乘速度,实现如果是左侧的鱼,向右移动;如果是右侧的鱼,向左移动的效果
}
void Fishs::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
painter->drawPixmap(-pixmap.width()/2,-pixmap.height(),pixmap);
}
void Fishs::fish_death()
{
//鱼死亡之后不会消失,而是重新刷新在屏幕外,制造出死亡的效果
if(this->isLeft==1) //如果是左侧的鱼,刷新在左侧
{
setPos(-50-rand()%300,200+rand()%500); //用随机数增加刷新点的随机性,让鱼分开出现
}
if(isLeft==-1) //如果是右侧的鱼,刷新在右侧
{
setPos(1400+rand()%150,200+rand()%500);
}
this->life=this->fulllife;
}
7. 其他鱼类(以沙丁鱼为例)
继承自Fishs类,构造函数主要用来向父类传参。
paint()用来实现不同鱼的动画。动画的原理是图片的更换,只要让下载到的资源图片按顺序显示,就可以有比较顺滑的动画效果。刷新不能太快也不能太慢,否则都会不合适,所以为了找出几个timeout换一次图片,我试了几个值,认为5次比较合适。其他鱼也类似,但是由于下载到的图片数量不一样,因此有一些差别。
#ifndef SARDINE_H
#define SARDINE_H
#include "fishs.h"
class Sardine:public Fishs
{
public:
Sardine(const QString & fileName,QGraphicsScene *scence,int isLeft,int life,int price);
void paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget); //重载动画
};
#endif // SARDINE_H
#include "sardine.h"
Sardine::Sardine(const QString & fileName,QGraphicsScene *scence,int isLeft,int life,int price):Fishs(fileName,scence,isLeft,life,price)
{
}
void Sardine::paint(QPainter *painter,const QStyleOptionGraphicsItem *option,QWidget *widget)
{
char filename[50]="0";
//只会初始化一次
//图片每5个timeout刷新一下,以制造出动画效果,经多次尝试取这个值,个人认为不快也不慢正合适
static int i=5;
if(i==34)
i=5;
sprintf(filename,"../getFish/images/sardiner%d.png",i++/5);
pixmap.load(filename);
painter->drawPixmap(-pixmap.width()/2,-pixmap.height(),pixmap);
}
总结
用qt做游戏还是比较麻烦的,写了不少东西,用游戏引擎的话会简单很多,而且效果更好。
但是当时毕竟是要做一个完成,做出来后提升了不少对qt和c++的理解和熟练度。
2401_85094156: 大佬求源码,求求了
CSDN-Ada助手: 恭喜您写了第11篇博客,内容丰富,对MySQL学习笔记进行了深入探讨,让读者受益匪浅。希望您能继续坚持创作,分享更多宝贵的学习心得体会。下一步建议可以尝试结合实际案例,讲解MySQL在具体项目中的应用,相信会更加生动有趣。期待您的下一篇作品!
CSDN-Ada助手: 恭喜作者发布了第10篇博客,标题为“FinalShell连接CentOS三步走”,内容介绍了FinalShell连接CentOS的方法,非常实用。希望作者能够继续保持创作的热情,分享更多关于CentOS的使用技巧和经验,也可以考虑结合实际案例进行分析和解决方案的提出,这样可以让读者更加深入地了解相关知识。期待作者的下一篇作品!
CSDN-Ada助手: 恭喜博主完成了第9篇博客《MySQL学习笔记(上)》!您的持续创作真是令人钦佩。在这篇博客中,您对MySQL学习的笔记进行了总结,为读者提供了宝贵的学习资源。 接下来,我想建议您继续深入探索MySQL的相关知识,并将您的学习心得与读者分享。比如,您可以写一篇《MySQL学习笔记(下)》,继续介绍MySQL的高级特性、性能优化技巧或者与其他数据库系统的比较等内容。希望您能继续保持谦虚的态度,为读者带来更多有价值的知识分享。期待您的下一篇博客!
CSDN-Ada助手: 恭喜你写了第8篇博客!标题为“JAVA学习笔记(上)”,这是一个了不起的成就。通过你的学习笔记,我能感受到你对JAVA的热情和努力。在下一步的创作中,我建议你可以继续分享关于JAVA的实践经验或者深入探讨一些高级主题,这样可以更好地帮助读者理解和运用JAVA。期待你的下一篇博客!加油!