Ответы:
Посмотрите на эту картину
Как вы можете видеть, существует относительно интуитивно понятный способ сопоставления прямоугольной системы координат 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 - вертикальный шестиугольный индекс (строка).
Обычные шестиугольники имеют шесть осей симметрии, но я предполагаю, что ваши шестиугольники имеют только две оси симметрии ( т.е. все углы не являются точно 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 .
Вам, вероятно, не нужно отменять регистрацию кликов между плитками. То есть, это не повредит и может даже помочь игроку, если вы позволите промежуткам между плитками также быть активными, если вы не говорите о большом промежутке между ними, который заполнен чем-то, что по логике не должно быть нажатым. (Скажем, гексы - это города на большой карте, где между ними находятся другие способные к щелчку вещи, такие как люди)
Чтобы сделать вышеупомянутое, вы можете просто нанести на карту центры всех гексов, а затем найти ближайший к мыши при нажатии на плоскость всех гексов. Ближайший центр на плоскости мозаичных шестиугольников всегда будет таким же, над которым вы зависаете.
Я уже отвечал на аналогичный вопрос с одинаковыми целями в отношении переполнения стека. Я опубликую его здесь для удобства: (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).
Если все ваши шестиугольники сделаны с одинаковыми пропорциями и размещением, вы можете использовать какой-либо актив оверлея для столкновений, что-то вроде:
Затем все, что вам нужно сделать, это поместить изображение столкновения в то место, где находится ваш шестиугольник, получить положение мыши относительно левого угла и посмотреть, НЕ является ли пиксель относительного положения НЕ белым (что означает столкновение).
Код (не проверен):
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;
}
Очевидно, вы могли бы выполнить проверку столкновения прямоугольника заранее (всего вашего изображения шестиугольника), чтобы улучшить производительность всего процесса.
Концепция довольно проста для понимания и реализации, но работает, только если ваши шестиугольники одинаковы. Это также может сработать, если у вас есть только набор возможных размеров шестиугольника, что будет означать, что вам потребуется более одного наложения на столкновение.
Если вы обнаружите, что это очень упрощенное решение того, что может быть намного более полным и многократно используемым (с использованием математики, чтобы действительно найти столкновение), но, безусловно, стоит попробовать, по моему мнению.
Там есть статья об игровом программировании Gems 7 называется Для Пчелы и Gamers: Как обращаться с шестиугольной , который будет именно то , что вам нужно.
К сожалению, в данный момент у меня нет своей копии книги, иначе я мог бы описать ее немного.