如何获取任意位置对应的正确六边形瓦片坐标?代码偏移问题求助
六边形网格坐标吸附功能故障排查与修复
问题描述
我在Unity中实现六边形网格坐标吸附功能:用灰色球体标记鼠标射线检测的命中位置,黄色球体标记吸附后的六边形瓦片位置。该功能在靠近网格中心时正常工作,但远离中心后便失效。
Blender中六边形瓦片截图:
现有代码
using UnityEngine; public class HexDebug : MonoBehaviour { [SerializeField] private LayerMask hexLayerMask; private const float tileSize = 1.1547f; private Camera mainCamera; private void Awake() { mainCamera = Camera.main; } private Vector3 GetMouseHitPoint() { var mousePosition = Input.mousePosition; var ray = mainCamera.ScreenPointToRay(mousePosition); return !Physics.Raycast(ray, out var hit, Mathf.Infinity, hexLayerMask) ? Vector3.zero : hit.point; } private Vector3 GetGridPosition() { return SnapHexToGrid(GetMouseHitPoint()); } private static Vector3 SnapHexToGrid(Vector3 worldPosition) { var col = (int)Mathf.Floor(worldPosition.x / (Mathf.Sqrt(3) * tileSize)); var row = (int)Mathf.Floor(worldPosition.z / (2f * tileSize * (3f / 4f))); var shouldOffset = row % 2 == 0; var width = Mathf.Sqrt(3) * tileSize; var offset = shouldOffset ? width / 2f : 0f; var xPosition = col * width + offset; var yPosition = row * 2f * tileSize * (3f / 4f); return new Vector3(xPosition, 0f, yPosition); } private void OnDrawGizmos() { if (!Application.isPlaying) return; Gizmos.DrawSphere(GetMouseHitPoint() + new Vector3(0f, 0.5f, 0f), 0.5f); Gizmos.color = Color.yellow; Gizmos.DrawSphere(GetGridPosition() + new Vector3(0f, 0.5f, 0f), 0.5f); } }
问题分析与修复
核心问题出在六边形网格坐标转换的数学逻辑上,当前取整方式未考虑偏移行的坐标修正,远离中心时误差累积导致吸附位置偏离正确瓦片。
错误点拆解
- 列计算忽略行偏移影响:奇偶行的列坐标基准存在偏移,但当前直接用
worldPosition.x除以宽度取整,未根据行偏移调整计算值,导致列坐标错误。 - 取整方式不匹配六边形网格特性:六边形网格需先转轴向坐标再转回世界坐标,直接用
Mathf.Floor取整会引发坐标偏移。
正确的坐标转换实现
替换SnapHexToGrid方法为以下代码,采用标准**轴向坐标(Axial Coordinates)**转换逻辑,确保任意位置吸附准确:
private static Vector3 SnapHexToGrid(Vector3 worldPosition) { float tileSize = 1.1547f; // 世界坐标转轴向坐标(q,r) float q = (worldPosition.x * Mathf.Sqrt(3)/3 - worldPosition.z / 3) / tileSize; float r = worldPosition.z * 2/3 / tileSize; // 轴向坐标取整(通过立方体坐标舍入修正误差) int qInt = Mathf.RoundToInt(q); int rInt = Mathf.RoundToInt(r); int sInt = Mathf.RoundToInt(-q - r); // 确保q + r + s = 0,修正舍入误差 float qDiff = Mathf.Abs(q - qInt); float rDiff = Mathf.Abs(r - rInt); float sDiff = Mathf.Abs(-q - r - sInt); if (qDiff > rDiff && qDiff > sDiff) { qInt = -rInt - sInt; } else if (rDiff > sDiff) { rInt = -qInt - sInt; } // 轴向坐标转回世界坐标 float x = tileSize * Mathf.Sqrt(3) * (qInt + rInt/2f); float z = tileSize * 3/2f * rInt; return new Vector3(x, 0f, z); }
关键说明
- 轴向坐标系统:六边形网格的标准定位方式,通过(q,r,s)三个坐标(满足q+r+s=0)避免奇偶行偏移的复杂判断。
- 舍入修正:浮点数计算误差可能导致坐标不满足q+r+s=0,通过差值比较修正,确保坐标落在正确瓦片上。
- 尺寸匹配:代码中
tileSize与你定义的常量一致,对应瓦片外接圆半径,转换逻辑完全匹配截图中的瓦片尺寸。
额外优化建议
- 将
tileSize改为可序列化字段,直接在Inspector中调整,避免硬编码。 - 射线检测时添加
Debug.DrawRay辅助调试,确认射线是否正确命中瓦片。
内容的提问来源于stack exchange,提问作者Disco Fever
相关产品推荐
相关产品推荐

