Qt实现打地鼠游戏的方法详解

2022-11-13 18:11:38 方法 详解 打地鼠

今天与大家分享一个小游戏的实现:打地鼠

看一下实现效果吧~

在指定的时间内打中一定数额的地鼠,否则失败,就如上述展示效果一样,自己写的小程序,居然连第二关也过不去,还挺尴尬的!

实现打地鼠小游戏不难,最主要的核心就是依靠定时器,按照一定间隔触发。接下来,我来讲解下是如何实现的吧!

功能讲解

开发环境:VS2017 + Qt5.14.2 x64

1.确定地鼠数量

对于打地鼠这款游戏来说,地鼠是从任意的洞口钻出。

在效果中,一共存在6个地鼠洞,最容易实现的方式:创建6个地鼠,每个地鼠洞都对应一个地鼠。

实现代码如下:

for (int i = 0; i < num; i++)
{
	QPushButton *btn = new QPushButton(this);
	int nTop = i / 3 == 0 ? 300 : 450;
	int nRemainder = i % 3;
	if (nRemainder == 0)
	{
		btn->setGeometry(190, nTop, 180, 130);
	}
	else if (nRemainder == 1)
	{
		btn->setGeometry(480, nTop, 180, 130);
	}
	else if(nRemainder == 2)
	{
		btn->setGeometry(780, nTop, 180, 130);
	}
	btn->setStyleSheet(qsBtnStyle);
	btn->setProperty("num", i);
	btn->hide();
	connect(btn, &QPushButton::clicked, this, &QGrameWhacAmole::OnBnClickedSusliks); //选中地鼠
	m_vetBtnCtrls.push_back(btn);
}

代码解析:

num:此刻代表的是6,表明了需要创建6个地鼠,平均分配到每个洞中。

对每个地鼠按钮响应对应的clicked消息,每当打中一个地鼠,OnBnClickedSusliks消息内就对分数+1。

并且,创建出的地鼠默认是隐藏状态的。

2.定义游戏难易程序

在程序中定义了四种难度,设置了枚举类型:

enum ENUM_GameMode
{
	GameMode_difficulty1,
	GameMode_difficulty2,
	GameMode_difficulty3, 
	GameMode_difficulty4, 
	GameMode_OK,
	GameMode_Failed,
};

GameMode_difficulty1:难度1,说明有1个地鼠出没

GameMode_difficulty2:难度2,说明有2个地鼠出没,

GameMode_difficulty3:难度3,说明有3个地鼠出没,

GameMode_difficulty4:难度4,说明有4个地鼠出没

在程序中,如何判断通过某一关呢?

宏定义确定通关分数

#define  difficulty1Count 10 //难度1个数
#define  difficulty2Count 30 
#define  difficulty3Count 60
#define  difficulty4Count 100

当第一关时,只需要打中10个地鼠;第二关需要累计打中30个地鼠,以此类推。

3.难度切换

在OnBnClickedSusliks消息中,根据当前的分数来确定是否要晋级。

响应消息后,对分数进行+1处理

m_nScore += 1;

m_nScore是当前类的成员变量,表示:打中的地鼠次数也就是当前分数。

打中后隐藏该地鼠

当打中某个地鼠后,需要立刻隐藏地鼠,此时就运用到了刚刚在创建地鼠时"setProperty"绑定的变量了。

QPushButton *btn = qobject_cast<QPushButton*>(sender());
int num = btn->property("num").toInt();
m_vetBtnCtrls[num]->hide(); //隐藏对应编号控件

分数判断是否晋级

当分数到达难度1时,晋升成难度2,其它的关卡都一样

if (m_nScore == difficulty1Count) //难度1通过
{
	killTimer(m_nTimerStartId);
	m_nTimerStartId = 0;

	this->HideTotalSucliks();
		
	m_enumMode = GameMode_difficulty2;
	this->SetTipsStyle(m_enumMode);

	m_nTimerStartId = startTimer(difficulty2Time);
	m_dwBeginTime = GetTickCount();
}

当通过第一关后,停止定时器,隐藏正在展示的所有地鼠,更改模式状态,重新设置提示文本,开始定时器,重新记录开始通关时间。

m_dwBeginTime:是记录每次开始游戏时的时间,主要作用于挑战失败的判断,也就是说,每次触发定时器时,当前最新时间与最开始通关时间的差值 大于 通关时间时,说明当前关卡挑战失败!

4.定时器处理

这也是当前小游戏中最核心的处理部分了~

为了方便起见,直接使用QWidget自带的定时器,而不是使用new QTimer的方式

virtual void timerEvent(QTimerEvent *event);

在定时器的处理中,分成了4部分,我们分别讲述~

获取定时器Id的触发消息

if (event->timerId() == m_nTimerStartId)
{
    //消息处理
}

只有当定时器的触发id与我们设定的id一致时,才可以。

关闭提示页面

在进行难度切换时,设置了提示文本,也就是效果图中的“开始”、“开始第二关”等文字提示信息,在进入到定时器事件中,首先判断,该控件是否隐藏?如果未隐藏,先进行隐藏。

if (ui.labTips->isHidden() == false)
{
	ui.labTips->hide();
}

在这里需要我走过一个坑:使用isVisible()不一定获取出控件的显示状态,但是isHidden()始终是有效的

判断当前关卡是否超时?

这也就是上文说到的m_dwBeginTime与最新触发时间的差值

DWord dwTime = GetTickCount() - m_dwBeginTime;
if (dwTime > difficultyTimeout)
{
	this->RunningFailed();
}

根据关卡不同,显示不同的地鼠

这里,就是对地鼠显示的逻辑处理了,根据枚举模式不同,分别处理

switch (m_enumMode)
{
case QGrameWhacAmole::GameMode_difficulty1:
	this->RunningGamedifficulty(1);
break;
case QGrameWhacAmole::GameMode_difficulty2:
	this->RunningGamedifficulty(2);
break;
case QGrameWhacAmole::GameMode_difficulty3:
	this->RunningGamedifficulty(3);
break;
case QGrameWhacAmole::GameMode_difficulty4:
	this->RunningGamedifficulty(4);
break;
default:
break;
}

核心函数是:RunningGamedifficulty

如何让地鼠进行随机显示呢?

在当前例子中,获取随机数[0,6)之间的值,随机到哪个数,哪个下标下对应的地鼠被显示,其余的地鼠处于隐藏状态。

随机数生成方法:

int QGrameWhacAmole::GetRandomNumber()
{
	QTime time = QTime::currentTime();
	qsrand(time.msec() + time.second() * 1000);
	int n = qrand() % 6;
	return n;
}

有人说使用这种方法可以在短时间内生成的随机数不相同,这个方法我已经验证过了,不行!

还有的人说可以添加sleep,我也尝试过了,不行!

那么,该如何获取不重复的随机数呢?

在这里,采用了std::set<int>容器的方式,RunningGamedifficulty中的部分代码如下:

void QGrameWhacAmole::RunningGamedifficulty(int nCount)
{
    1:随机数生成
    std::set<int> setRandom; //存储随机数
    for (int i = 0; setRandom.size() < nCount; i++)
    {
	//获取随机数
	int num = this->GetRandomNumber();
	//如果随机数在容器中从未出现过,存储并应用
	if (setRandom.size() != 0)
	{
		std::set<int>::iterator itFind = setRandom.find(num);
		if (itFind != setRandom.end())
		{
			continue; //存在重复值,后续不进行处理
		}
	}
	//容器中存在数据,存储之前进行判断
	setRandom.insert(num);
     }
        
        
}

根据上述代码也可以看出,每生成一个随机数,就进行存储,当容器中出现相同的随机数时,重新生成。

nCount:就是需要展示的地鼠个数,在for循环中,中间的判断条件与以往不同,当有效地地鼠编号大于地鼠个数后,就不再获取随机数了。

这种方式,无论是获取多少个地鼠个数都是适用的。

其次,根据获取的显示的地鼠下标数就可以对所有的地鼠进行做显示、隐藏操作了,代码如下:

for (int m = 0; m < m_vetBtnCtrls.size(); m++)
{
	std::set<int>::iterator itNum = setRandom.find(m);
	if (itNum != setRandom.end())
	{
		m_vetBtnCtrls[m]->show();
	}
	else
	{
		m_vetBtnCtrls[m]->hide();
	}
}

总结

到这里,核心的实现功能就已经讲解完了,功能难点:

1:根据地鼠个数随机显示地鼠位置(RunningGamedifficulty处理逻辑)。

2:关卡晋级。

3:挑战失败处理。

到此这篇关于Qt实现打地鼠游戏的方法详解的文章就介绍到这了,更多相关Qt打地鼠游戏内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章