学习cocos2dx 3.3 缩放滑动并且准确点击对象

2015年03月18日 09:50 0 点赞 0 评论 更新于 2025-11-21 13:49

之前已经有一篇更基础的TileMap笔记了,这两天使用Cocos2d-x 3.3的TileMap时,发现以前的部分内容无法适用。因此,我又撰写了这篇札记。

未掌握的同学请注意

主要实现目标

  1. 能够对TiledMap进行3倍缩放。
  2. 能够使用鼠标滑动TiledMap。
  3. 在缩放和滑动的情况下,点击一个Tile能够判断出实际的格子坐标。

缩放和滑动的实现有很多示例,这里不再详细解释,文章末尾会提供完整代码。下面主要介绍第三条目标的实现方法。

从图中可以看到,我创建了一个points对象组,并建立了两个对象:startPosendPosstartPos大概覆盖了4个Tile,其坐标分别为[0,24][1,24][0,25][1,25]。同时,我为startPos设置了一个自定义属性id。我的需求是,当点击上述4个Tile时,能够获取该对象的id值。

读取地图对象

首先,我编写了一个函数来读取地图对象:

void HelloWorld::parseTileMap()
{
CCLOG("parseTileMap");
if (_tiledMap == NULL)
return;
_objectsGroup = _tiledMap->getObjectGroup("points");
// tileX 0 tileY 24
ValueMap startPos = _objectsGroup->getObject("startPos");
std::string name = startPos["name"].asString();
float pointX = startPos["x"].asFloat();
float pointY = startPos["y"].asFloat();
float PointWidth = startPos["width"].asFloat();
float PointHeight = startPos["height"].asFloat();
Size winSize = Director::getInstance()->getWinSize();
Point mapPoint = _tiledMap->getPosition();
Point centerPos = covertTiledPointToCenterPoint(Point(0, 24));
Point tilePos = covertPointToTiledPoint(Point(pointX, pointY));
Point tilePos2 = covertPointToTiledPoint(centerPos);
}

很顺利地拿到了startPos对象的pointXpointY,但值是(0, 296),并非预期的(1, 768),这让我有些困惑。直到查看了TMXLayergetPositionAt源码,才发现(0, 296)是经过“分辨率转换”后的值。

Vec2 TMXLayer::getPositionAt(const Vec2& pos)
{
Vec2 ret = Vec2::ZERO;
switch (_layerOrientation)
{
case TMXOrientationOrtho:
ret = getPositionForOrthoAt(pos);
break;
case TMXOrientationIso:
ret = getPositionForIsoAt(pos);
break;
case TMXOrientationHex:
ret = getPositionForHexAt(pos);
break;
case TMXOrientationStaggered:
ret = getPositionForStaggeredAt(pos);
break;
}
CCLOG("%f,%f,%f", ret.x, ret.y, CC_CONTENT_SCALE_FACTOR());
ret = CC_POINT_PIXELS_TO_POINTS(ret);
return ret;
}

宏定义及坐标转换分析

下面是相关宏的定义:

/** @def CC_POINT_PIXELS_TO_POINTS
Converts a rect in pixels to points
*/
#define CC_POINT_PIXELS_TO_POINTS(__pixels__) \
Vec2( (__pixels__).x / CC_CONTENT_SCALE_FACTOR(), (__pixels__).y / CC_CONTENT_SCALE_FACTOR())

/** @def CC_CONTENT_SCALE_FACTOR
On Mac it returns 1;
On iPhone it returns 2 if RetinaDisplay is On. Otherwise it returns 1
*/
#define CC_CONTENT_SCALE_FACTOR() Director::getInstance()->getContentScaleFactor()

从源码可以得出以下结论:

  1. 可以确定的是,TileMap上的像素(pixel)与点(point)的比例是1:1,但在不同设备上该比例不一定相同。在Mac设备上,像素与点的比例是1:1;在支持Retina显示的iPhone设备上,比例是2:1;低分辨率设备的比例会更大。我们将这个比值称为像素尺寸因子。
  2. 通过CC_POINT_PIXELS_TO_POINTS可以将TileMap上的坐标值转换为设备上的坐标值。
  3. setScalegetScale的功能类似,但需要分开计算。

触摸点转换为TileMap绝对坐标的额外处理

由于TileMap可以滑动和点击,在将触摸点转换为TileMap绝对坐标时,需要额外进行以下两点处理:

1. 以TileMapLayer的坐标作为偏移量

bool HelloWorld::onTouchBegan(Touch *touch, Event* event)
{
CCLOG("HelloWorld::onTouchBegan");
Point touchPoint = touch->getLocation();
Point mapPoint = _tiledMap->getPosition();
Point realPoint = touchPoint - mapPoint;
Point tilePoint = covertPointToTiledPoint(realPoint);
CCLOG("tilePoint x:%f y:%f", tilePoint.x, tilePoint.y);
tryGetObjectPropertyByPosition(realPoint);
return true;
}

2. 乘以缩放系数

// 参数:触摸坐标
string HelloWorld::tryGetObjectPropertyByPosition(Point position)
{
float scale = _tiledMap->getScale();
float factor = CC_CONTENT_SCALE_FACTOR();
float realPointX = position.x / scale;
float realPointY = position.y / scale;
for (auto item : _objectsGroup->getObjects())
{
ValueMap curObject = item.asValueMap();
float pointX = curObject["x"].asFloat();
float pointY = curObject["y"].asFloat();
float PointWidth = curObject["width"].asFloat();
float PointHeight = curObject["height"].asFloat();
if (realPointX >= pointX && realPointX <= pointX + PointWidth &&
realPointY >= pointY && realPointY <= pointY + PointHeight)
{
string objectId = curObject["id"].asString();
CCLOG("HelloWorld:tryGetObjectPropertyByPosition: %s", objectId.c_str());
return objectId;
}
}
return "";
}

触摸点转换为TileMapPoint的逆推方法

接下来,逆推从触摸点转换为TileMapPoint的方法:

Point HelloWorld::covertTiledPointToCenterPoint(Point p)
{
float scale = _tiledMap->getScale();
float factor = CC_CONTENT_SCALE_FACTOR();
CCLOG("HelloWorld::covertTiledPointToCenterPoint scale:%f factor:%f", scale, factor);
int offsetX = _tiledMap->getTileSize().width / (2 * factor);
int offsetY = _tiledMap->getTileSize().height / (2 * factor);
TMXLayer* layer = _tiledMap->layerNamed("background");
Point point = layer->getPositionAt(p);
point = Point(point.x + offsetX, point.y - offsetY);
point = Point(point.x * scale, point.y * scale);
return point;
}

综上所述,通过以上步骤,我们可以实现Cocos2d-x 3.3中TileMap的缩放、滑动以及准确点击对象的功能。完整代码可在文末获取。

作者信息

feifeila

feifeila

共发布了 3994 篇文章