我已经在一个小项目上工作了几天,直到我将我的“地图”实现更改为与我所基于的游戏(Dofus)相同时,一切都运转良好(这对于社区)。
基本上,我将网格布局旋转了45°(请参见下图),从左上角到右下角进行构造。每个像元xIndex和zIndex代表它在图像上的位置(xIndex; zIndex),我只想获取两个像元之间的距离,而不用对角线移动。
我试图在图片上解释:
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实现了我所做的(但尚未翻译)。
答案 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);
小样(因为我有^^,所以使用了棋盘抽屉)
在写上层说明时才来找我:
您已经知道在图块之间的步骤:一直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);
...
完全无需处理索引。