cocos2d-x实现一个PopStar(消灭星星)游戏的逻辑分析及源码

前言

说起PopStar这个游戏,或许很多人都不知道是啥,但是如果说起消灭星星的话,可能就会有很多人恍然大悟,原来是它。那么,这个消灭星星长得什么样子呢?我们用一张图来看看:
在这里插入图片描述emmm,是的,具体来说,长得就是这样,我们通过点击图片上某一个颜色的星星块,如果,这个颜色块周围存在和他相同的颜色块时,它就会消除掉所有相同的颜色块。直到屏幕上没有颜色块或者不能找到相邻的同色颜色块块时,游戏就结束。
以上呢就是游戏的一个玩法,而我们设计游戏的人,首先要了解的,也是这个游戏的玩法,并且提取有用信息,作为我们的头脑风暴的结果。比如以下几个点:
1.点击事件
2.消除相同颜色块
3.如果有消除,颜色块从上到下填充
4.如果某一列为空,颜色块从右往左填充

以上五点,应该就是整个游戏的核心功能。因此,我们接下来就逐一来探讨以下逻辑的实现:

1.如何响应点击事件

在cocos2d-x中,对鼠标的响应事件封装得很好,在本次的游戏中,我们只需要响应其单点响应即可,具体的一个写法可以为:

#创建一个鼠标监听对象
auto touchListener = EventListenerTouchOneByOne::create();
#为该对象指定相应事件
touchListener->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
#注册鼠标监听对象
Director::getInstance()->getEventDispatcher()->addEventListenerWithSceneGraphPriority(touchListener, this);

以上三个步骤,实际上应该是所有响应事件的日常操作了吧,当然也只是应该,毕竟我目前遇到的套路大致都如此,如果你不是用cocos来进行游戏开发的话,只需要把这部分调整成你所用的语言或者框架的响应事件逻辑即可。
以上,我们是实现了如何响应鼠标事件,但怎么响应呢?实际上就是上面代码中的第二部分:

#HelloWorld::onTouchBegan 此处为响应逻辑,也是主要的游戏逻辑入口
CC_CALLBACK_2(HelloWorld::onTouchBegan, this);

实际上,它在游戏中的逻辑是这样的:

bool HelloWorld::onTouchBegan(cocos2d::Touch *t, cocos2d::Event *e)
{	
#获取当前颜色块
	Vec2 point1 = t->getLocation();
	Size visibleSize = Director::getInstance()->getVisibleSize();
	PopSprite *cur = getPopSpriteByPoint(visibleSize, point1);
	if (cur != NULL)
	{	
		index = 0;
		memset(visit, 0, sizeof(visit));
		#收集相同颜色块 
		checkPopUDLR(cur);
		#删除相同颜色块
		if (index>1)
		{
			for (int i = 0; i < index; i++)
			{
				sameStart[i]->setNumber(-1);
			}
			index = 0;
		}
		#更新上下
		updateUD();
		#更新左右
		updateLR();
	}
	return true;
}

注意以上的几个注释:是不是就和我们剩下的几个步骤很是接近?实际上,这个游戏的核心都已经封装在这一小小的逻辑里面,那事不宜迟,我们来揭露以下的几个问题:

怎么收集到相同的颜色块?

这个问题其实很简单,获取相同的颜色块就如同走迷宫,如何获取全部可走的迷宫呢?这里用到的是深搜的思想:何为深搜,就是从当前的颜色块出发,按照上下左右(这里的顺序可以调整)去访问附近的颜色块,如果附近的颜色块颜色相同,重复搜索的步骤,否则,跳过。当等到不能在往下搜索的时候,就代表了全部的相同且相邻的颜色块被找齐了,此时,我们只需要用一个数组存储这些相同的颜色块,等到搜索结束后,删除即可:搜索部分如下:

	/**
	这部分是实现准备好的变量,所以此处注释了
	#x的两个方向
	int xx[4] = { 1,0,0,-1 };
	#y的两个方向,联合就是针对x和y的上下左右四个方向
	int yy[4] = { 0,1,-1,0 };
	#是否被访问过,防止重复访问
	int visit[10][10] = { 0 };
	#保存相同颜色块的数组
	PopSprite*  sameStart[10*10];**/
void HelloWorld::checkPopUDLR(PopSprite* cur) 
{
	#获取当前颜色块的坐标
	int x = cur->getX();
	int y = cur->getY();
	#设置该坐标的访问标识为1,代表访问过
	visit[x][y] = 1;
	#添加当前颜色块
	sameStart[index++] = cur;
	#循环,实现四个方向的轮询
	for (int  i = 0; i < 4; i++)
	{
		#获取第一个方向:根据xx和yy的顺序调整
		int x1 = x + xx[i];
		int y1 = y + yy[i];
		#判断是否出界
		if (x1 >= 0 && x1 < 10 && y1 >= 0 && y1 < 10)
		{
			#如果符合相同颜色,并且没有被访问过
			if (popStarSprite[x1][y1]->getNumber() == cur->getNumber() && visit[x1][y1] == 0)
			{	
			#从当前颜色块出发,继续轮询,知道所有轮询结束
				checkPopUDLR(popStarSprite[x1][y1]);
			}
		}
	}
}

通过上面的方法,我们就可以实现手机相同颜色块,接下来就是,消灭这些颜色块。具体如下:

#注意:此处的index代表相同颜色块的个数,如果index为1时
#代表只有一个颜色块,此时不做消灭操作
if (index>1)
		{
		#遍历所有颜色块,删除
		#删除是指将其内部的值设置为初值,此处为-1
			for (int i = 0; i < index; i++)
			{
				sameStart[i]->setNumber(-1);
			}
			#清空数组
			index = 0;
		}

以上是消灭操作,消灭完之后,需要把空的地方天上,需要满足两个步骤:

上下填充,以及左右填充:

先看上下填充

上下填充的方式,其实是把每一列的星星抽出来,值为-1的值挪到前面,剩下的自然按顺序往下降,具体做法是:按从下网上的顺序,把值不为初值的数全部按顺序抽取排列,再把剩余的空位补-1.最后把这一列数按顺序回归该列:如下:

void HelloWorld::updateUD()
{
	#轮询10列
	for (int  x = 0; x < 10; x++)
	{
		
		dataIndex = 0;
		#抽取每一列的各个数
		for (int  y = 0; y <10; y++)
		{
			#如果初值不为1(-1)
			if (popStarSprite[x][y]->getNumber() != -1)
			{
				#将其放入新的数组
				data[dataIndex++] = popStarSprite[x][y]->getNumber();
			}

		}
		#剩下的位数,填充为-1
		for(;dataIndex<10; dataIndex++)
		{
			data[dataIndex] = -1;
		}
		dataIndex = 0;
		for (int y = 0; y < 10; y++)
		{
			#按顺序填充回到原二位数组
			popStarSprite[x][y]->setNumber(data[dataIndex++]);
		

		}
		
	}
}

再看左右填充

左右填充的做法是:从左往右,查询某列为空,如果是,将其挪至最后一列,注意,如果已经挪了一次,则我们所查询的次数就响应减少即可:具体如

void HelloWorld::updateLR()
{
	#从左往右遍历。此处的count初始为0,代表每有被挪动的列数
	for (int  x = 0; x < 9-count; x++)
	{
		#查询是否某列全部为空
		bool isHasToLeft = true;
		for (int y = 0; y < 10; y++)
		{
			if (popStarSprite[x][y]->getNumber() != -1)
			{
				isHasToLeft = false;
				break;
			}
		}
		#如果为空,则 isHasToLeft值为true
		if (isHasToLeft)
		{
			# isHasToLeft值为true,代表需要挪动该列,count+1,
			count++;
			#往右挪动整列
			for (int j=x;  j< 9;j++)
			{
				for (int  k = 0; k < 10; k++)
				{
					popStarSprite[j][k]->setNumber(popStarSprite[j + 1][k]->getNumber());
					popStarSprite[j+1][k]->setNumber(-1);
				}
			}
			x--;
		}
	}

}

至此:关于这款游戏的核心点已经讲解完毕:附上一个DEMO版本,仅供参考:
链接:https://pan.baidu.com/s/1U6v3TXS7A60FGVM6CbokPw
提取码:wobh

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: Age of Ai 设计师:meimeiellie 返回首页