两个网格单元之间的距离,没有对角线

时间:2019-06-19 09:07:23

标签: c# unity3d math geometry

我已经在一个小项目上工作了几天,直到我将我的“地图”实现更改为与我所基于的游戏(Dofus)相同时,一切都运转良好(这对于社区)。

基本上,我将网格布局旋转了45°(请参见下图),从左上角到右下角进行构造。每个像元xIndex和zIndex代表它在图像上的位置(xIndex; zIndex),我只想获取两个像元之间的距离,而不用对角线移动。

Grid

我试图在图片上解释:

  • GetDistanceBetweenTiles(A,B)应该为3

  • GetDistanceBetweenTiles(A,C)应该为5

  • GetDistanceBetweenTiles(B,C)应该为2

我发现了“曼哈顿距离”,看起来像是我想要的,但没有给我上面的值。

这是代码:

private int GetDistanceBetweenTiles(MovableObject a, MovableObject b)
{      
    //int dist = Mathf.Abs(a.xIndex - b.xIndex) + Mathf.Abs(a.zIndex - b.zIndex);
    int minX = a.xIndex < b.xIndex ? a.xIndex : b.xIndex;
    int maxX = a.xIndex > b.xIndex ? a.xIndex : b.xIndex;
    int minZ = a.zIndex < b.zIndex ? a.zIndex : b.zIndex;
    int maxZ = a.zIndex > b.zIndex ? a.zIndex : b.zIndex;

    int distX = (maxX - minX);
    int distZ = (maxZ - minZ);

    int dist = Mathf.Abs(maxX - minX) + Mathf.Abs(maxZ - minZ);

    print($"Distance between {a.name} and {b.name} is {dist}");

    return dist;
}

任何帮助将不胜感激。

如果有帮助,here is the project working with the first map实现了我所做的(但尚未翻译)。

2 个答案:

答案 0 :(得分:2)

使用简单的公式在倾斜的行中创建新坐标:

row = z/2 - x   ("/" for **integer division**)
col = z - row

现在我们可以将曼哈顿距离计算为

abs(row2 - row1) + abs(col2 - col1)

以您为例

x   z       r   c  
4,  2  =>  -3,  5
1,  4  =>   1,  4 
distance = (1-(-3)) + (5-4) = 4 + 1 = 5

说明:您的网格旋转了45度:

  0  1  2  3  4  5  6  7  8    \column   

              40|41               row -4
           30|31|42|43            row -3   
        20|21|32|33|44|45         row -2
     10|11|22|23|34|35|46|47      row -1  
  00|01|12|13|24|15|36|37|48      row 0
     02|03|14|15|26|27|38         row 1
        04|05|16|17|28            row 2
           06|07|18               row 3

答案 1 :(得分:1)

“无数学”解决方案

我可能会为您提供解决方法。我是个懒惰的人,数学很差……所以我通常让Unity在像您这样的情况下为我做数学;)

为此,您需要一个专用的GameObject,它以表示网格“旋转”的方式旋转,因此0,45,0

然后-由于您的图块始终始终在旋转的坐标系中以1的步长完全移动-您可以使用基于索引的距离来代替,而直接使用Transform.InverseTransformPoint比较绝对位置,以便获取相对于旋转对象的位置。

InverseTransformPoint像上述那样重新调谐所使用的变换在本地空间中的给定世界位置,以便将对象最初放置在例如x=1, z=1在我们旋转的局部空间中的位置将为z=1.1414..., x=0

我只是将此组件附加到旋转的对象上。.实际上我是在Awake中折腾以确保;)

public class PositionsManager : MonoBehaviour
{
    // I know .. singleton pattern .. buuu
    // but that's the fastest way to prototype ;)
    public static PositionsManager Singleton;

    private void Awake()
    {
        // just for making sure this object is at world origin
        transform.position = Vector3.zero;

        // rotate the object liek you need it
        // possible that in your case you rather wanted -45°
        transform.eulerAngles = new Vector3(0, 45, 0);

        // since InverseTransformPoint is affacted by scale
        // just make sure this object has the default scale
        transform.localScale = Vector3.one;

        // set the singleton so we can easily access this reference
        Singleton = this;
    }

    public Vector2Int GetDistance(Transform from, Transform to)
    {
        var localPosFrom = transform.InverseTransformPoint(from.position);
        var localPosTo = transform.InverseTransformPoint(to.position);

        // Now you can simply get the actual position distance and return 
        // them as vector2 so you can even still see the components
        // seperately
        var difference = localPosTo - localPosFrom;

        // since you are using X-Z not X-Y you have to convert the vector "manually"
        return new Vector2Int(Mathf.RoundToInt(difference.x), Mathf.RoundToInt(difference.z));
    }

    public int GetAbsoluteDistance(Transform from, Trasnform to)
    {
        var difference = GetDistance(from, to);

        return Mathf.Abs(difference.x) + Mathf.Abs(difference.y);
    }
}

现在,当您需要获取绝对距离时,您可以轻松完成

var difference = PositionsManager.Singleton.GetDistance(objectA.transform, objectB.transform);
var absoluteDistance = PositionsManager.Singleton.GetAbsoluteDistance(objectA.transform, objectB.transform);

小样(因为我有^^,所以使用了棋盘抽屉)

enter image description here


数学解决方案

在写上层说明时才来找我:

您已经知道在图块之间的步骤:一直Mathf.Sqrt(2)

同样,您可以简单地使用世界上的绝对位置并像比较它们

private float Sqrt2;

private void Awake()
{
    Sqrt2 = Mathf.Sqrt(2);
}

...

  // devide the actual difference by Sqrt(2)
  var difference = (objectA.position - objectB.position) / Mathf.Sqrt(2);
  // again set the Vector2 manually since we use Z not Y
  // This step is optional if you anyway aren't interrested in the Vector2
  // distance .. jsut added it for completeness
  // You might need the rounding part though
  var fixedDifference = new Vector2Int(Mathf.RoundToInt(difference.x), Mathf.RoundToInt(difference.z));

  // get the absolute difference
  var absoluteDistance = Mathf.Abs(fixedDifference.x) + Mathf.Abs(fixedDifference.y);

...

完全无需处理索引。