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.