Jak obrócić strukturę sześciokątnych płytek na siatce sześciokątnej?

10

Moja izometryczna gra 2D wykorzystuje sześciokątną mapę siatki. W odniesieniu do poniższego obrazu, w jaki sposób mogę obrócić jasnoniebieskie struktury sześciokątne o 60 stopni wokół różowych sześciokątów?

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

EDYTOWAĆ:

Główny hex to (0,0). Inne heksy są dziećmi, ich liczba jest ustalona. Zdefiniuję tylko jedną pozycję (w tym przypadku jej prawą) i w razie potrzeby obliczę inne kierunki (lewy dolny, prawy botom, prawy górny, lewy górny i lewy). Inne heksy są zdefiniowane jak: Package.Add (-1,0), Package.Add (-2,0) i tak dalej.

wprowadź opis zdjęcia tutaj

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

W tym kodzie Numberjest heks główny i Pointto heks, który chcę obrócić, ale nie działa:

wprowadź opis zdjęcia tutaj

ruzsoo
źródło
1
na czym dokładnie polega problem? jak wdrożyć to lub jakieś złe wyniki?
Ali1S232,
Czy przyciągasz obroty do 6 krawędzi różowego sześciokąta, czy też kąty obrotu są dowolne? Ponadto, który z różowych sześciokątów w strukturze po prawej stronie się obraca?
Keeblebrox
Obracanie pojedynczych kafelków może być po prostu łatwiejsze, ale prowadzi to do pytania, co dzieje się z kafelkami, które już tam są, i dobrze byłoby wiedzieć w ogóle, zanim spróbuję udzielić odpowiedzi.
James
Przepraszam za pomyłkę. Mówię o lewej części obrazu. Miałem złe wyniki, zawsze niektóre heksy znajdują się w niewłaściwych miejscach. Różowy heks jest główny, a jasnoniebieskie heksy są dziećmi. Załóżmy, że główny hex to (5,5), a następnie zdefiniuję hex dziecka (-1,0), aby dziecko znajdowało się po lewej stronie różu i tak dalej. Chcę wiedzieć, jak obrócić to dziecko hex o 60 stopni (wtedy będzie na lewym różu). łatwiej: pracuję nad systemem kompilacji w mojej grze strategicznej. Często w grach strategicznych możesz obrócić budynek przed jego umieszczeniem. Obliczę heksy, które trzeba zbudować.
ruzsoo
Czy zestaw wybranych heksów za każdym razem musi być dokładnie taki sam? Czy na przykład umieszczasz 3 obiekty na heksach po obu stronach różowego heksa? A może po prostu chcesz narysować linię o określonej długości i zdecydować, które heksy najlepiej ją przecinają, niezależnie od tego, ile to będzie? Czy chcesz to zrobić ze stałą liczbą heksów, czy dowolną liczbą?
Tim Holt

Odpowiedzi:

11

Jak zauważa Martin Sojka , obroty są prostsze, jeśli przekonwertujesz na inny układ współrzędnych, wykonasz obrót, a następnie przekonwertujesz z powrotem.

Używam innego układu współrzędnych niż Martin, oznaczonego etykietą x,y,z. W tym systemie nie ma wahań i jest przydatny w przypadku wielu algorytmów szesnastkowych. W tym systemie możesz obracać hex za 0,0,0pomocą „obracania” współrzędnych i odwracania ich znaków: x,y,zzmienia się w -y,-z,-xjedną stronę i-z,-x,-y drugą stronę. Mam schemat na tej stronie .

(Przykro mi z powodu x / y / z vs X / Y, ale używam x / y / z na mojej stronie i używasz X / Y w kodzie, więc w tej odpowiedzi sprawa ma znaczenie! Więc zamierzam użyć xx,yy,zz jako nazwy zmiennych poniżej, aby ułatwić ich rozróżnienie).

Konwertuj swoje X,Ywspółrzędne na x,y,zformat:

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

Wykonaj obrót o 60 ° w jedną lub drugą stronę:

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

Konwertuj z x,y,zpowrotem na X,Y:

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

Na przykład, jeśli zaczniesz od (X = -2, Y = 1) i chcesz obrócić o 60 ° w prawo, przekonwertujesz:

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

następnie obróć o -2,1,160 ° w prawo za pomocą:

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

jak widzisz tutaj:

Przykład rotacji heksadecymalnej dla -2,1,1

następnie przekonwertuj z -1,2,-1powrotem:

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

Więc (X = -2, Y = 1) obraca się o 60 ° w prawo w (X = -2, Y = -1).

amitp
źródło
4

Najpierw zdefiniujmy nowy numer. Nie martw się, to łatwe.

  • f : f × f = -3

Lub, mówiąc prosto: f = √3 × i , przy czym i jest jednostką urojoną . Dzięki temu obrót o 60 stopni w kierunku zgodnym z ruchem wskazówek zegara jest taki sam jak pomnożenie przez 1/2 × (1 - f ) , a obrót o 60 stopni w kierunku przeciwnym do ruchu wskazówek zegara jest taki sam jak pomnożenie przez 1/2 × (1 + f ) . Jeśli brzmi to dziwnie, pamiętaj, że mnożenie przez liczbę zespoloną jest takie samo jak obrót w płaszczyźnie 2D. Po prostu „wyciskamy” liczby zespolone w wyobrażonym kierunku nieco (o √3), aby nie mieć do czynienia z pierwiastkami kwadratowymi ... ani z liczbami całkowitymi.

Możemy również zapisać punkt (a, b) jako a + b × f .

To pozwala nam obrócić dowolny punkt na płaszczyźnie; na przykład punkt (2,0) = 2 + 0 × f zmienia się na (1, -1), a następnie na (-1, -1), (-2,0), (-1,1), ( 1,1) i wreszcie wróć do (2,0), po prostu mnożąc go.

Oczywiście potrzebujemy sposobu na przeniesienie tych punktów ze współrzędnych na te, w których wykonujemy obrót, a następnie z powrotem. W tym celu potrzebna jest jeszcze jedna informacja: jeśli punkt, który wykonujemy, obraca się wokół „lewej” lub „prawej” linii pionowej. Dla uproszczenia deklarujemy, że ma on wartość „wahania” w 0, jeśli znajduje się po jego lewej stronie (jak środek obrotu [0,0] na dwóch dolnych obrazach), i 1, jeśli znajduje się po prawej stronie z tego. To rozszerza nasze pierwotne punkty na trójwymiarowe; ( x , y , w ), przy czym „w” wynosi 0 lub 1 po normalizacji. Funkcja normalizacji to:

NORM: ( x , y , w ) -> ( x + floor ( w / 2), y , w mod 2), przy zdefiniowanej operacji „mod” tak, że zwraca tylko wartości dodatnie lub zero.

Nasz algorytm wygląda teraz następująco:

  1. Przekształć nasze punkty ( a , b , c ) w ich pozycje względem środka obrotu ( x , y , w ), obliczając ( a - x , b - y , c - w ), a następnie normalizując wynik. To oczywiście ustawia środek obrotu na (0,0,0).

  2. Przekształć nasze punkty z ich „rodzimych” współrzędnych na obrotowe zespoły złożone: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Obróć nasze punkty, mnożąc je w razie potrzeby przez jedną z powyższych liczb obrotowych.

  4. Ra przekształca punkty z powrotem ze współrzędnych obrotowych na ich „rodzime”: ( r , s ) -> (floor ( r / 2), s , r mod 2), z „mod” zdefiniowanym jak powyżej.

  5. Ponownie przekształć punkty z powrotem do ich pierwotnej pozycji, dodając je do środka obrotu ( x , y , z ) i normalizując.


Prosta wersja z naszych numerów „potrójnych” oparty f w C ++ będzie wyglądać następująco:

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
źródło