Как я могу реализовать сборку гексагональной мозаики в XNA?

13

У меня есть мозаичная карта с шестиугольником, на которой мне нужно проверить, нажимается ли шестиугольник. Шестиугольники на самом деле не соприкасаются, скорее между ними есть небольшой зазор.

Кто-нибудь знает, как я могу проверить, щелкает ли шестиугольник, не усложняя все это?

Joel
источник

Ответы:

13

Посмотрите на эту картину

гексагональное разложение

Как вы можете видеть, существует относительно интуитивно понятный способ сопоставления прямоугольной системы координат x, y с шестиугольной.

Мы можем говорить о «прямоугольных» неправильных шестиугольниках, то есть шестиугольниках, вписанных в эллипсы, или шестиугольниках, полученных из правильных шестиугольников, масштабированных в обоих направлениях непропорционально (без вращений-сдвигов).

Прямоугольный шестиугольник может быть определен высотой и шириной описывающего прямоугольника плюс ширина вписывающего прямоугольника. (Ш, ш, ч)

вписывающие / ограничивающие прямоугольники

Самый простой способ узнать гексагональный индекс - это разделить пространство следующим образом:

космическая перегородка

Ширина прямоугольника равна w + (W - w) / 2 = (w + W) / 2, его высота h / 2; ширина зеленого прямоугольника равна (Ww) / 2. Легко узнать, в каком прямоугольнике находится точка:

введите описание изображения здесь

u и v - координаты напоминания, указывающие, где находится точка в прямоугольнике i, j: используя w, мы можем сказать, находимся ли мы в зеленой зоне (u <(Ww) / 2) или нет.

если это так, мы находимся в зеленой зоне, нам нужно знать, находимся ли мы в верхней или нижней половине шестиугольника: мы в верхней половине, если i и j оба четные или оба нечетные; в противном случае мы находимся в нижней половине.

В обоих случаях полезно преобразовывать u и v, чтобы они варьировались от 0 до 1:

введите описание изображения здесь

если мы находимся в нижней половине и V <U

или

если мы находимся в верхней половине и (1-V)> U

тогда мы уменьшаем я на один

Теперь нам просто нужно уменьшить j на единицу, если i нечетно, чтобы увидеть, что i - это горизонтальный шестиугольный индекс (столбец), а целая часть j / 2 - вертикальный шестиугольный индекс (строка).

введите описание изображения здесь

FXIII
источник
Благодарность! Действительно полезно, было несколько проблем, возникающих с расщеплением зеленой части (должен ли я вычесть 1 из себя или нет), но я думаю, что это было связано с ориентацией моих шестиугольников. Все работает, спасибо!
Джоэл
14

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

Вот параметры одного шестиугольника. Его центр O, наибольшая ширина 2a, высота 2bи длина верхнего края 2c.

         Y ^
           |
       ____|____
      /  b |   |\
     /     |   | \
    /      |   |  \
---(-------+---+---)------>
    \     O|   c  / a      X
     \     |     /
      \____|____/
           |

Это расположение строк / столбцов с началом координат в центре нижнего левого шестиугольника. Если ваша установка отличается, переведите ваши (x,y)координаты, чтобы использовать этот случай, или используйте -yвместо, yнапример:

col 0
 | col 1
 |   | col 2
 |   |  |
 __  | __    __    __    __   
/  \__/  \__/  \__/  \__/  \__
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/_ _ line 2
\__/  \__/  \__/  \__/  \__/  \ _ _ _ line 1
/ .\__/  \__/  \__/  \__/  \__/_ _ line 0
\__/  \__/  \__/  \__/  \__/

Следующий код даст вам строку и столбец шестиугольника, содержащего точку (x,y):

static void GetHex(float x, float y, out int row, out int column)
{
  // Find out which major row and column we are on:
  row = (int)(y / b);
  column = (int)(x / (a + c));

  // Compute the offset into these row and column:
  float dy = y - (float)row * b;
  float dx = x - (float)column * (a + c);

  // Are we on the left of the hexagon edge, or on the right?
  if (((row ^ column) & 1) == 0)
      dy = b - dy;
  int right = dy * (a - c) < b * (dx - c) ? 1 : 0;

  // Now we have all the information we need, just fine-tune row and column.
  row += (column ^ row ^ right) & 1;
  column += right;
}

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

Сэм Хоцевар
источник
Впервые я услышал об ideOne, но это кажется очень полезным!
Дэвид Гувейя
@davidluzgouveia: да, это круто. Я не люблю веб или облачные сервисы, но этот полезен.
Сэм Хоцевар
1

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

jgallant
источник
1

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

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

DampeS8N
источник
1

Я уже отвечал на аналогичный вопрос с одинаковыми целями в отношении переполнения стека. Я опубликую его здесь для удобства: (NB - весь код написан и протестирован на Java)

Шестиугольная сетка с наложенной квадратной сеткой

Это изображение показывает верхний левый угол гексагональной сетки и наложена синяя квадратная сетка. Легко определить, какой из квадратов находится внутри, и это дало бы грубое приближение к какому шестиугольнику тоже. Белые части шестиугольников показывают, где квадрат и шестиугольная сетка имеют одинаковые координаты, а серые части шестиугольников показывают, где их нет.

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

private final Hexagon getSelectedHexagon(int x, int y)
{
    // Find the row and column of the box that the point falls in.
    int row = (int) (y / gridHeight);
    int column;

    boolean rowIsOdd = row % 2 == 1;

    // Is the row an odd number?
    if (rowIsOdd)// Yes: Offset x to match the indent of the row
        column = (int) ((x - halfWidth) / gridWidth);
    else// No: Calculate normally
        column = (int) (x / gridWidth);

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

    // Work out the position of the point relative to the box it is in
    double relY = y - (row * gridHeight);
    double relX;

    if (rowIsOdd)
        relX = (x - (column * gridWidth)) - halfWidth;
    else
        relX = x - (column * gridWidth);

Наличие относительных координат облегчает следующий шаг.

Общее уравнение для прямой

Как и на изображении выше, если у нашей точки > mx + c, мы знаем, что наша точка лежит над линией, а в нашем случае - шестиугольником выше и слева от текущей строки и столбца. Обратите внимание, что система координат в java имеет y, начинающийся с 0 в верхнем левом углу экрана, а не нижний левый, как обычно в математике, следовательно, отрицательный градиент, используемый для левого края, и положительный градиент, используемый для правого.

    // Work out if the point is above either of the hexagon's top edges
    if (relY < (-m * relX) + c) // LEFT edge
        {
            row--;
            if (!rowIsOdd)
                column--;
        }
    else if (relY < (m * relX) - c) // RIGHT edge
        {
            row--;
            if (rowIsOdd)
                column++;
        }

    return hexagons[column][row];
}

Краткое объяснение переменных, использованных в приведенном выше примере:

введите описание изображения здесь введите описание изображения здесь

m - градиент, поэтому m = c / halfWidth


Дополнение Неошамама к вышесказанному

Это дополнение к ответу Себастьяна Троя. Я бы оставил это как комментарий, но мне пока не хватает репутации.

Если вы хотите реализовать осевую систему координат, как описано здесь: http://www.redblobgames.com/grids/hexagons/

Вы можете внести небольшие изменения в код.

Вместо того

// Is the row an odd number?
if (rowIsOdd)// Yes: Offset x to match the indent of the row
    column = (int) ((x - halfWidth) / gridWidth);
else// No: Calculate normally
    column = (int) (x / gridWidth);

использовать этот

float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way

Это приведет к тому, что координата (0, 2) будет находиться в том же диагональном столбце, что и (0, 0) и (0, 1), вместо того, чтобы находиться непосредственно под (0, 0).

Troyseph
источник
Я понимаю, что это не учитывает промежутки между шестиугольниками, но это должно помочь значительно сузить вашу проблему.
Тройсеф
0

Если все ваши шестиугольники сделаны с одинаковыми пропорциями и размещением, вы можете использовать какой-либо актив оверлея для столкновений, что-то вроде: Накладка на столкновение для шестиугольника

Затем все, что вам нужно сделать, это поместить изображение столкновения в то место, где находится ваш шестиугольник, получить положение мыши относительно левого угла и посмотреть, НЕ является ли пиксель относительного положения НЕ белым (что означает столкновение).

Код (не проверен):

bool IsMouseTouchingHexagon(Vector2 mousePosition, Vector2 hexagonPosition,
    Rectangle hexagonRectangle, Texture2D hexagonImage)
{
    Vector2 mousePositionToTopLeft = mousePosition - hexagonPosition;

    // We make sure that the mouse is over the hexagon's rectangle.
    if (mousePositionToTopLeft.X >= 0 && mousePositionToTopLeft.X < hexagonRectangle.Width && 
        mousePositionToTopLeft.Y >= 0 && mousePositionToTopLeft.Y < hexagonRectangle.Height)
    {
        // Where "PixelColorAt" returns the color of a pixel of an image at a certain position.
        if (PixelColorAt(hexagonImage, mousePositionToTopLeft) == Color.White)
        {
            // If the color is not white, we are colliding with the hexagon
            return true;
        }
    }

    // if we get here, it means that we did not find a collision.
    return false;
}

Очевидно, вы могли бы выполнить проверку столкновения прямоугольника заранее (всего вашего изображения шестиугольника), чтобы улучшить производительность всего процесса.

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

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

Джесси Эмонд
источник
Со своим способом вы могли бы использовать любой набор неправильных форм и дать им все наложение с уникальным цветом, затем сопоставить цвета с объектом, который они накладывают, так что вы выбираете цвет из буфера наложения и затем используете свою карту, чтобы получить объект под мышкой. Не то чтобы я особенно одобрял эту технику, звучит немного слишком сложно и попытка преждевременной оптимизации ИМХО.
Тройсеф
0

Там есть статья об игровом программировании Gems 7 называется Для Пчелы и Gamers: Как обращаться с шестиугольной , который будет именно то , что вам нужно.

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

Дэвид Гувея
источник