¿Cómo giro una estructura de mosaicos hexagonales en una cuadrícula hexagonal?

10

Mi juego isométrico 2D utiliza un mapa de cuadrícula hexagonal. En referencia a la imagen a continuación, ¿cómo giro las estructuras hexagonales azul claro en 60 grados alrededor de los hexágonos rosados?

http://www.algonet.se/~afb/spriteworld/ongoing/HexMap.jpg

EDITAR:

El hexágono principal es (0,0). Otros hexes son hijos, el conteo de ellos es fijo. Voy a definir solo una posición (en este caso es la derecha) y calcular otras direcciones si es necesario (izquierda-abajo, derecha-abajo, derecha-arriba, izquierda-arriba e izquierda). Otros hexes se definen como: Package.Add (-1,0), Package.Add (-2,0) y así sucesivamente.

ingrese la descripción de la imagen aquí

switch(Direction)
{
case DirRightDown:
    if(Number.Y % 2 && Point.X % 2)
        Number.X += 1;
    Number.Y += Point.X + Point.Y / 2;

    Number.X += Point.X / 2 - Point.Y / 1.5;
    break;
}

En este código Numberes el hexágono principal y Pointes el hexágono que quiero rotar, pero no funciona:

ingrese la descripción de la imagen aquí

ruzsoo
fuente
1
¿Cuál es el problema exactamente? ¿Cómo implementar eso o algunos malos resultados?
Ali1S232
¿Está ajustando rotaciones a los 6 bordes del hexágono rosa, o los ángulos de rotación son arbitrarios? Además, ¿cuál de los hexágonos rosados ​​en la estructura del lado derecho está rotando?
Keeblebrox
Puede que sea más fácil rotar las fichas individuales, pero esto lleva a la pregunta de qué sucede con las fichas que ya están allí y esto sería bueno saber en general antes de que pueda intentar dar una respuesta.
James
Perdón por el error. Estoy hablando de la parte izquierda de la imagen. Tuve malos resultados, siempre algunos hexes están en lugares equivocados. El hexágono rosa es principal y los hexágonos azules brillantes son niños. Supongamos que el hexágono principal es (5,5), luego defino un hexágono hijo (-1,0) para que el hijo esté en el lado izquierdo de rosa y así sucesivamente. Quiero saber cómo rotar este hexágono infantil en 60 grados (luego estará en la parte superior izquierda de rosa). más fácil: estoy trabajando en el sistema de construcción en mi juego de estrategia. A menudo, en los juegos de estrategia puedes rotar el edificio antes de colocarlo. Voy a calcular los hexes que necesitan construir.
ruzsoo
¿El conjunto de hexes seleccionados tiene que ser exactamente el mismo recuento cada vez? Es decir, ¿está, por ejemplo, colocando específicamente 3 objetos en los hexágonos a cada lado del hexágono rosa? ¿O simplemente quieres dibujar una línea de una longitud determinada y decidir qué hexágonos se cruzan mejor, independientemente de cuántos sean? ¿Desea hacer esto con un número fijo de hexes o un número arbitrario?
Tim Holt

Respuestas:

11

Como señala Martin Sojka , las rotaciones son más simples si convierte a un sistema de coordenadas diferente, realiza la rotación y luego vuelve a convertir.

Yo uso un sistema de coordenadas diferente al de Martin, etiquetado x,y,z . No hay bamboleo en este sistema, y ​​es útil para muchos algoritmos hexadecimales. En este sistema, puede rotar el hexágono 0,0,0"rotando" las coordenadas y volteando sus signos: se x,y,zconvierte en -y,-z,-xun sentido y-z,-x,-y otra. Tengo un diagrama en esta página .

(Lamento lo de x / y / z vs X / Y, pero uso x / y / z en mi sitio y tú usas X / Y en tu código, ¡así que en esta respuesta el caso es importante! Así que voy a usar xx,yy,zzcomo los nombres de variables a continuación para intentar que sea más fácil distinguir).

Convierta sus X,Ycoordenadas al x,y,zformato:

xx = X - (Y - Y&1) / 2
zz = Y
yy = -xx - zz

Realice una rotación de 60 ° de una forma u otra:

xx, yy, zz = -zz, -xx, -yy
     # OR
xx, yy, zz = -yy, -zz, -xx

Convertir el x,y,z espalda a su X,Y:

X = xx + (zz - zz&1) / 2
Y = zz

Por ejemplo, si comienza con (X = -2, Y = 1) y desea girar 60 ° a la derecha, convertiría:

xx = -2 - (1 - 1&1) / 2 = -2
zz = 1
yy = 2-1 = 1

luego rotar -2,1,1 60 ° a la derecha con:

xx, yy, zz = -zz, -xx, -yy = -1, 2, -1

como ves aquí:

Ejemplo de rotación hexagonal para -2,1,1

luego convertir -1,2,-1 nuevo:

X = -1 + (-1 - -1&1) / 2 = -2
Y = -1

Entonces (X = -2, Y = 1) gira 60 ° a la derecha en (X = -2, Y = -1).

amitp
fuente
4

Primero definamos un nuevo número. No te preocupes, es fácil.

  • f : f × f = -3

O, para decirlo simplemente: f = √3 × i , siendo i la unidad imaginaria . Con esto, una rotación de 60 grados en sentido horario es lo mismo que la multiplicación por 1/2 × (1 - f ) , y la rotación de 60 grados en sentido antihorario es lo mismo que la multiplicación por 1/2 × (1 + f ) . Si esto suena extraño, recuerde que la multiplicación por un número complejo es lo mismo que la rotación en el plano 2D. Simplemente "aplastamos" un poco los números complejos en la dirección imaginaria (por √3) para no tener que lidiar con raíces cuadradas ... o no enteros, para el caso.

También podemos escribir el punto (a, b) como a + b × f .

Esto nos permite rotar cualquier punto del plano; por ejemplo, el punto (2,0) = 2 + 0 × f gira a (1, -1), luego a (-1, -1), (-2,0), (-1,1), ( 1,1) y finalmente volver a (2,0), simplemente multiplicándolo.

Por supuesto, necesitamos una forma de traducir esos puntos de nuestras coordenadas a aquellos en los que hacemos las rotaciones, y luego nuevamente. Para esto, se necesita otra información: si el punto en el que hacemos la rotación es hacia la "izquierda" o la "derecha" de la línea vertical. Para simplificar, declaramos que tiene un valor de "oscilación" w de 0 si está a la izquierda (como el centro de la rotación [0,0] en las dos imágenes inferiores), y de 1 si está a la derecha de eso. Esto extiende nuestros puntos originales para ser tridimensionales; ( x , y , w ), con "w" siendo 0 o 1 después de la normalización. La función de normalización es:

NORM: ( x , y , w ) -> ( x + piso ( w / 2), y , w mod 2), con la operación "mod" definida de modo que solo devuelva valores positivos o cero.

Nuestro algoritmo ahora se ve de la siguiente manera:

  1. Transforme nuestros puntos ( a , b , c ) en sus posiciones relativas al centro de rotación ( x , y , w ) calculando ( a - x , b - y , c - w ), luego normalizando el resultado. Esto pone el centro de rotación en (0,0,0) obviamente.

  2. Transforme nuestros puntos de sus coordenadas "nativas" a las complejas rotacionales: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Gire nuestros puntos multiplicándolos con uno de los números de rotación anteriores, según sea necesario.

  4. Ra-transforma los puntos de vuelta desde las coordenadas de rotación a sus "nativas": ( r , s ) -> (floor ( r / 2), s , r mod 2), con "mod" definido como arriba.

  5. Vuelva a transformar los puntos a su posición original agregándolos al centro de rotación ( x , y , z ) y normalizando.


Una versión simple de nuestros números "triplex" basada en f en C ++ se vería así:

class hex {
    public:
        int x;
        int y;
        int w; /* "wobble"; for any given map, y+w is either odd or
                  even for ALL hexes of that map */
    hex(int x, int y, int w) : x(x), y(y), w(w) {}
    /* rest of the implementation */
};

class triplex {
    public:
        int r; /* real part */
        int s; /* f-imaginary part */
        triplex(int new_r, int new_s) : r(new_r), s(new_s) {}
        triplex(const hex &hexfield)
        {
            r = hexfield.x * 2 + hexfield.w;
            s = hexfield.y;
        }
        triplex(const triplex &other)
        {
            this->r = other.r; this->s = other.s;
        }
    private:
        /* C++ has crazy integer division and mod semantics. */
        int _div(int a, unsigned int b)
        {
            int res = a / b;
            if( a < 0 && a % b != 0 ) { res -= 1; }
            return res;
        }
        int _mod(int a, unsigned int b)
        {
            int res = a % b;
            if( res < 0 ) { res += a; }
            return res;
        }
    public:
        /*
         * Self-assignment operator; simple enough
         */
        triplex & operator=(const triplex &rhs)
        {
            this->r = rhs.r; this->s = rhs.s;
            return *this;
        }
        /*
         * Multiplication operators - our main workhorse
         * Watch out for overflows
         */
        triplex & operator*=(const triplex &rhs)
        {
            /*
             * (this->r + this->s * f) * (rhs.r + rhs.s * f)
             * = this->r * rhs.r + (this->r * rhs.s + this->s * rhs.r ) * f
             *   + this->s * rhs.s * f * f
             *
             * ... remembering that f * f = -3 ...
             *
             * = (this->r * rhs.r - 3 * this->s * rhs.s)
             *   + (this->r * rhs.s + this->s * rhs.r) * f
             */
            int new_r = this->r * rhs.r - 3 * this->s * rhs.s;
            int new_s = this->r * rhs.s + this->s * rhs.r;
            this->r = new_r; this->s = new_s;
            return *this;
        }
        const triplex operator*(const triplex &other)
        {
            return triplex(*this) *= other;
        }
        /*
         * Now for the rotations ...
         */
        triplex rotate60CW() /* rotate this by 60 degrees clockwise */
        {
            /*
             * The rotation is the same as multiplikation with (1,-1)
             * followed by halving all values (multiplication by (1/2, 0).
             * If the values come from transformation from a hex field,
             * they will always land back on the hex field; else
             * we might lose some information due to the last step.
             */
            (*this) *= triplex(1, -1);
            this->r /= 2;
            this->s /= 2;
        }
        triplex rotate60CCW() /* Same, counter-clockwise */
        {
            (*this) *= triplex(1, 1);
            this->r /= 2;
            this->s /= 2;
        }
        /*
         * Finally, we'd like to get a hex back (actually, I'd
         * typically create this as a constructor of the hex class)
         */
        operator hex()
        {
            return hex(_div(this->r, 2), this->s, _mod(this->r, 2));
        }
};
Martin Sojka
fuente