Odpowiedzi:
Spójrz na to zdjęcie
Jak widać, istnieje stosunkowo intuicyjny sposób odwzorowania prostokątnego układu współrzędnych x, y na sześciokątny.
Możemy mówić o „prostokątnych” nieregularnych sześciokątach, tj. Sześciokątach wpisanych w elipsy lub sześciokątach uzyskanych z regularnych sześciokątów skalowanych nieproporcjonalnie w obu kierunkach (bez ścinania-rotacji).
Prostokątny sześciokąt można zdefiniować na podstawie wysokości i szerokości prostokąta opisującego oraz szerokości prostokąta opisującego. (W, w, h)
Najłatwiejszym sposobem na znalezienie indeksu heksagonalnego jest podzielenie przestrzeni w następujący sposób:
Prostokąt ma szerokość w + (W - w) / 2 = (w + W) / 2, jego wysokość wynosi h / 2; szerokość zielonego prostokąta wynosi (Ww) / 2. Łatwo dowiedzieć się, w którym prostokącie znajduje się punkt:
u i v są współrzędnymi przypomnienia, które wskazują, gdzie punkt znajduje się w prostokącie i, j: Używając w, możemy powiedzieć, czy jesteśmy w zielonym obszarze (u <(Ww) / 2), czy nie.
jeśli tak jest, jesteśmy w zielonym obszarze, musimy wiedzieć, czy znajdujemy się w górnej czy dolnej połowie sześciokąta: jesteśmy w górnej połowie, jeśli i i j są parzyste lub oba nieparzyste; w przeciwnym razie jesteśmy w dolnej połowie.
W obu przypadkach przydatne jest przekształcenie u i v, aby różniły się między 0 a 1:
jeśli jesteśmy w dolnej połowie i v <u
lub
jeśli jesteśmy w górnej połowie i (1-v)> u
następnie zmniejszamy o jeden
Teraz po prostu musimy zmniejszyć j o jeden, jeśli i jest nieparzyste, aby zobaczyć, że i jest poziomym indeksem sześciokątnym (kolumna), a liczba całkowita j / 2 jest pionowym indeksem sześciokątnym (wiersz)
Zwykłe sześciokąty mają sześć osi symetrii, ale założę, że twoje sześciokąty mają tylko dwie osie symetrii ( tzn. Wszystkie kąty nie są dokładnie 60 stopni). Niekoniecznie dlatego, że twoja nie ma pełnej symetrii, ale ponieważ może być przydatna dla kogoś innego.
Oto parametry jednego sześciokąta. Jego środek jest O
, największa szerokość 2a
, wysokość 2b
i długość górnej krawędzi wynosi 2c
.
Y ^
|
____|____
/ b | |\
/ | | \
/ | | \
---(-------+---+---)------>
\ O| c / a X
\ | /
\____|____/
|
Jest to układ wierszy / kolumn, którego początek znajduje się na środku lewego dolnego sześciokąta. Jeśli konfiguracja jest inna, przetłumacz (x,y)
współrzędne, aby oprzeć się na tym przypadku, lub użyj -y
zamiast, y
na przykład:
col 0
| col 1
| | col 2
| | |
__ | __ __ __ __
/ \__/ \__/ \__/ \__/ \__
\__/ \__/ \__/ \__/ \__/ \
/ \__/ \__/ \__/ \__/ \__/
\__/ \__/ \__/ \__/ \__/ \
/ \__/ \__/ \__/ \__/ \__/_ _ line 2
\__/ \__/ \__/ \__/ \__/ \ _ _ _ line 1
/ .\__/ \__/ \__/ \__/ \__/_ _ line 0
\__/ \__/ \__/ \__/ \__/
Poniższy kod da ci wiersz i kolumnę sześciokąta zawierającego punkt (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;
}
Możesz sprawdzić, czy powyższy kod rysuje idealne sześciokąty w tym przebiegu IdeOne .
Prawdopodobnie nie musisz wyrejestrowywać kliknięć między kafelkami. Oznacza to, że nie zaszkodzi, a może nawet pomóc graczowi, jeśli pozwolisz na klikanie spacji między kafelkami, chyba że mówisz o dużej przestrzeni między nimi wypełnionej czymś, co logicznie nie powinno zostać klikniętym. (Powiedzmy, że heksy to miasta na dużej mapie, na których między nimi znajdują się inne klikalne rzeczy, takie jak ludzie)
Aby to zrobić, możesz po prostu wykreślić środki wszystkich heksów, a następnie znaleźć najbliższy kursor myszy po kliknięciu na płaszczyźnie wszystkich heksów. Najbliższe centrum na płaszczyźnie mozaik sześciokąta będzie zawsze tym samym, nad którym unosisz się.
Odpowiedziałem już na podobne pytanie, z identycznymi celami, nad przepełnieniem stosu. Ponownie opublikuję je tutaj dla pewności: (Uwaga - cały kod jest napisany i przetestowany w Javie)
Ten obraz pokazuje lewy górny róg sześciokątnej siatki, a nałożony jest niebieski kwadrat. Łatwo jest ustalić, który kwadrat jest w środku, a to dałoby przybliżone przybliżenie, który sześciokąt też. Białe części sześciokątów pokazują, gdzie kwadratowa i sześciokątna siatka mają te same współrzędne, a szare części sześciokątów pokazują, gdzie nie.
Rozwiązanie jest teraz tak proste, jak znalezienie pola, w którym znajduje się punkt, a następnie sprawdzenie, czy punkt znajduje się w którymś z trójkątów, i poprawienie odpowiedzi w razie potrzeby.
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);
W tym momencie mamy wiersz i kolumnę pola, w którym znajduje się nasz punkt, następnie musimy przetestować nasz punkt na dwóch górnych krawędziach sześciokąta, aby zobaczyć, czy nasz punkt leży w którymś z sześciokątów powyżej:
// 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);
Posiadanie współrzędnych względnych ułatwia następny krok.
Podobnie jak na powyższym obrazku, jeśli y naszego punktu wynosi > mx + c , wiemy, że nasz punkt leży powyżej linii, aw naszym przypadku sześciokąt powyżej i na lewo od bieżącego wiersza i kolumny. Zauważ, że układ współrzędnych w java ma y od 0 w lewym górnym rogu ekranu, a nie w lewym dolnym rogu, jak to zwykle jest w matematyce, stąd gradient ujemny użyty dla lewej krawędzi i gradient dodatni użyty dla prawej.
// 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];
}
Szybkie wyjaśnienie zmiennych użytych w powyższym przykładzie:
m jest gradientem, więc m = c / halfWidth
To jest dodatek do odpowiedzi SebastianTroy. Zostawiłbym to jako komentarz, ale nie mam jeszcze wystarczającej reputacji.
Jeśli chcesz wdrożyć osiowy układ współrzędnych, jak opisano tutaj: http://www.redblobgames.com/grids/hexagons/
Możesz wprowadzić niewielką modyfikację kodu.
Zamiast
// 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);
Użyj tego
float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way
Spowoduje to, że współrzędne (0, 2) będą znajdować się w tej samej ukośnej kolumnie, co (0, 0) i (0, 1), zamiast znajdować się bezpośrednio poniżej (0, 0).
Jeśli wszystkie twoje sześciokąty są wykonane przy użyciu tych samych proporcji i rozmieszczenia, możesz użyć jakiegoś zasobu nakładki dla kolizji, coś w stylu:
Następnie wystarczy umieścić obraz kolizji w miejscu, w którym znajduje się sześciokąt, uzyskać pozycję myszy względem lewego rogu i sprawdzić, czy piksel pozycji względnej NIE jest biały (co oznacza kolizję).
Kod (nie testowany):
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;
}
Oczywiście można wcześniej wykonać kontrolę kolizji prostokąta (całego obrazu sześciokąta), aby poprawić wydajność całego procesu.
Koncepcja jest dość prosta do zrozumienia i wdrożenia, ale działa tylko wtedy, gdy wszystkie sześciokąty są takie same. Może to również działać, jeśli masz tylko zestaw możliwych wymiarów sześciokąta, co oznaczałoby, że potrzebujesz więcej niż jednej nakładki kolizyjnej.
Jeśli okaże się, że jest to bardzo uproszczone rozwiązanie tego, co może być o wiele bardziej kompletne i wielokrotnego użytku (używanie matematyki, aby naprawdę znaleźć kolizję), ale moim zdaniem zdecydowanie warto spróbować.
Jest artykuł na temat Gems Programming Game 7 pt. „ Pszczoły i gracze: jak obchodzić się z sześciokątnymi kafelkami”, który byłby dokładnie tym, czego potrzebujesz.
Niestety w tej chwili nie mam ze sobą egzemplarza książki, inaczej mógłbym ją trochę opisać.