Як я можу реалізовувати шестикутний вибір плитки в XNA?


13

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

Хтось знає, як я міг би перейти до перевірки того, чи натискають шестикутник, не надто ускладнюючи все?

Відповіді:


13

Погляньте на цю картину

шестикутне розкладання

Як ви бачите, існує відносно інтуїтивно зрозумілий спосіб відображення 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 - індекс вертикального шестикутника (рядок)

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


Спасибі! Дійсно корисно, було кілька проблем, що виникали з розщепленням зеленого перерізу (слід відняти 1 від i чи ні), але я думаю, що це було пов'язано з орієнтацією моїх шестикутників. Всі працюємо, дякую!
Джоель

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: так, це приголомшливо. Я не захоплююсь веб-або хмарними сервісами, але ця корисна.
sam hocevar

1

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


1

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

Для цього можна просто намалювати центри всіх шестигранників, а потім знайти найближчий до миші при натисканні на площину всіх шестикутників. Найближчий центр на площині шестикутників із тесельованої форми завжди буде тим самим, на якому ви навісаєте.


1

Я вже відповів на подібне запитання, з однаковими цілями, над 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 / половина ширини


Додаток NeoShamam до вищезазначеного

Це доповнення до відповіді Себастьяна Троя. Я б залишив це як коментар, але мені ще недостатньо репутації.

Якщо ви хочете реалізувати осьову систему координат, як описано тут: 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).


Я розумію, що це не враховує прогалини між шестикутниками, але це повинно значно зменшити вашу проблему.
Тройсеф

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

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

Концепція досить проста для розуміння та реалізації, але працює лише в тому випадку, якщо ваші шестикутники однакові. Це також може спрацювати, якщо у вас є лише набір можливих розмірів шестикутника, що означатиме, що вам знадобиться більше, ніж одна накладка зіткнення.

Якщо ви вважаєте це дуже спрощеним рішенням того, що може бути набагато більш повним і багаторазовим (використовуючи математику, щоб дійсно знайти зіткнення), але, на мою думку, це, безумовно, варто спробувати.


Зі свого боку ви можете використовувати будь-який набір неправильних фігур і надати їм усе накладку унікальним кольором, а потім зіставити кольори на об'єкт, на який вони накладаються, щоб ви вибрали колір із буфера накладення, а потім використали свою карту, щоб отримати об'єкт під мишкою. Не те, що я особливо схвалюю цю техніку, звучить трохи надмірно складно і спроба передчасної оптимізації IMHO.
Тройсеф

0

Там є стаття про Ігрове програмування Gems 7 під назвою Для бджіл та геймерів: як поводитися з шестикутними плитками, які були б саме такими, що вам потрібні.

На жаль, наразі у мене немає примірника книги, інакше я міг би її трохи описати.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.