1. 程式人生 > >cocos2d-x實現一個PopStar(消滅星星)遊戲的邏輯分析及原始碼

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