Como rotacionar uma estrutura de ladrilhos hexagonais em uma grade hexagonal?


10

Meu jogo isométrico 2D usa um mapa de grade hexagonal. Em referência à imagem abaixo, como faço para girar as estruturas de hexágono azul claro em 60 graus ao redor dos hexágonos rosa?

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

EDITAR:

O hex principal é (0,0). Outros hexágonos são filhos, o número deles é fixo. Vou definir apenas uma posição (neste caso, à direita) e calcular outras direções, se necessário (canto inferior esquerdo, botão direito, canto superior direito, canto superior esquerdo e esquerdo). Outros hexes são definidos como: Package.Add (-1,0), Package.Add (-2,0) e assim por diante.

insira a descrição da imagem aqui

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

Neste código Numberé o hex principal e Pointé o hex que eu quero girar, mas não funciona:

insira a descrição da imagem aqui


1
qual é exatamente o problema? como implementar isso ou alguns resultados ruins?
— Ali1S232 26/07

Você está encaixando rotações nas 6 arestas do hexágono rosa ou os ângulos de rotação são arbitrários? Além disso, quais dos hexágonos rosa na estrutura do lado direito você está girando?
— 31511 Keeblebrox

Pode ser mais fácil girar os blocos individuais, mas isso leva à questão do que acontece com os blocos que já estão lá, e seria bom saber isso em geral antes que eu possa tentar dar uma resposta.
— James

Desculpa pelo engano. Estou falando da parte esquerda da imagem. Eu tive resultados ruins, sempre que alguns azaramentos estão em lugares errados. O hexágono rosa é principal e hexágonos azuis brilhantes são childs. Suponha que hex principal seja (5,5), então eu defino um hex filho (-1,0) para que o filho fique no lado esquerdo do rosa e assim por diante. Eu quero saber como girar esse hexágono infantil em 60 graus (então ele estará no canto superior esquerdo do rosa). mais fácil: estou trabalhando no sistema de compilação no meu jogo de estratégia. Geralmente, em jogos de estratégia, você pode girar o edifício antes de colocá-lo. Vou calcular os hexágonos que precisam ser construídos.
— Ruzsoo

O conjunto de hexágonos selecionados precisa ser exatamente a mesma contagem todas as vezes? Ou seja, você está, por exemplo, colocando especificamente 3 objetos nos hexágonos de cada lado do hexágono rosa? Ou você está apenas querendo desenhar uma linha de um determinado comprimento e decidir quais hexágonos a cruzam melhor, independentemente de quantos serão? Você quer fazer isso com um número fixo de hexágonos ou um número arbitrário?
— 21811 Tim Holt

Respostas:


11

Como observa Martin Sojka , as rotações são mais simples se você converter para um sistema de coordenadas diferente, executar a rotação e depois voltar.

Eu uso um sistema de coordenadas diferente do Martin, rotulado x,y,z. Não há oscilação neste sistema, e é útil para muitos algoritmos hexadecimais. Neste sistema, você pode girar o hexágono ao 0,0,0“girar” as coordenadas e virar seus sinais: x,y,zse transforma de -y,-z,-xum lado e -z,-x,-ydo outro. Eu tenho um diagrama nesta página .

(Sinto muito por x / y / z vs X / Y, mas eu uso x / y / z no meu site e você usa X / Y no seu código, portanto, nesta resposta o caso é importante! Então, eu vou usar xx,yy,zzcomo os nomes das variáveis ​​abaixo para tentar facilitar a distinção.)

Converta suas X,Ycoordenadas no x,y,zformato:

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

Faça uma rotação de 60 ° de uma maneira ou de outra:

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

Converta as x,y,zcostas em seu X,Y:

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

Por exemplo, se você começar com (X = -2, Y = 1) e quiser girar 60 ° para a direita, converterá:

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

depois gire -2,1,160 ° para a direita com:

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

como você vê aqui:

Exemplo de rotação hexadecimal para -2,1,1

depois converta de -1,2,-1volta:

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

Então (X = -2, Y = 1) gira 60 ° para a direita em (X = -2, Y = -1).


4

Vamos primeiro definir um novo número. Não se preocupe, é fácil.

  • f : f × f = -3

Ou, para simplificar: f = √3 × i , sendo i a unidade imaginária . Com isso, uma rotação de 60 graus no sentido horário é o mesmo que multiplicação por 1/2 × (1 - f ) e rotação de 60 graus no sentido anti-horário, o mesmo que multiplicação por 1/2 × (1 + f ) . Se isso soa estranho, lembre-se de que a multiplicação por um número complexo é igual à rotação no plano 2D. Nós apenas "esmagamos" os números complexos na direção imaginária um pouco (por √3) para não ter que lidar com raízes quadradas ... ou não inteiros, nesse caso.

Também podemos escrever o ponto (a, b) como a + b × f .

Isso nos permite rotacionar qualquer ponto do avião; por exemplo, o ponto (2,0) = 2 + 0 × f gira para (1, -1), depois para (-1, -1), (-2,0), (-1,1), ( 1,1) e, finalmente, de volta a (2,0), simplesmente multiplicando-o.

Obviamente, precisamos de uma maneira de traduzir esses pontos de nossas coordenadas para aqueles em que fazemos as rotações e depois voltar. Para isso, é necessária outra informação: se o ponto em que fazemos a rotação for para a "esquerda" ou a "direita" da linha vertical. Por uma questão de simplicidade, declaramos que tem um valor de "oscilação" w de 0 se estiver à esquerda (como o centro da rotação [0,0] em suas duas figuras inferiores) e de 1 se estiver à direita disso. Isso amplia nossos pontos originais para serem tridimensionais; ( x , y , w ), com "w" sendo 0 ou 1 após a normalização. A função de normalização é:

NORM: ( x , y , w ) -> ( x + piso ( w / 2), y , w mod 2), com a operação "mod" definida de forma que retorne apenas valores positivos ou zero.

Nosso algoritmo agora tem a seguinte aparência:

  1. Transforme nossos pontos ( a , b , c ) em suas posições em relação ao centro rotacional ( x , y , w ) calculando ( a - x , b - y , c - w ) e normalizando o resultado. Isso coloca o centro rotacional em (0,0,0) obviamente.

  2. Transforme nossos pontos de suas coordenadas "nativas" para as coordenadas do complexo rotacional: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Gire nossos pontos multiplicando-os por um dos números de rotação acima, conforme necessário.

  4. Transformar Ra de volta dos pontos das coordenadas rotacionais para os "nativos": ( r , s ) -> (piso ( r / 2), s , r mod 2), com "mod" definido como acima.

  5. Volte a transformar os pontos na posição original, adicionando-os ao centro de rotação ( x , y , z ) e normalizando.


Uma versão simples de nossos números "triplex" baseados em f em C ++ seria assim:

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));
        }
};
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.