Come posso ruotare una struttura di tessere esagonali su una griglia esagonale?


10

Il mio gioco isometrico 2D utilizza una mappa a griglia esagonale. In riferimento all'immagine seguente, come posso ruotare le strutture di esagono azzurro di 60 gradi attorno agli esagoni rosa?

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

MODIFICARE:

L'esagono principale è (0,0). Altri esagoni sono bambini, il conteggio è fisso. Definirò solo una posizione (in questo caso la sua destra) e calcolerò altre direzioni se necessario (in basso a sinistra, in basso a destra, in alto a destra, in alto a sinistra e a sinistra). Altri esagoni sono definiti come: Package.Add (-1,0), Package.Add (-2,0) e così via.

inserisci qui la descrizione dell'immagine

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

In questo codice Numberè l'esagono principale ed Pointè l'esagono che voglio ruotare, ma non funziona:

inserisci qui la descrizione dell'immagine


1
qual è esattamente il problema? come implementare questo o alcuni cattivi risultati?
Ali1S232,

Stai facendo scattare delle rotazioni verso i 6 bordi dell'esagono rosa o gli angoli di rotazione sono arbitrari? Inoltre, quale degli esagoni rosa nella struttura del lato destro stai ruotando?
Keeblebrox,

Potrebbe essere più semplice ruotare le singole tessere, ma questo porta alla domanda su cosa succede alle tessere che sono già lì e questo sarebbe utile sapere in generale prima che io possa provare a dare una risposta.
James,

Scusa per lo sbaglio. Sto parlando della parte sinistra dell'immagine. Ho avuto cattivi risultati, mai alcuni esagoni sono in posti sbagliati. L'esagono rosa è principale e gli esagoni blu brillante sono bambini. Supponiamo che l'esagono principale sia (5,5), quindi definisco un esagono figlio (-1,0) in modo che il bambino si trovi sul lato sinistro del rosa e così via. Voglio sapere come ruotare questo esagono bambino di 60 gradi (quindi sarà nella parte superiore sinistra del rosa). più semplice: sto lavorando su un sistema di build nel mio gioco di strategia. Spesso nei giochi di strategia è possibile ruotare l'edificio prima di posizionarlo. Ho intenzione di calcolare gli esagoni che devono essere costruiti.
ruzsoo,

L'insieme degli esagoni selezionati deve essere sempre lo stesso conteggio ogni volta? Cioè, ad esempio stai posizionando specificamente 3 oggetti sugli esagoni su entrambi i lati dell'esagono rosa? Oppure vuoi solo disegnare una linea di una determinata lunghezza e decidere quali esagoni la intersecano meglio, indipendentemente da quanti saranno? Vuoi farlo con un numero fisso di esagoni o un numero arbitrario?
Tim Holt,

Risposte:


11

Come osserva Martin Sojka , le rotazioni sono più semplici se si converte in un sistema di coordinate diverso, si esegue la rotazione, quindi si converte indietro.

Uso un sistema di coordinate diverso rispetto a Martin, etichettato x,y,z. Non c'è oscillazione in questo sistema ed è utile per molti algoritmi esadecimali. In questo sistema è possibile ruotare l'esagono 0,0,0ruotando le coordinate e capovolgendole: x,y,zsi trasforma in -y,-z,-xun modo e -z,-x,-ynell'altro. Ho un diagramma in questa pagina .

(Mi dispiace per x / y / z vs X / Y ma uso x / y / z sul mio sito e tu usi X / Y nel tuo codice, quindi in questa risposta il caso è importante! Quindi userò xx,yy,zzcome i nomi delle variabili sottostanti per cercare di semplificare la distinzione.)

Converti le tue X,Ycoordinate nel x,y,zformato:

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

Eseguire una rotazione di 60 ° in un modo o nell'altro:

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

Converti la x,y,zschiena in X,Y:

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

Ad esempio, se inizi con (X = -2, Y = 1) e vuoi ruotare di 60 ° a destra, convertiresti:

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

quindi ruotare di -2,1,160 ° a destra con:

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

come vedi qui:

Esempio di rotazione esadecimale per -2,1,1

quindi riconvertire -1,2,-1:

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

Quindi (X = -2, Y = 1) ruota di 60 ° a destra in (X = -2, Y = -1).


4

Definiamo prima un nuovo numero. Non preoccuparti, è facile.

  • f : f × f = -3

Oppure, per dirla semplicemente: f = √3 × i , con i che sono l' unità immaginaria . In questo modo, una rotazione di 60 gradi in senso orario equivale alla moltiplicazione di 1/2 × (1 - f ) e una rotazione di 60 gradi in senso antiorario uguale alla moltiplicazione di 1/2 × (1 + f ) . Se questo suona strano, ricorda che la moltiplicazione per un numero complesso è la stessa della rotazione sul piano 2D. Dobbiamo semplicemente "schiacciare" un po 'i numeri complessi nella direzione immaginaria (di √3) per non avere a che fare con radici quadrate ... o non numeri interi, del resto.

Possiamo anche scrivere il punto (a, b) come a + b × f .

Questo ci consente di ruotare qualsiasi punto nel piano; ad esempio, il punto (2,0) = 2 + 0 × f ruota su (1, -1), quindi su (-1, -1), (-2,0), (-1,1), ( 1,1) e infine di nuovo a (2,0), semplicemente moltiplicandolo.

Naturalmente, abbiamo bisogno di un modo per tradurre quei punti dalle nostre coordinate in quelli in cui facciamo le rotazioni, e poi di nuovo indietro. Per questo, sono necessarie altre informazioni: se il punto in cui facciamo la rotazione è alla "sinistra" o alla "destra" della linea verticale. Per semplicità, dichiariamo che ha un valore di "oscillazione" w di 0 se è a sinistra di esso (come il centro della rotazione [0,0] nelle tue due immagini in basso) e di 1 se è a destra di esso. Questo estende i nostri punti originali come tridimensionali; ( x , y , w ), con "w" che è 0 o 1 dopo la normalizzazione. La funzione di normalizzazione è:

NORM: ( x , y , w ) -> ( x + floor ( w / 2), y , w mod 2), con l'operazione "mod" definita in modo tale da restituire solo valori positivi o zero.

Il nostro algoritmo ora ha il seguente aspetto:

  1. Trasforma i nostri punti ( a , b , c ) nelle loro posizioni rispetto al centro di rotazione ( x , y , w ) calcolando ( a - x , b - y , c - w ), quindi normalizzando il risultato. Ciò pone ovviamente il centro di rotazione a (0,0,0).

  2. Trasforma i nostri punti dalle loro coordinate "native" a quelle complesse rotazionali: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Ruota i nostri punti moltiplicandoli con uno dei numeri di rotazione sopra, secondo necessità.

  4. Trasforma i punti indietro dalle coordinate di rotazione a quelle "native": ( r , s ) -> (piano ( r / 2), s , r mod 2), con "mod" definito come sopra.

  5. Trasforma i punti nella loro posizione originale aggiungendoli al centro di rotazione ( x , y , z ) e normalizzando.


Una semplice versione dei nostri numeri "triplex" basati su f in C ++ sarebbe simile a questa:

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));
        }
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.