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