觸摸點為屏幕像素座標,需轉換為 TMX 地圖的網格座標 (tile coordinates) 以檢測碰撞層 (Meta Layer) 是否可通行。
關鍵步驟:
convertToNodeSpace)。
// 在繼承自 CCTMXTiledMap 的類中,或傳入 map 節點
Vec2 tileCoordForPosition(Vec2 position) {
// 1. 轉換到地圖節點座標系
Vec2 nodePos = this->convertToNodeSpace(position);
// 2. 獲取地圖屬性
Size mapSize = this->getMapSize(); // 瓦片數 (寬, 高)
Size tileSize = this->getTileSize(); // 單個瓦片像素尺寸
// 3. 計算網格索引
int x = static_cast(nodePos.x / tileSize.width);
int y = static_cast((mapSize.height * tileSize.height - nodePos.y) / tileSize.height);
// 關鍵:mapSize.height * tileSize.height = 地圖總高度 (像素)
// nodePos.y 從下往上,需用總高度減去得到從上往下的行號
// 4. 邊界檢查
x = MAX(0, MIN(x, static_cast(mapSize.width) - 1));
y = MAX(0, MIN(y, static_cast(mapSize.height) - 1));
return Vec2(x, y);
}
bool isPositionWalkable(Vec2 touchPos) {
Vec2 tileCoord = tileCoordForPosition(touchPos);
// 獲取 Meta 層
TMXLayer *metaLayer = this->getLayer("Meta");
if (!metaLayer) return true; // 無碰撞層視為可通行
// 獲取該網格的精靈 (GID > 0 表示有碰撞瓦片)
Sprite *tileSprite = metaLayer->getTileAt(tileCoord);
// 或直接獲取 GID
int gid = metaLayer->getTileGIDAt(tileCoord);
return gid == 0; // GID 為 0 表示空瓦片,可通行
}
| 錯誤做法 | 正確做法 | 後果 |
|---|---|---|
直接用 position.x / tileWidth | 先 convertToNodeSpace | 父節點縮放/旋轉/位移導致座標錯誤 |
| Y 軸不翻轉 | mapHeight * tileHeight - nodePos.y | 點擊底部檢測到頂部行,垂直鏡像 |
用 tileWidth 計算 Y | 用 tileHeight | 非正方形瓦片時行號錯誤 |
| 無邊界檢查 | clamp 到 [0, mapSize-1] | 越界訪問 Crash 或邏輯錯誤 |
上述為**正交地圖** (Orthogonal)。等距地圖 (Isometric) 轉換公式不同:
// 等距地圖座標轉換 (參考)
float x = (nodePos.x / tileSize.width + (mapSize.height - nodePos.y / tileSize.height)) / 2;
float y = (mapSize.height - nodePos.y / tileSize.height - nodePos.x / tileSize.width) / 2;
六邊形地圖需根據具體朝向 (pointy-top/flat-top) 推導公式。
核心公式:
x = nodePos.x / tileWidth
y = (mapHeight * tileHeight - nodePos.y) / tileHeight
務必:轉節點座標、Y軸翻轉、邊界檢查。封裝為工具函數復用,避免重複造輪子。