45 度地图:真实坐标怎么换算成格子坐标
我们项目里的地图是斜 45 度视角的,也就是常说的等距(isometric)地图: 屏幕上看到的每一个「格子」其实是一个扁菱形。策划配表、剧情触发、刷怪点用的都是真实世界坐标, 而寻路、碰撞、视野裁剪要的却是格子坐标。这两者之间隔着一次 45 度旋转, 第一次看那段转换代码的时候我是懵的,后来把映射关系老老实实列成方程才顺过来。
先约定几个参数
- 格子宽度
W = 100,格子高度H = 50(2:1 的菱形,斜视角里最常见的比例) - 整张地图
200 × 200格 - 真实坐标记作
x、y,格子坐标记作gridX、gridY
那个看着有点怪的公式
通用写法是这样:
gridY = 0.5 * ( y / (格子高度 >> 1) − x / (格子宽度 >> 1) ) + 纵向格子总数 / 2
把上面的参数代进去,就是配表里最常见的那一版:
y: ( 0.5 * ( (D1 / 25) − (C1 / 50) ) ) + 100
这里的 D1 是真实坐标的 y,C1 是真实坐标的 x;
25 是 50 >> 1,50 是 100 >> 1,
两个 100 是 200 格的一半。也就是说,这个公式里没有一个数字是拍脑袋来的,
全都能从格子尺寸和地图格数推出来。
为什么长这样
第一步:把真实坐标除以半格。
令 u = x / (W / 2)、v = y / (H / 2),
代入参数就是 u = x / 50、v = y / 25。
这一步的意义是把像素刻度换掉——在等距地图里,沿着格子轴走一格,世界坐标正好变化「半个格宽 + 半个格高」,
所以除以半格之后,u、v 就变成了以格子为单位的量。
第二步:把「沿两条格子轴各走了多少格」换成「行列号」。 格子坐标的两条轴在世界空间里是斜的,一格一格的走法对应的是「行 + 列」的和与差,于是有:
v = gridX + gridY
后面那个「+ 横向格子总数」是地图偏移量:格子地图的左下角在世界坐标里并不在原点, 要先把这个偏移补回来,否则算出来的行列号会整体偏掉半张图。
第三步:解这个小方程组。
把上面两式相减、相加,就能把 gridX、gridY 单独解出来:
gridX + gridY = v → gridY = ( v − u + 200 ) / 2
整理一下就是开头那个公式:一个 0.5 来自除以 2,
一个 − 100(横向格数的一半)、一个 + 100(纵向格数的一半)来自那个偏移量。
所以公式里「一个减、一个加」不是笔误,而是行列号在坐标系里朝向不同导致的。
拿几个坐标验算一下
用 u = x / 50、v = y / 25 代入上面三步算一遍:
| 真实坐标 (x, y) | u | v | 格子坐标 (gridX, gridY) | 说明 |
|---|---|---|---|---|
| (10000, 5000) | 200 | 200 | (100, 100) | 地图正中心 |
| (10000, 0) | 200 | 0 | (0, 0) | 世界矩形下边中点 |
| (20000, 5000) | 400 | 200 | (200, 0) | 世界矩形右边中点 |
| (10000, 10000) | 200 | 400 | (200, 200) | 世界矩形上边中点 |
| (0, 5000) | 0 | 200 | (0, 200) | 世界矩形左边中点 |
这里有个我第一次没反应过来、后来才发现挺关键的点:格子地图是内接在世界矩形里的一个菱形。
世界矩形的范围是 x ∈ [0, 20000]、y ∈ [0, 10000],
而它的四个角算出来是 (−100, 100)、(100, −100)、
(100, 300)、(300, 100)——全都在格子地图外面。
反过来,格子地图的四个角,对应的正是世界矩形四条边的中点,也就是上面表里那四行。
所以配表的时候不能想当然地以为「世界坐标的原点就是地图左下角」。真实坐标 (0, 0)
落在格子地图之外,用它去取格子会直接越界。要按表里这个对应关系去摆东西。
反算:从格子回到真实坐标
拾取点击点、把格子里的配置还原成世界坐标时要用反算,直接把上面第二步的两个式子倒过来:
v = gridX + gridY
x = u * (格子宽度 >> 1)
y = v * (格子高度 >> 1)
拿 gridX = gridY = 100 试一下:u = 100 − 100 + 200 = 200、
v = 200,于是 x = 200 × 50 = 10000、y = 200 × 25 = 5000,
正好回到地图中心,和正算的结果对得上。
两个提醒
- 取整要用 floor,不要用 round。 格子归属是一个「区间」而不是「就近」, 用 round 会让菱形边界附近的坐标来回跳格,表现出来就是点击选中偶尔跳到相邻格。
-
>> 1只对偶数边长成立。 现在格高 50、格宽 100 都是偶数,位运算没问题;哪天策划把格子尺寸改成奇数,>> 1会把 0.5 直接丢掉,必须改回/ 2。 这种地方最容易埋雷,改尺寸的时候一定要顺手搜一遍。