Cocos2d-x 3.2制作三消游戏《万圣大作战》3:触摸事件与精灵的交换

2015年03月19日 11:37 0 点赞 0 评论 更新于 2025-11-21 17:42

在开发三消类游戏时,使用Cocos2d-x需要解决两个关键问题:触摸事件处理和精灵交换。下面将详细介绍这两个问题的实现方法。

1. 触摸事件处理

在三消游戏中,玩家需要通过滑动屏幕来操作,因此需要实现触摸事件处理,以获取触摸起始点和触摸方向,进而确定移动的起始精灵和终止精灵。

1.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);

在Cocos2d-x中,监听器共有五种类型,分别是触摸事件、键盘响应事件、加速记录事件、鼠标响应事件和自定义事件。其中,触摸事件的监听器又分为单点触摸和多点触摸。在3.0版本以前,使用的CCTouchBeganCCTouchMovedCCTouchEnd等方法已被弃用,现在通过监听器绑定相应的处理函数。

1.2 定义关键变量

为了获取起始精灵和终止精灵,需要定义两个变量staSpriteendSprite,并在onTouchBeganonTouchMoved函数中进行操作。

1.3 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;
}

1.4 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. 交换精灵

交换精灵有两种情况:交换后满足消除条件则进行消除操作;交换后不满足消除条件则将精灵恢复到原来的位置。

2.1 swapSprite函数实现

// 交换精灵
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 colChainListOfFirst;
getColChain(staSprite, colChainListOfFirst);
std::list rowChainListOfFirst;
getRowChain(staSprite, rowChainListOfFirst);
std::list colChainListOfSecond;
getColChain(endSprite, colChainListOfSecond);
std::list 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));
}

swapSprite函数的执行流程如下:

  1. 获取初始精灵和终止精灵的位置。
  2. 在地图数组中交换两个精灵的位置(后台交换)。
  3. 检查是否满足消除条件。
    • 如果满足消除条件,执行交换动作。
    • 如果不满足消除条件,将后台交换的精灵恢复到原来的位置,并执行交换再恢复的动作。

总结

至此,我们完成了触摸事件处理和精灵交换的实现。可以运行程序,查看效果。通过上述步骤,我们实现了三消游戏中精灵交换的核心功能,为后续的开发奠定了基础。

作者信息

boke

boke

共发布了 3994 篇文章