1.觸摸事件
我們玩三消遊戲,就要對屏幕進行滑動,所以需要做一個觸摸事件來處理對屏幕的觸摸。
這裏主要是要獲得開始觸摸和觸摸方向兩個,
因爲只需要知道移動的起始精靈和移動的終止精靈。
所以,在遊戲界面的初始函數,進行觸摸事件的設置(綁定函數和添加監聽器)
// 觸摸事件處理
auto touchListener = EventListenerTouchOneByOne::create();
touchListener->onTouchBegan = CC_CALLBACK_2(GameScene::onTouchBegan, this);
touchListener->onTouchMoved = CC_CALLBACK_2(GameScene::onTouchMoved, this);
_eventDispatcher->addEventListenerWithSceneGraphPriority(touchListener, this);
PS:關於這個監聽器,共有五種:觸摸事件、鍵盤響應事件、加速記錄事件、鼠標響應事件和自定義事件。而且觸摸事件的監聽器還分爲兩種,單點觸摸和多點觸摸。
而且,在3.0版本以前,之前所用的 CCTouchBegan、CCTouchMoved、CCTouchEnd這些都已經被OUT了,現在是讓監聽器自己綁定函數。
再回到本文,之前說過,觸摸事件最重要的是得到起始精靈和終止精靈,所以要定義這兩個變量,然後在onTouchBegan和onTouchMoved進行操作。
先是 onTouchBegan函數:
bool GameScene::onTouchBegan(Touch *touch, Event *unused)
{
staSprite = NULL;
endSprite = NULL;
if ( isTouchEna ) {
auto location = touch->getLocation();
staSprite = spriteOfPoint(&location);
}
return isTouchEna;
}
這裏出現了一個新變量——isTouchEna,從名字中可以看出它的作用,是否可以觸摸。
什麼時候不可以呢? 檢測是否有可消除精靈的時候,精靈正在下落的時候,均不可以觸摸。
這個變量還需要在前面設置一下,
要在構造函數中,設置變量爲 true,
然後在 update函數中 isTouchEna = !isAction; (如果精靈正在移動中,就應該忽視觸摸事件)
在這裏,又出現了一個工具函數——spriteOfPoint(根據觸摸點的位置,返回是地圖中哪個精靈)
// 根據觸摸的點位置,返回是地圖中哪個精靈
SpriteShape *GameScene::spriteOfPoint(Point *point)
{
SpriteShape *spr = NULL;
Rect rect = Rect(0, 0, 0, 0);
Size sz;
sz.height=SPRITE_WIDTH;
sz.width=SPRITE_WIDTH;
for( int r = 0 ; r < ROWS ; ++r ) {
for( int c = 0 ; c < COLS ; ++c ) {
spr = map[r][c];
if( spr ) {
rect.origin.x = spr->getPositionX() - ( SPRITE_WIDTH / 2);
rect.origin.y = spr->getPositionY() - ( SPRITE_WIDTH / 2);
rect.size = sz;
if (rect.containsPoint(*point)) {
return spr;
}
}
}
}
return NULL;
}
然後是 onTouchMoved函數:
// 觸摸後移動的方向
void GameScene::onTouchMoved(Touch *touch, Event *unused) {
// 如果沒有初始精靈 或者 觸摸事件不可行,直接返回
if (!staSprite || !isTouchEna) {
return;
}
// 獲取 初始精靈 的行列
int row = staSprite->getRow();
int col = staSprite->getCol();
// 獲取移動到的 點 的位置
auto location = touch->getLocation();
auto halfSpriteWidth = SPRITE_WIDTH / 2;
auto halfSpriteHeight = SPRITE_WIDTH / 2;
auto upRect = Rect(staSprite->getPositionX() - halfSpriteWidth,
staSprite->getPositionY() + halfSpriteHeight,
SPRITE_WIDTH,
SPRITE_WIDTH);
// 判斷是在向哪個方向移動,
if (upRect.containsPoint(location)) {
++row;
if ( row < ROWS ) {
endSprite = map[row][col];
}
swapSprite();
return;
}
auto downRect = Rect(staSprite->getPositionX() - halfSpriteWidth,
staSprite->getPositionY() - (halfSpriteHeight * 3),
SPRITE_WIDTH,
SPRITE_WIDTH);
if (downRect.containsPoint(location)) {
--row;
if ( row >= 0 ) {
endSprite = map[row][col];
}
swapSprite();
return;
}
auto leftRect = Rect(staSprite->getPositionX() - (halfSpriteWidth * 3),
staSprite->getPositionY() - halfSpriteHeight,
SPRITE_WIDTH,
SPRITE_WIDTH);
if (leftRect.containsPoint(location)) {
--col;
if ( col >= 0 ) {
endSprite = map[row][col];
}
swapSprite();
return;
}
auto rightRect = Rect(staSprite->getPositionX() + halfSpriteWidth,
staSprite->getPositionY() - halfSpriteHeight,
SPRITE_WIDTH,
SPRITE_WIDTH);
if (rightRect.containsPoint(location)) {
++col;
if ( col < COLS ) {
endSprite = map[row][col];
}
swapSprite();
return;
}
// 否則,並非一個有效的移動
}
這裏還有一個函數 swapSprite,顧名思義,就是判斷好向哪個方向移動後,直接交換這兩個精靈。
2. 交換精靈
關於交換精靈,有兩種情況
> 交換後,滿足消除條件,消除
> 交換後,不滿足消除條件,返回原樣
所以,函數是這樣的:
// 交換精靈
void GameScene::swapSprite() {
// 移動中,不允許再次觸摸,執行動作設置爲true
isAction = true;
isTouchEna = false;
// 初始精靈 和 終止精靈 均不能爲空
if (!staSprite || !endSprite) {
return;
}
Point posOfSrc = staSprite->getPosition();
Point posOfDest = endSprite->getPosition();
float time = 0.2;
// 在數組中交換位置
map[ staSprite -> getRow() ][staSprite -> getCol() ] = endSprite;
map[ endSprite -> getRow() ][endSprite -> getCol() ] = staSprite;
int tmpRow = staSprite->getRow();
int tmpCol = staSprite->getCol();
staSprite->setRow(endSprite->getRow());
staSprite->setCol(endSprite->getCol());
endSprite->setRow(tmpRow);
endSprite->setCol(tmpCol);
// 檢查是否能消除
std::list<SpriteShape *> colChainListOfFirst;
getColChain(staSprite, colChainListOfFirst);
std::list<SpriteShape *> rowChainListOfFirst;
getRowChain(staSprite, rowChainListOfFirst);
std::list<SpriteShape *> colChainListOfSecond;
getColChain(endSprite, colChainListOfSecond);
std::list<SpriteShape *> rowChainListOfSecond;
getRowChain(endSprite, rowChainListOfSecond);
if (colChainListOfFirst.size() >= 3
|| rowChainListOfFirst.size() >= 3
|| colChainListOfSecond.size() >= 3
|| rowChainListOfSecond.size() >= 3) {
// 如果能夠消除,僅僅進行移動(不會移動回來)
staSprite->runAction(MoveTo::create(time, posOfDest));
endSprite->runAction(MoveTo::create(time, posOfSrc));
return;
}
// 不能消除,則移動過去還要返回
map[ staSprite -> getRow()][staSprite -> getCol() ] = endSprite;
map[ endSprite -> getRow()][endSprite -> getCol() ] = staSprite;
tmpRow = staSprite->getRow();
tmpCol = staSprite->getCol();
staSprite->setRow(endSprite->getRow());
staSprite->setCol(endSprite->getCol());
endSprite->setRow(tmpRow);
endSprite->setCol(tmpCol);
staSprite->runAction(Sequence::create(
MoveTo::create(time, posOfDest),
MoveTo::create(time, posOfSrc),
NULL));
endSprite->runAction(Sequence::create(
MoveTo::create(time, posOfSrc),
MoveTo::create(time, posOfDest),
NULL));
}
這裏,再看一下這個函數,
就是先獲取初始精靈和終止精靈的位置,
然後,只是單純在地圖中交換(後臺交換,數組變動,在遊戲界面中還是沒有變動的)
判斷是否可以消除,如果可以消除了,那就執行動作 交換兩個精靈位置。
如果不可以消除,不要忘了把後臺交換過的兩個精靈再交換過來,然後在執行兩個動作(就是交換一次,再交換回來)。
這裏,並不是先交換,再判斷,再執行接下來的動作;
而是,先後臺數組交換,然後判斷,然後執行 交換一次加消除 還是交換兩次動作。
好啦,編寫到這裏,我們可以運行一下,看看效果。
到這,精靈的交換已經完成了。
本章資源和代碼:點擊下載
感謝本文筆者LT大樹_的分享,Cocos引擎中文官網歡迎更多的開發者分享開發經驗,來稿請發送至[email protected]。