2016-04-24 4 views
3

проблемВертикальная Hex сетка: Получить й кольцо плитки окружающей специфично координаты

То, что я пытаюсь сделать, это получить число х колец из заданной точки, и хранить эти кольца в List<List<HexCoordinate>>, где внутренний list - список всех гексов в этом кольце, а HexCoordinate - это структура, определенная ниже

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

Изображения и попытки

У меня есть вертикальная (Flat Top) Hex сетка, которая выглядит следующим образом Overall Hex Grid

В коде, каждая плитка представлена ​​простой структурой HexCoordinate GitHub

public struct HexCoordinate : IEquatable<HexCoordinate> 
{ 
    public int X { get; private set; } 
    public int Y { get; private set; } 

    public HexCoordinate(int x, int y) 
    { 
     X = x; 
     Y = y; 
    } 

    public HexCoordinate North() => this + new HexCoordinate(0, -1); 
    public HexCoordinate South() => this + new HexCoordinate(0, 1); 

    public HexCoordinate West() => this + new HexCoordinate(-1, 0); 
    public HexCoordinate East() => this + new HexCoordinate(1, 0); 

    public HexCoordinate NorthWest() => this + new HexCoordinate(-1, -1); 
    public HexCoordinate NorthEast() => this + new HexCoordinate(1, -1); 

    public HexCoordinate SouthWest() => this + new HexCoordinate(-1, 1); 
    public HexCoordinate SouthEast() => this + new HexCoordinate(1, 1); 

    public bool Equals(HexCoordinate other) 
    { 
     return X == other.X && 
       Y == other.Y; 
    } 
    public static HexCoordinate operator +(HexCoordinate one, HexCoordinate two) 
    { 
     return new HexCoordinate(one.X + two.X, one.Y + two.Y); 
    } 
} 

Для моего примера, в частности, у меня есть изображение, которое я сделал, чтобы попытаться понять эту проблему самостоятельно Close Up Example

То, что я Пытался

И потому, что мне нужно, чтобы показать, что я уже пробовал, это то, что я пытался до сих пор

public const int MAX_RINGS = 3; 
public List<List<HexCoordinate> GetsRingsWithinHex(HexCoordinate coordinate, int maxRings = MAX_RINGS) 
{ 
    // Attempt One Pseudocode 
    // reference: http://gamedev.stackexchange.com/questions/51264/get-ring-of-tiles-in-hexagon-grid 
    // int ring = 1 
    // Travel around the ring by traversing N,SE,S,SW,NW,N,NE multiplied by the ring number 
    // ring++ 
    //  Travel Around ring again 
    //  cont until desired ring... 

    var hexRings = new List<List<HexCoordinate>>(); 

    // Add in the current hex to the list 
    var currentHex = new List<HexCoordinate>(); 
    currentHex.add(coordinate); 
    hexRings.Add(currentHex); 

    // Now go through and add the other rings 
    while (hexRings.Count <= maxRings) 
    { 
     var ring = new List<HexCoordinate>(); 
     HexCoordinate tempCoordinate = coordinate; 
     int currentRingNumber = hexRings.Count; 

     // We start off by going north to the correct ring, and then adding it to our list 
     for (int i = 0; i < currentRingNumber; i++) 
     { 
      tempCoordinate = tempCoordinate.North(); 
     } 
     ring.add(tempCoordinate); 

     // After that, we proceed to go clockwise around the ring until we come back to the start 
     for (int i = 0; i < currentRingNumber; i++) 
     { 
      tempCoordinate = tempCoordinate.SouthEast(); 

      // If the ring is an odd number, you need to re-align the coordinates back to where whey should be 
      if (IntExtensions.IsOdd(i)) tempCoordinate = tempCoordinate.North(); 

      ring.add(tempCoordinate); 
     } 

     // The rightmost segment is east because we can go straight down the required number of times 
     for (int i = 0; i < currentRingNumber; i++) 
     { 
      tempCoordinate = tempCoordinate.South(); 
      ring.add(tempCoordinate); 
     } 

     // We utilize Current Ring - 1 because we only want this to run on rings that are greater than 1 
     for (int i = 0; i < currentRingNumber - 1; i++) 
     { 
      tempCoordinate = tempCoordinate.SouthWest(); 
      ring.add(tempCoordinate); 
     } 

     // Coming into this statement, we are now at the bottom 3 coordinates. 
     // Since our grid is laid out vertically, we can assume that these three hexes will be directly west of eachother 
     // So we only have to go west twice to make our way to the next north segment 
     for (int i = 0; i < 2; i++) 
     { 
      tempCoordinate = tempCoordinate.West(); 
      ring.add(tempCoordinate); 
     } 

     // We utilize Current Ring - 1 because we only want this to run on rings that are greater than 1 
     for (int i = 0; i < currentRingNumber - 1; i++) 
     { 
      tempCoordinate = tempCoordinate.NorthWest(); 
      ring.add(tempCoordinate); 
     } 

     // The left most segment is easy because we can just go straight up 
     for (int i = 0; i < currentRingNumber; i++) 
     { 
      tempCoordinate = tempCoordinate.North(); 
      ring.add(tempCoordinate); 
     } 

     // We utilize Current Ring - 1 because we only want this to run on rings that are greater than 1 
     for (int i = 0; i < currentRingNumber - 1; i++) 
     { 
      tempCoordinate = tempCoordinate.NorthEast(); 

      // If the ring is an even number, you need to re-align the coordinates back to where whey should be 
      if (IntExtensions.IsEven(i)) tempCoordinate = tempCoordinate.South(); 

      ring.add(tempCoordinate); 
     } 

     // Finally, we add the ring to our system rings and loop until we no longer fit the criteria 
     hexRings.Add(ring); 
    } 

    return hexRings; 
} 

И в случае, если это необходимо, вот мои IntExtensions

public static class IntExtensions 
{ 
    public static bool IsBetween(this int num, int low, int high) 
    { 
     return num >= low && num <= high; 
    } 

    public static bool IsOdd(this int value) 
    { 
     return value % 2 != 0; 
    } 

    public static bool IsEven(this int value) 
    { 
     return value % 2 == 0; 
    } 
} 

Моя текущая проблема заключается в том, что этот алгоритм работает для t он 1-го и 2-го колец, но как только он доберется до третьего кольца (и, предположительно, дальше, если я запустил его за 3), координаты внизу и углы начнут смещаться на 1 ... как видно из вывода в моем консоль ниже (с тем, что он должен быть отредактирован вручную вручную)

Ring 0 - System 5, 5 

Ring 1 - System 5, 4 
Ring 1 - System 6, 5 
Ring 1 - System 6, 6 
Ring 1 - System 5, 6 
Ring 1 - System 4, 6 
Ring 1 - System 4, 5 

Ring 2 - System 5, 3 
Ring 2 - System 6, 4 
Ring 2 - System 7, 4 
Ring 2 - System 7, 5 
Ring 2 - System 7, 6 
Ring 2 - System 6, 7 
Ring 2 - System 5, 7 
Ring 2 - System 4, 7 
Ring 2 - System 3, 6 
Ring 2 - System 3, 5 
Ring 2 - System 3, 4 
Ring 2 - System 4, 4 

Ring 3 - System 5, 2 
Ring 3 - System 6, 3 
Ring 3 - System 7, 3 
Ring 3 - System 8, 4 
Ring 3 - System 8, 5 
Ring 3 - System 8, 6 
Ring 3 - System 8, 7 
Ring 3 - System 7, 8 //(Should be 7, 7) 
Ring 3 - System 6, 9 //(Should be 6, 8) 
Ring 3 - System 5, 9 //(Should be 5, 8) 
Ring 3 - System 4, 9 //(Should be 4, 8) 
Ring 3 - System 3, 8 //(Should be 3, 7) 
Ring 3 - System 2, 7 
Ring 3 - System 2, 6 
Ring 3 - System 2, 5 
Ring 3 - System 2, 4 
Ring 3 - System 3, 4 //(Should be 3, 3) 
Ring 3 - System 4, 3 

ли кто-нибудь сможет помочь поставить меня в правильном направлении, или предоставить мне алгоритм, который позволил бы мне получить кольцо проклятий? Я лично застрял в этой проблеме около полутора лет, и я, похоже, не могу понять этого.

+1

Первое, что приходит мне на ум: Почему бы не использовать другую схему нумерации для сетки? Двойное разрешение. Шести в первом столбце начинаются с 0 с шагом 2, шестнадцатеричные столбцы второго столбца начинаются с 1 с шагом 2. Следовательно, ваши гексы расположены на обычной квадратной сетке с двойным разрешением. Затем вы можете использовать алгоритм круга Брешенхэм, чтобы быстро решить, пересечен ли шестнадцатеричный круг с кругом радиуса 2 *. – BitTickler

+0

My F # port of Wikipedia bresenham C обычно не выглядит хорошо :) Но из моих текущих исследований я склонен думать, что описанный выше подход в принципе возможен. Здесь ваше ожидаемое ring3 приводит к моему «двойному разрешению». Вместо этого не отображали шестиугольники, кроме кругов;) http://imgur.com/kZtWw8S – BitTickler

+0

вам необходимо учесть ** LSB ** координаты 'x' и' y' и соответствующим образом скорректировать позицию.Вместо этого вам не нужны круги, кроме шестиугольников, поэтому Bresenham непригодным для использования. Вместо этого вы ищете все гексы с расстоянием 1,2,3,4 ... ячейки от источника, поэтому «A *» было бы лучше. Посмотрите на [Улучшение производительности обнаружения щелчка на изометрической сетке с шахматным столбцом] (http://stackoverflow.com/a/35917976/2521214), особенно 'cell2scr' и' scr2cell', что я подразумеваю под LSB 'x, y' (это квадратная сетка, но проблема одна и та же). btw nice sketch – Spektre

ответ

0

Хорошо, поэтому я думаю, что, возможно, придумал решение моей проблемы. Я проверил его до 4 колец, и он дал мне все правильные гексы в соответствующих кольцах.

public List<List<HexCoordinate>> GetsRingsSurroundingHex(HexCoordinate coordinate, int maxRings) 
    { 
     // idea reference: http://gamedev.stackexchange.com/questions/51264/get-ring-of-tiles-in-hexagon-grid 
     // int ring = 1 
     // Travel around the ring by traversing N,SE,S,SW,NW,N,NE multiplied by the ring number 
     // ring++ 
     //  Travel Around ring again 
     //  cont until desired ring... 

     var hexRings = new List<List<HexCoordinate>>(); 

     // Add in the current hex to the list 
     var currentHex = new List<HexCoordinate>(); 
     currentHex.Add(coordinate); 
     hexRings.Add(currentHex); 

     // Now go through and add the other rings 
     while (hexRings.Count <= maxRings) 
     { 
      var ring = new List<HexCoordinate>(); 
      HexCoordinate tempCoordinate = coordinate; 
      int currentRingNumber = hexRings.Count; 

      // We start off by going north to the correct ring, and then adding it to our list 
      for (int i = 0; i < currentRingNumber; i++) 
      { 
       tempCoordinate = tempCoordinate.North(); 
      } 
      ring.Add(tempCoordinate); 

      // After that, we proceed to go clockwise around the ring until we come back to the start 
      for (int i = 0; i < currentRingNumber; i++) 
      { 
       tempCoordinate = tempCoordinate.SouthEast(); 

       // If the ring is an odd number, you need to re-align the coordinates back to where whey should be 
       if (IntExtensions.IsOdd(i)) tempCoordinate = tempCoordinate.North(); 

       ring.Add(tempCoordinate); 
      } 

      // The rightmost segment is east because we can go straight down the required number of times 
      for (int i = 0; i < currentRingNumber; i++) 
      { 
       tempCoordinate = tempCoordinate.South(); 
       ring.Add(tempCoordinate); 
      } 

      // We utilize Current Ring - 1 because we only want this to run on rings that are greater than 1 
      for (int i = 0; i < currentRingNumber - 1; i++) 
      { 
       if (currentRingNumber.IsEven()) 
       { 
        if (i.IsEven()) 
         tempCoordinate = tempCoordinate.SouthWest(); 
        else 
         tempCoordinate = tempCoordinate.West(); 
       } 
       else 
       { 
        if (i.IsEven()) 
         tempCoordinate = tempCoordinate.West(); 
        else 
         tempCoordinate = tempCoordinate.SouthWest(); 
       } 

       ring.Add(tempCoordinate); 
      } 

      // Coming into this statement, we are now at the bottom 3 coordinates. 
      // Since our grid is laid out vertically, we can assume that these three hexes will be directly west of each other 
      // So we only have to go west twice to make our way to the next north segment 
      for (int i = 0; i < 2; i++) 
      { 
       tempCoordinate = tempCoordinate.West(); 
       ring.Add(tempCoordinate); 
      } 

      // We utilize Current Ring - 1 because we only want this to run on rings that are greater than 1 
      for (int i = 0; i < currentRingNumber - 1; i++) 
      { 
       if (i.IsEven()) 
        tempCoordinate = tempCoordinate.NorthWest(); 
       else 
        tempCoordinate = tempCoordinate.West(); 

       ring.Add(tempCoordinate); 
      } 

      // The left most segment is easy because we can just go straight up 
      for (int i = 0; i < currentRingNumber; i++) 
      { 
       tempCoordinate = tempCoordinate.North(); 
       ring.Add(tempCoordinate); 
      } 

      // We utilize Current Ring - 1 because we only want this to run on rings that are greater than 1 
      for (int i = 0; i < currentRingNumber - 1; i++) 
      { 
       if (currentRingNumber.IsEven()) 
       { 
        if (i.IsEven()) 
         tempCoordinate = tempCoordinate.East(); 
        else 
         tempCoordinate = tempCoordinate.NorthEast(); 
       } 
       else 
       { 
        if (i.IsEven()) 
         tempCoordinate = tempCoordinate.NorthEast(); 
        else 
         tempCoordinate = tempCoordinate.East(); 
       } 

       ring.Add(tempCoordinate); 
      } 

      // Finally, we add the ring to our system rings and loop until we no longer fit the criteria 
      hexRings.Add(ring); 
     } 

     return hexRings; 
    }