Расстояние между двумя ячейками сетки без диагонали - PullRequest
1 голос
/ 19 июня 2019

Я работал над небольшим проектом в течение нескольких дней, все работало нормально, пока я не изменил свою реализацию "карты" на ту же, что и в игре (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;
}

Любая помощь будет принята с благодарностью.

Если она может помочь, - это проект, работающий с первой реализацией карты , которую я сделал (но еще не переведен).

Ответы [ 2 ]

2 голосов
/ 19 июня 2019

Давайте создадим новые координаты в наклонных строках с помощью простых формул:

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 голос
/ 19 июня 2019

Решение "Нет математики"

Возможно, у меня есть решение для вас. Я немного ленивый человек и очень плохо разбираюсь в математике ... поэтому я обычно позволяю 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);

Little Demo (использовал ящик для шахматной доски, так как у меня был ^^)

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);

...

все еще полностью без необходимости иметь дело с индексами вообще.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...