QT:简单的小游戏开发日记

最近在b站上学了下GUI开发 (实在不想看黑框了 ),做出来一个翻金币的小游戏。虽然写完了但是不会打包,一直也没有给别人帮我debug。在我会打包之前现在这里复盘一下程序是怎么写出来的。

说实话,Qt的学习确实让我有点了解了程序的运行机制。最简单的就是connect函数只走一遍,不是发一遍信号走一遍。而且不是发信号的时候才走这句话。

资源添加

开发一个程序不可避免的需要图片和音效。虽然我现在还没加音效 由于我用的visual studio开发,vs自动给我创建了一个.qrc文件,资源全放在这里面。
在这里插入图片描述
添加的文件的话,你需要先把你的资源先打包一下放到工程文件夹下面。下面的prefix是添加一个前缀。
把文件放qrc文件的目的是可以使用相对路径,对于需要移植的程序来说非常重要。
像我这个引用的时候(":/source/name…") 如果有前缀在前面加前缀即可。

程序主窗口

thefinalgame.cpp

主窗口我做的比较粗糙,1280*720的界面就放了一个“开机按钮”,简单的设置了一下标题栏,起名“Coin Turning”。 菜单栏只放了一个关闭按钮。

程序的关闭并连接警告对话框

这个程序一共有两个正常途径将程序关闭

第一个是菜单栏上的关闭,第二个是直接点击右上角叉号。
简单说下菜单栏上关闭,他是QAction 类的,点击它会产生triggered信号,用connect连接一个lambda表达式关闭窗口即可。

我们关闭窗口会产生关闭事件,这个在主窗口父类是一个virual函数,说明我们可以在主窗口类重写。

void TheFinalGame::closeEvent(QCloseEvent* ce)
{
	//生成警告对话框
	int res = QMessageBox::question(this , "Warning" , "Sure to quit?" , QMessageBox::Yes | QMessageBox::No , QMessageBox::Yes);
	//如果点的是确定 退出
   if (res == QMessageBox::Yes) this->close();
   //如果不是 不理会
   else if (res == 65536 || res == QMessageBox::No) ce->ignore();
}

载入背景图片

用paintevent载入背景图片
用QPixmap作为载体

void TheFinalGame::paintEvent(QPaintEvent*)
{
    QPainter painter(this);

    QPixmap pix;
    pix.load(":/source/ground.jpeg");

    painter.drawPixmap(QRect(QPoint(0 , 23) , QPoint(1280 , 720)) , pix);
    /*
    绘制背景图片
    先在Qpixmap类中加载一张图片
    用画家在选定的矩形区域画出即可
    */
}

创建按钮类(开始 返回 关卡选择)

mypushbutton.h/mypushbutton.cpp

想做一个按一下就能变色(其实是换图片)的按钮,所有构造函数改了一下。
传入两张图片的路径,不按显示其中一个,按了显示另一个。

myPushButton::myPushButton(QString NormalPath , QString PressPath = "")
//第二个是按下时显示的图片 默认是空 因为有的按钮不需要变色
{
	this->NormalImgPath = NormalPath; //不按按钮的路径
	this->PressImgPath = PressPath; //按了按纽的路径

	QPixmap pix;
	pix.load(NormalImgPath);

	this->setFixedSize(QSize(pix.width()  , pix.height()));  //设置按钮大小
	this->setStyleSheet("QPushButton{border:0px;}"); //去掉图片周围白色 
	this->setIcon(QIcon(pix)); //设置按钮上的图标
	this->setIconSize(QSize(pix.width()  , pix.height())); //设置按钮中图片的大小
}

按钮动画

实现点一下按钮,按钮上下跳动的动画。
在按钮类里定义两个函数,一个实现按钮往下,另一个实现往上

void myPushButton::zoom1()
{
	QPropertyAnimation* anime = new QPropertyAnimation(this , "geometry");
	//设置事件间隔
	anime->setDuration(200);

	//起始位置
	anime->setStartValue(QRect(this->x() , this->y() , this->width() , this->height()));
	//结束位置
	anime->setEndValue(QRect(this->x() , this->y() + 10 , this->width() , this->height()));
	//设置弹跳曲线
	anime->setEasingCurve(QEasingCurve::OutBounce);

	anime->start();
}

void myPushButton::zoom2()
{
	QPropertyAnimation* anime = new QPropertyAnimation(this , "geometry");
	//设置事件间隔
	anime->setDuration(200);

	//起始位置
	anime->setStartValue(QRect(this->x() , this->y() + 10 , this->width() , this->height()));
	//结束位置
	anime->setEndValue(QRect(this->x() , this->y() , this->width() , this->height()));
	//设置弹跳曲线
	anime->setEasingCurve(QEasingCurve::OutBounce);

	anime->start();
}

用connect函数连接lambda表达式,点击就启动
加入了暂停300ms 防止执行过快,观感不好

connect(btn_start , &QPushButton::clicked , [=] () {
        btn_start->zoom1();
        btn_start->zoom2();
        QTimer::singleShot(300 , this , [=] () {
            this->hide();
            chooselevel->show();
        });
    });

选择关卡窗口

chooselevel.h/chooselevel.cpp
设置窗口各种参数和主窗口是同样的方式

设置返回按钮

返回按钮比开始按钮只多了一个功能,就是按下变色(实际上是图片切换)。只需要在鼠标按下事件里加一点东西。

void myPushButton::mousePressEvent(QMouseEvent* me)
{
	if(this->PressImgPath != "")
		this->setIcon(QIcon(this->PressImgPath));
	//通过pressimgpath判断这个按钮是不是需要变色
	QPushButton::mousePressEvent(me);
	//如果不拦截这个信号 交给父亲处理
}

还有一个问题 就是按了返回之后如何返回上一个场景。我们在选关窗口里并没有上一个窗口的对象。但是我们在上一个窗口有这个窗口的对象。
我们可以在chooselevel.h里手动实现一个信号
因为在主窗口里我们有选关窗口对象,我们可以接收到chooselevel发出的信号。

thefinalgame.cpp

connect(chooselevel , &ChooseLevel::back() , [=](){
	this->show();
})

back()是实现在chooselevel.h中的信号

设置选关按钮

让我对connect了解更深的地方

for(int i = 1 ; i <= 4 ; i ++)
		for (int j = 1; j <= 5; j ++)
		{
			myPushButton* btn_level;
			btn_level = new myPushButton(":source/Unchoose.png");
			btn_level->setParent(this);
			btn_level->setIconSize(QSize(0.8 * btn_level->size()));
			btn_level->move(QPoint((j - 1) * 0.18 * this->width() , (i - 1) * 0.25 * this->height()));

			Levels* levels = new Levels(j + (i - 1) * 5);
			
			connect(btn_level , &QPushButton::clicked , [=] () {
				btn_level->zoom1();
				btn_level->zoom2();
				QTimer::singleShot(300 , this , [=] () {
					this->hide();
					levels->show();
					qDebug() << levels->number;
				});
			});
			
			//这里很多人可能会问 申请这么多,只用一个指针存。调用怎么办
			//其实这里是申请之后立马connect了,只是用一下这个地址,这个地址其实就用不到了,没必要再用新的指针存下一个了。

			QLabel* label_level = new QLabel(btn_level);
			label_level->setText(QString().number((i - 1) * 5 + j));
			btn_level->move(QPoint((j - 1) * 0.18 * this->width() , (i - 1) * 0.25 * this->height()));
			label_level->setFixedSize(QSize(btn_level->size()));
			label_level->setAlignment(Qt::AlignCenter);

			QFont font_level;
			font_level.setPointSize(35);

			label_level->setFont(QFont(font_level));
			label_level->setStyleSheet("color:red;");
			//这里是label 显示关卡号的,注意父亲要设置为按钮,如果设置为窗体会导致按钮按不了

			connect(levels , &Levels::back , [=] () {
				this->show();
			});
//下一个窗口返回这里需要的connect
		}

关卡窗口

选关之后就正式进关了

我们需要创建金币类,一关是4*4的金币,难点是如何翻动附近的金币。

这里我没跟着教程做,凭借算法竞赛的经验,自己口胡出来了一个方法。

for(int i = 1 ; i <= 4 ; i ++)
		for (int j = 1; j <= 4; j ++)
		{
			QLabel* label = new QLabel(this);
			QPixmap pix(":/source/coinbackground.png");
			pix = pix.scaled(QSize(0.8 * pix.size()));
			label->setGeometry(0 , 0 , pix.width() , pix.height());
			label->setPixmap(pix);
			label->move(250 + pix.width() * (j - 1) , 53 + pix.height() * (i - 1));
			//贴图部分

			myCoin* coin = new myCoin(lev[this->number - 1][i - 1][j - 1] , this);
			v[this->number - 1].push_back(coin);
			coin->move(230 + pix.width() * (j - 1) , 30 + pix.height() * (i - 1));
			//创建金币类,每关一个vector, 16个金币地址按顺序存进vector
	
		}
	int f = 0;
	for (QVector<myCoin*>::iterator it = v[this->number - 1].begin(); it != v[this->number - 1].end(); it ++)
	{
		f ++;
		connect(*it , &QPushButton::clicked , [=] () {
			(*it)->turn();
			if (f > 4) v[this->number - 1][f - 4 - 1]->turn();
			if (f <= 12) v[this->number - 1][f + 4 - 1]->turn();
			if (f % 4 != 1) v[this->number - 1][f - 1 - 1]->turn();
			if (f % 4 != 0) v[this->number - 1][f + 1 - 1]->turn();
			isWin();
		});
	}

金币是线性存储的,怎么应用的4*4行列里呢
当前数-4就是上一行 注意判断是不是第一行
同理+4是下一行 注意判断是不是最后一行
前后的话好判断 注意判断是不是在头和尾 如果%4 = 0 是最后一列 %4 = 1 是前一列

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值