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

ruzsoo
fonte
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).

amitp
fonte
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));
        }
};
Martin Sojka
fonte