¿Cómo puedo implementar la selección hexagonal de mapas de mosaico en XNA?


13

Tengo un mapa de mosaico hexagonal en el que debo verificar cuándo se hace clic en un hexágono. Los hexágonos no se tocan realmente, sino que tienen un pequeño espacio entre cada uno de ellos.

¿Alguien sabe cómo podría verificar si se hace clic en un hexágono sin complicarlo demasiado?

Respuestas:


13

Echa un vistazo a esta foto

descomposición hexagonal

Como puede ver, hay una forma relativamente intuitiva de mapear el sistema de coordenadas rectangulares x, y al hexagonal.

Podemos hablar de hexágonos irregulares "rect", es decir, hexágonos inscritos en elipses o hexágonos obtenidos de hexágonos regulares escalados en ambas direcciones de manera desproporcionada (sin rotaciones-cizallas).

Un hexágono rectángulo puede definirse por la altura y el ancho del rectángulo circunscriptor más el ancho del rectificador. (W, w, h)

inscribir / circunscribir rectángulos

La forma más fácil de averiguar el índice hexagonal es dividir el espacio de la siguiente manera:

partición espacial

El ancho del rectángulo es w + (W - w) / 2 = (w + W) / 2, su altura es h / 2; El ancho del rectángulo verde es (Ww) / 2. Es fácil averiguar en qué rectángulo cae el punto:

ingrese la descripción de la imagen aquí

u y v son las coordenadas de recordatorio que indican dónde está el punto dentro del rectángulo i, j: Usando w podemos decir si estamos en el área verde (u <(Ww) / 2) o no.

si es el caso de que estamos en el área verde, necesitamos saber si estamos en la mitad superior o inferior del hexágono: estamos en la mitad superior si i y j son ambos pares o impares; estamos en la mitad inferior de lo contrario.

En ambos casos es útil transformar u y v para que varíen entre 0 y 1:

ingrese la descripción de la imagen aquí

si estamos en la mitad inferior y v <u

o

si estamos en la mitad superior y (1-v)> u

entonces decrementamos i por uno

Ahora simplemente tenemos que disminuir j por uno si i es extraño para ver que i es el índice del hexágono horizontal (columna) y la parte entera de j / 2 es el índice del hexágono vertical (fila)

ingrese la descripción de la imagen aquí


¡Gracias! Realmente útil, tuve algunos problemas con la división de la sección verde (debería restar 1 de i o no), pero creo que eso se debió a la orientación de mis hexágonos. Todo funcionando, gracias!
Joel

14

Los hexágonos regulares tienen seis ejes de simetría, pero supongo que sus hexágonos solo tienen dos ejes de simetría ( es decir, todos los ángulos no son exactamente 60 grados). No necesariamente porque el tuyo no tiene la simetría completa, sino porque puede ser útil para otra persona.

Aquí están los parámetros de un hexágono. Su centro está adentro O, el ancho más grande es 2a, la altura es 2by la longitud del borde superior es 2c.

         Y ^
           |
       ____|____
      /  b |   |\
     /     |   | \
    /      |   |  \
---(-------+---+---)------>
    \     O|   c  / a      X
     \     |     /
      \____|____/
           |

Este es el diseño de fila / columna, con el origen en el centro del hexágono inferior izquierdo. Si su configuración es diferente, traduzca sus (x,y)coordenadas para recurrir a este caso, o use en -ylugar de, ypor ejemplo:

col 0
 | col 1
 |   | col 2
 |   |  |
 __  | __    __    __    __   
/  \__/  \__/  \__/  \__/  \__
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/
\__/  \__/  \__/  \__/  \__/  \
/  \__/  \__/  \__/  \__/  \__/_ _ line 2
\__/  \__/  \__/  \__/  \__/  \ _ _ _ line 1
/ .\__/  \__/  \__/  \__/  \__/_ _ line 0
\__/  \__/  \__/  \__/  \__/

El siguiente código le dará la fila y la columna del punto que contiene el 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;
}

Puede comprobar que el código anterior dibuja hexágonos perfectos en esta ejecución de IdeOne .


La primera vez que escuché sobre ideOne, ¡pero parece realmente útil!
David Gouveia

@davidluzgouveia: sí, es increíble. No soy aficionado a los servicios web o en la nube, pero este es útil.
sam hocevar

1

Podrías colocar 3 rectángulos rotados dentro del área del hexágono, y si se hace correctamente, llenaría el área exactamente. Entonces sería simplemente una cuestión de verificar la colisión en los tres rectángulos.


1

Probablemente no necesite cancelar el registro de clics entre los mosaicos. Es decir, no dolerá e incluso podría ayudar al jugador si permite que se haga clic en los espacios entre las fichas a menos que esté hablando de un gran espacio entre ellos que está lleno de algo que lógicamente no debería hacer clic (Digamos, los hexes son ciudades en un mapa grande donde entre ellos hay otras cosas que se pueden hacer clic como personas)

Para hacer lo anterior, simplemente puede trazar los centros de todos los hexes, y luego encontrar el más cercano al mouse cuando se hace clic en el plano de todos los hexes. El centro más cercano en un plano de hexágonos teselados siempre será el mismo sobre el que se desplaza.


1

Ya he respondido una pregunta similar, con objetivos idénticos, en Stack Overflow , la volveré a publicar aquí para mayor comodidad: (Nota: todo el código está escrito y probado en Java)

Rejilla hexagonal con una rejilla cuadrada superpuesta

Esta imagen muestra la esquina superior izquierda de una cuadrícula hexagonal y superpuesta es una cuadrícula cuadrada azul. Es fácil encontrar en cuál de los cuadrados hay un punto dentro y esto también daría una aproximación aproximada de cada hexágono. Las porciones blancas de los hexágonos muestran dónde el cuadrado y la cuadrícula hexagonal comparten las mismas coordenadas y las porciones grises de los hexágonos muestran dónde no.

La solución ahora es tan simple como encontrar en qué cuadro se encuentra un punto, luego verificar si el punto está en cualquiera de los triángulos y corregir la respuesta si es necesario.

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

En este punto, tenemos la fila y la columna de la caja en la que se encuentra nuestro punto, luego debemos probar nuestro punto contra los dos bordes superiores del hexágono para ver si nuestro punto se encuentra en cualquiera de los hexágonos anteriores:

    // 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);

Tener coordenadas relativas facilita el siguiente paso.

Ecuación genérica para una línea recta.

Al igual que en la imagen de arriba, si la y de nuestro punto es > mx + c , sabemos que nuestro punto se encuentra por encima de la línea y, en nuestro caso, el hexágono arriba ya la izquierda de la fila y columna actuales. Tenga en cuenta que el sistema de coordenadas en Java tiene y comenzando en 0 en la parte superior izquierda de la pantalla y no en la parte inferior izquierda, como es habitual en matemáticas, de ahí el gradiente negativo utilizado para el borde izquierdo y el gradiente positivo utilizado para la derecha.

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

Una explicación rápida de las variables utilizadas en el ejemplo anterior:

ingrese la descripción de la imagen aquí ingrese la descripción de la imagen aquí

m es el gradiente, entonces m = c / halfWidth


La adición de NeoShamam a lo anterior

Esta es una adición a la respuesta de Sebastian Troy. Lo dejaría como un comentario, pero todavía no tengo suficiente reputación.

Si desea implementar un sistema de coordenadas axiales como se describe aquí: http://www.redblobgames.com/grids/hexagons/

Puede hacer una ligera modificación al código.

En lugar 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);

utilizar esta

float columnOffset = row * halfWidth;
column = (int)(x + columnOffset)/gridWidth; //switch + to - to align the grid the other way

Esto hará que la coordenada (0, 2) esté en la misma columna diagonal que (0, 0) y (0, 1) en lugar de estar directamente debajo de (0, 0).


Me doy cuenta de que esto no tiene en cuenta las brechas entre los hexágonos, pero debería ayudar a reducir su problema considerablemente.
Troyseph

0

Si todos sus hexágonos están hechos con las mismas proporciones y ubicación, podría usar algún tipo de recurso de superposición para las colisiones, algo así como: La superposición de colisión para un hexágono

Luego, todo lo que tiene que hacer es colocar la imagen de colisión donde está su hexágono, obtener la posición del mouse con respecto a la esquina izquierda y ver si el píxel de la posición relativa NO es blanco (lo que significa que hay una colisión).

Código (no probado):

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, podría realizar una verificación de colisión de rectángulo de antemano (de toda su imagen hexagonal) para mejorar el rendimiento de todo el proceso.

El concepto es bastante simple de entender e implementar, pero solo funciona si sus hexágonos son todos iguales. También podría funcionar si solo tiene un conjunto de dimensiones hexagonales posibles, lo que significaría que necesitaría más de una superposición de colisión.

Si considera que es una solución muy simple para lo que podría ser mucho más completo y reutilizable (usando las matemáticas para encontrar realmente la colisión), pero definitivamente vale la pena intentarlo en mi opinión.


A su manera, puede usar cualquier conjunto de formas irregulares y darles a todos una superposición con un color único, luego asignar los colores al objeto que están superponiendo para que elija un color de su búfer de superposición y luego use su mapa para obtener el objeto debajo del mouse. No es que esté particularmente respaldando esta técnica, parece un poco demasiado complicado y un intento de optimización prematura en mi humilde opinión.
Troyseph

0

Hay un artículo sobre Game Programming Gems 7 llamado For Bees and Gamers: How to Handle Hexagonal Tiles que sería exactamente lo que necesita.

Desafortunadamente no tengo mi copia del libro conmigo en este momento, de lo contrario podría haberlo descrito un poco.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.