Відповіді:
Погляньте на цю картину
Як ви бачите, існує відносно інтуїтивно зрозумілий спосіб відображення x, y прямокутної системи координат до шестикутної.
Ми можемо говорити про «прямі» неправильні шестикутники, тобто шестикутники, вписані в еліпси, або шестикутники, отримані із звичайних шестикутників, масштабованих в обох напрямках непропорційно (відсутність обертання-зсуву).
Прямий шестикутник можна визначити по висоті та ширині обписуючого прямокутника плюс ширині вписаного. (Ш, ш, год)
Найпростіший спосіб з’ясувати шестикутний індекс - розділити простір наступним чином:
Ширина прямокутника - w + (W - w) / 2 = (w + W) / 2, його висота h / 2; ширина зеленого прямокутника дорівнює (Вт) / 2. Неважко дізнатися, куди в який прямокутник падає точка:
u і v - координати нагадування, які вказують, де точка знаходиться в прямокутнику i, j: За допомогою w ми можемо сказати, знаходимося ми в зеленій зоні (u <(Ww) / 2) чи ні.
якщо це так, що ми знаходимося в зеленій зоні, нам потрібно знати, чи знаходимося ми у верхній чи нижній половині шестикутника: ми знаходимося у верхній половині, якщо i і j є парними або обома непарними; ми в нижній половині інакше.
В обох випадках корисно трансформувати u і v, тому вони змінюються між 0 і 1:
якщо ми знаходимося в нижній половині і v <u
або
якщо ми знаходимося у верхній половині і (1-v)> u
то ми декремент i по одному
Тепер нам просто треба декрементувати j на один, якщо мені не дивно бачити, що 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 .
Можливо, вам не потрібно скасовувати кліки між плитками. Тобто, це не зашкодить і навіть може допомогти гравцеві, якщо ви дозволите пробіл між плитками, а також якщо не будете говорити про великий простір між ними, який заповнений чимось, що логічно не повинно натискати. (Скажімо, шестикутники - це міста на великій карті, де між ними знаходяться інші речі, доступні для натискання, наприклад, люди)
Для цього можна просто намалювати центри всіх шестигранників, а потім знайти найближчий до миші при натисканні на площину всіх шестикутників. Найближчий центр на площині шестикутників із тесельованої форми завжди буде тим самим, на якому ви навісаєте.
Я вже відповів на подібне запитання, з однаковими цілями, над Stack Overflow я перекладу його тут для зручності: (Примітка - весь код записаний і перевірений на 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 / половина ширини
Це доповнення до відповіді Себастьяна Троя. Я б залишив це як коментар, але мені ще недостатньо репутації.
Якщо ви хочете реалізувати осьову систему координат, як описано тут: 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 під назвою Для бджіл та геймерів: як поводитися з шестикутними плитками, які були б саме такими, що вам потрібні.
На жаль, наразі у мене немає примірника книги, інакше я міг би її трохи описати.