You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取任意位置对应的正确六边形瓦片坐标?代码偏移问题求助

六边形网格坐标吸附功能故障排查与修复

问题描述

我在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);
    }
}

问题分析与修复

核心问题出在六边形网格坐标转换的数学逻辑上,当前取整方式未考虑偏移行的坐标修正,远离中心时误差累积导致吸附位置偏离正确瓦片。

错误点拆解

  1. 列计算忽略行偏移影响:奇偶行的列坐标基准存在偏移,但当前直接用worldPosition.x除以宽度取整,未根据行偏移调整计算值,导致列坐标错误。
  2. 取整方式不匹配六边形网格特性:六边形网格需先转轴向坐标再转回世界坐标,直接用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 17:10:41