Respostas:
Dê uma olhada nesta foto
Como você pode ver, existe uma maneira relativamente intuitiva de mapear o sistema de coordenadas retangulares x, y para o hexagonal.
Podemos falar sobre hexágonos irregulares "retos", ou seja, hexágonos inscritos em elipses ou hexágonos obtidos de hexágonos regulares escalados desproporcionalmente em ambas as direções (sem rotações-cisalhamento).
Um hexágono reto pode ser definido pela altura e largura do retângulo circunscrito mais a largura do retângulo de inscrição. (W, W, H)
A maneira mais fácil de descobrir o índice hexagonal é particionar o espaço da seguinte maneira:
A largura do retângulo é w + (W - w) / 2 = (w + W) / 2, sua altura é h / 2; a largura do retângulo verde é (Ww) / 2. É fácil descobrir em que ponto do retângulo o ponto está:
u e v são as coordenadas do lembrete que indicam onde o ponto está dentro do retângulo i, j: usando w, podemos dizer se estamos na área verde (u <(Ww) / 2) ou não.
se for o caso de estarmos na área verde, precisamos saber se estamos na metade superior ou inferior do hexágono: estamos na metade superior se i e j forem pares ou ímpares; estamos na metade inferior caso contrário.
Nos dois casos, é útil transformar u e v para que eles variem entre 0 e 1:
se estamos na metade inferior e v <u
ou
se estamos na metade superior e (1-v)> u
então decrementamos i por um
Agora, simplesmente precisamos decrementar j por um, se i for estranho ver que i é o índice hexagonal horizontal (coluna) e a parte inteira de j / 2 é o índice hexagonal vertical (linha)
Hexágonos regulares têm seis eixos de simetria, mas assumirei que seus hexágonos têm apenas dois eixos de simetria ( ou seja, todos os ângulos não são exatamente de 60 graus). Não necessariamente porque a sua não tem a simetria completa, mas porque pode ser útil para outra pessoa.
Aqui estão os parâmetros de um hexágono. Seu centro está O
, a maior largura 2a
, a altura 2b
e o comprimento da borda superior 2c
.
Y ^
|
____|____
/ b | |\
/ | | \
/ | | \
---(-------+---+---)------>
\ O| c / a X
\ | /
\____|____/
|
Esse é o layout de linha / coluna, com a origem no centro do hexágono inferior esquerdo. Se sua configuração for diferente, traduza suas (x,y)
coordenadas para recorrer a este caso ou use em -y
vez de, y
por exemplo:
col 0
| col 1
| | col 2
| | |
__ | __ __ __ __
/ \__/ \__/ \__/ \__/ \__
\__/ \__/ \__/ \__/ \__/ \
/ \__/ \__/ \__/ \__/ \__/
\__/ \__/ \__/ \__/ \__/ \
/ \__/ \__/ \__/ \__/ \__/_ _ line 2
\__/ \__/ \__/ \__/ \__/ \ _ _ _ line 1
/ .\__/ \__/ \__/ \__/ \__/_ _ line 0
\__/ \__/ \__/ \__/ \__/
O código a seguir fornecerá a linha e a coluna do ponto que contém o hexágono (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;
}
Você pode verificar se o código acima desenha hexágonos perfeitos nesta execução do IdeOne .
Você provavelmente não precisa cancelar o registro de cliques entre os blocos. Ou seja, não vai doer e pode até ajudar o jogador se você permitir que os espaços entre os blocos sejam clicáveis também, a menos que você esteja falando de um grande espaço entre eles preenchido com algo que logicamente não deveria ser clicado. (Digamos, os hexágonos são cidades em um mapa grande, onde estão outras coisas que podem ser clicadas, como pessoas)
Para fazer o acima, você pode simplesmente traçar os centros de todos os hexágonos e, em seguida, encontrar o mais próximo ao mouse quando clicar no plano de todos os hexágonos. O centro mais próximo em um plano de hexágonos em mosaico será sempre o mesmo sobre o qual você está passando o mouse.
Já respondi a uma pergunta semelhante, com objetivos idênticos, no Stack Overflow . Vou repassá-la aqui por conveniência: (NB - todo o código é escrito e testado em Java)
Esta imagem mostra o canto superior esquerdo de uma grade hexagonal e sobreposta é uma grade quadrada azul. É fácil encontrar em quais quadrados um ponto está dentro e isso daria uma aproximação aproximada de qual hexágono também. As partes brancas dos hexágonos mostram onde a grade quadrada e hexagonal compartilham as mesmas coordenadas e as partes cinza dos hexágonos mostram onde não.
A solução agora é tão simples quanto encontrar em qual caixa um ponto está, verificar se o ponto está em um dos triângulos e corrigir a resposta, se necessário.
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);
Nesse ponto, temos a linha e a coluna da caixa em que está o ponto; em seguida, precisamos testar nosso ponto contra as duas arestas superiores do hexágono para ver se nosso ponto está em um dos hexágonos acima:
// 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);
Ter coordenadas relativas facilita o próximo passo.
Como na imagem acima, se o y do nosso ponto é > mx + c , sabemos que o ponto está acima da linha e, no nosso caso, o hexágono acima e à esquerda da linha e coluna atuais. Observe que o sistema de coordenadas em java tem y começando em 0 no canto superior esquerdo da tela e não no canto inferior esquerdo como é habitual em matemática, portanto, o gradiente negativo usado para a borda esquerda e o gradiente positivo usado para a direita.
// 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];
}
Uma explicação rápida das variáveis usadas no exemplo acima:
m é o gradiente, então m = c / halfWidth
Este é um adendo à resposta de SebastianTroy. Gostaria de deixar como comentário, mas ainda não tenho reputação suficiente.
Se você deseja implementar um sistema de coordenadas axiais, conforme descrito aqui: http://www.redblobgames.com/grids/hexagons/
Você pode fazer uma ligeira modificação no código.
Ao invés de
// 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);
usa isto
float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way
Isso fará com que a coordenada (0, 2) esteja na mesma coluna diagonal que (0, 0) e (0, 1), em vez de ficar diretamente abaixo (0, 0).
Se todos os seus hexágonos forem feitos com as mesmas proporções e colocação, você poderá usar algum tipo de recurso de sobreposição para as colisões, algo como:
Então, tudo o que você precisa fazer é colocar a imagem de colisão onde está o hexágono, obter a posição do mouse em relação ao canto esquerdo e ver se o pixel da posição relativa NÃO é branco (o que significa que há uma colisão).
Código (não testado):
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;
}
Obviamente, você poderia executar uma verificação de colisão de retângulo com antecedência (de toda a imagem hexagonal) para melhorar o desempenho de todo o processo.
O conceito é bastante simples de entender e implementar, mas só funciona se os hexágonos forem todos iguais. Também poderia funcionar se você tivesse apenas um conjunto de dimensões possíveis do hexágono, o que significaria que seria necessário mais de uma sobreposição de colisão.
Se achar que é uma solução muito simplista para o que poderia ser muito mais completo e reutilizável (usando a matemática para realmente encontrar a colisão), mas definitivamente vale a pena tentar na minha opinião.
Há um artigo sobre Gemas de programação de jogos 7, intitulado Para abelhas e jogadores: Como lidar com ladrilhos hexagonais, que seria exatamente o que você precisa.
Infelizmente, não tenho minha cópia do livro comigo no momento, caso contrário, eu poderia ter descrito um pouco.