如何逆向推导交错Tile Map中由像素位置求瓦片位置的算法
Got it, let's break down how to reverse this staggered tile map coordinate conversion step by step. First, let's recap what the original algorithm does so we're clear on exactly what we need to undo:
Original algorithm (tile to pixel):
float diffX = 0; if ((int)tilePos.y % 2 == 1) diffX = tileSize.width / 2; return Vec2 ( tilePos.x * tileSize.width + diffX, (mapSize.height - tilePos.y - 1) * tileSize.height / 2);
Key Observations from the Original Code
- Y-axis: Tile Y positions are mapped from bottom-to-top (since
mapSize.height - tilePos.y - 1flips the vertical direction), and each tile takes up half its full height in pixel space. - X-axis: Odd-numbered tile rows (Y is odd) are offset right by half a tile width; even rows have no offset.
Step-by-Step Reverse Calculation
1. First Solve for tilePos.y
The Y calculation doesn't depend on X, so we start here. Rearrange the original Y formula:
Original: pixelY = (mapSize.height - tilePos.y - 1) * (tileSize.height / 2)
Rearranged to solve for tilePos.y:
tilePos.y = mapSize.height - 1 - (pixelY / (tileSize.height / 2))
We cast the result to an integer because tile positions are discrete—this effectively truncates (floors) the value, which correctly maps the pixel Y to its corresponding tile row.
2. Adjust Pixel X and Solve for tilePos.x
Since X depends on whether tilePos.y is odd/even, we first use the tilePos.y we just calculated to adjust the pixel X:
- If
tilePos.yis odd: Subtract half the tile width from the pixel X to undo the original offset. - If even: No adjustment needed.
Then reverse the original X formula (pixelX = tilePos.x * tileSize.width + diffX):
tilePos.x = adjustedPixelX / tileSize.width
Again, cast to an integer to get the discrete tile column.
Full Reverse Algorithm Code
Here's the code implementing this logic, matching the style of the original snippet:
Vec2 pixelToTilePos(Vec2 pixelPos, Vec2 tileSize, Vec2 mapSize) { // Calculate tile Y first (since X depends on Y's parity) float halfTileHeight = tileSize.height / 2.0f; int tileY = mapSize.height - 1 - static_cast<int>(pixelPos.y / halfTileHeight); // Adjust pixel X based on tile Y's parity float adjustedPixelX = pixelPos.x; if (tileY % 2 == 1) { adjustedPixelX -= tileSize.width / 2.0f; } // Calculate tile X int tileX = static_cast<int>(adjustedPixelX / tileSize.width); // Optional: Clamp values to valid map bounds to avoid out-of-bounds tiles tileX = std::max(0, std::min(tileX, static_cast<int>(mapSize.width) - 1)); tileY = std::max(0, std::min(tileY, static_cast<int>(mapSize.height) - 1)); return Vec2(tileX, tileY); }
Notes
- Integer Truncation: Using
static_cast<int>works here because we're dealing with positive coordinates (assuming your pixel origin is top-left or bottom-left as standard). If you have negative coordinates, usestd::floor()instead to ensure correct truncation. - Bounds Clamping: The optional clamp step ensures you don't get tile positions outside the map's dimensions—this is a good safety measure, but you can omit it if you're confident input pixels are always within the map.
内容的提问来源于stack exchange,提问作者Eugene Lim

