Wie drehe ich eine Struktur aus sechseckigen Kacheln auf einem sechseckigen Gitter?

10

Mein isometrisches 2D-Spiel verwendet eine hexagonale Gitterkarte. Wie drehe ich in Bezug auf das Bild unten die hellblauen Sechseckstrukturen um 60 Grad um die rosa Sechsecke?

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

BEARBEITEN:

Haupthex ist (0,0). Andere Felder sind Kinder, deren Anzahl festgelegt ist. Ich werde nur eine Position definieren (in diesem Fall die rechte) und bei Bedarf andere Richtungen berechnen (links unten, rechts unten, rechts oben, links oben und links). Andere Felder sind wie folgt definiert: Package.Add (-1,0), Package.Add (-2,0) und so weiter.

Geben Sie hier die Bildbeschreibung ein

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 diesem Code Numberbefindet sich das Haupthex und Pointdas Hex, das ich drehen möchte, aber es funktioniert nicht:

Geben Sie hier die Bildbeschreibung ein

Ruzsoo
quelle
1
Was genau ist das Problem? Wie kann man das oder einige schlechte Ergebnisse umsetzen?
Ali1S232
Schnappen Sie Rotationen an den 6 Kanten des rosa Sechsecks oder sind die Rotationswinkel beliebig? Um welches der rosa Sechsecke in der rechten Struktur drehen Sie sich?
Keeblebrox
Es mag einfacher sein, die einzelnen Kacheln zu drehen, aber dies führt zu der Frage, was mit den bereits vorhandenen Kacheln passiert, und dies wäre im Allgemeinen gut zu wissen, bevor ich versuchen kann, eine Antwort zu geben.
James
Entschuldigung für den Fehler. Ich spreche über den linken Teil des Bildes. Ich hatte schlechte Ergebnisse, immer sind einige Felder an falschen Stellen. Das rosa Hex ist das Hauptfeld und die hellblauen Felder sind Kinder. Angenommen, das Haupthex ist (5,5), dann definiere ich ein untergeordnetes Hex (-1,0), so dass sich das Kind auf der linken Seite von Pink befindet und so weiter. Ich möchte wissen, wie dieses Kind um 60 Grad gedreht wird (dann ist es links oben in Pink). einfacher: Ich arbeite an einem Build-System in meinem Strategiespiel. Oft können Sie in Strategiespielen das Gebäude drehen, bevor Sie es platzieren. Ich werde die Hexen berechnen, die gebaut werden müssen.
Ruzsoo
Muss die Menge der ausgewählten Felder jedes Mal genau gleich sein? Das heißt, platzieren Sie zum Beispiel speziell 3 Objekte auf den Feldern auf beiden Seiten des rosa Feldes? Oder möchten Sie einfach nur eine Linie einer bestimmten Länge zeichnen und entscheiden, welche Felder sie am besten schneiden, unabhängig davon, wie viele es sein werden? Möchten Sie dies mit einer festen Anzahl von Hexen oder einer beliebigen Anzahl tun?
Tim Holt

Antworten:

11

Wie Martin Sojka bemerkt, sind Rotationen einfacher, wenn Sie in ein anderes Koordinatensystem konvertieren, die Rotation durchführen und dann zurück konvertieren.

Ich benutze ein anderes Koordinatensystem als Martin x,y,z. In diesem System gibt es kein Wackeln und es ist nützlich für viele Hex-Algorithmen. In diesem System können Sie das Hex drehen, 0,0,0indem Sie die Koordinaten „drehen“ und ihre Vorzeichen umdrehen: x,y,zDreht sich in die -y,-z,-xeine und in -z,-x,-ydie andere Richtung. Ich habe ein Diagramm auf dieser Seite .

(Es tut mir leid wegen x / y / z gegen X / Y, aber ich verwende x / y / z auf meiner Website und Sie verwenden X / Y in Ihrem Code, sodass in dieser Antwort der Fall wichtig ist! Also werde ich verwenden xx,yy,zzwie die Variablennamen unten, um die Unterscheidung zu erleichtern.)

Konvertieren Sie Ihre X,YKoordinaten in das x,y,zFormat:

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

Führen Sie eine Drehung um 60 ° in die eine oder andere Richtung durch:

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

Konvertieren Sie den x,y,zRücken zu Ihrem X,Y:

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

Wenn Sie beispielsweise mit (X = -2, Y = 1) beginnen und um 60 ° nach rechts drehen möchten, konvertieren Sie:

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

dann um -2,1,160 ° nach rechts drehen mit:

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

wie Sie hier sehen:

Hex-Rotationsbeispiel für -2,1,1

dann zurück konvertieren -1,2,-1:

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

Also dreht sich (X = -2, Y = 1) um 60 ° nach rechts in (X = -2, Y = -1).

amitp
quelle
4

Definieren wir zunächst eine neue Nummer. Keine Sorge, es ist einfach.

  • f : f × f = -3

Oder einfach ausgedrückt: f = √3 × i , wobei i die imaginäre Einheit ist . Damit entspricht eine Drehung um 60 Grad im Uhrzeigersinn der Multiplikation mit 1/2 × (1 - f ) und eine Drehung um 60 Grad gegen den Uhrzeigersinn der Multiplikation mit 1/2 × (1 + f ) . Wenn dies seltsam klingt, denken Sie daran, dass die Multiplikation mit einer komplexen Zahl der Drehung in der 2D-Ebene entspricht. Wir "quetschen" nur die komplexen Zahlen in der imaginären Richtung ein wenig (um √3), um uns nicht mit Quadratwurzeln oder Nicht-Ganzzahlen befassen zu müssen.

Wir können den Punkt (a, b) auch als a + b × f schreiben .

Dadurch können wir jeden Punkt in der Ebene drehen. Zum Beispiel dreht sich der Punkt (2,0) = 2 + 0 × f zu (1, -1), dann zu (-1, -1), (-2,0), (-1,1), ( 1,1) und schließlich zurück zu (2,0), einfach durch Multiplikation.

Natürlich brauchen wir eine Möglichkeit, diese Punkte von unseren Koordinaten in diejenigen zu übersetzen, in denen wir die Rotationen durchführen, und dann wieder zurück. Dazu wird eine weitere Information benötigt: Wenn der Punkt, um den wir die Drehung durchführen, links oder rechts von der vertikalen Linie liegt. Der Einfachheit halber erklären wir, dass es einen "Wobble" -Wert w von 0 hat, wenn es links davon ist (wie der Mittelpunkt der Drehung [0,0] in Ihren unteren beiden Bildern), und von 1, wenn es rechts davon ist davon. Dies erweitert unsere ursprünglichen Punkte auf dreidimensional; ( x , y , w ), wobei "w" nach der Normalisierung entweder 0 oder 1 ist. Die Normalisierungsfunktion ist:

NORM: ( x , y , w ) -> ( x + Etage ( w / 2), y , w mod 2), wobei die "mod" -Operation so definiert ist, dass nur positive Werte oder Null zurückgegeben werden.

Unser Algorithmus sieht nun wie folgt aus:

  1. Transformieren Sie unsere Punkte ( a , b , c ) in ihre Positionen relativ zum Rotationszentrum ( x , y , w ), indem Sie ( a - x , b - y , c - w ) berechnen und dann das Ergebnis normalisieren. Dies setzt das Rotationszentrum offensichtlich auf (0,0,0).

  2. Transformieren Sie unsere Punkte von ihren "nativen" Koordinaten in die komplexen Rotationskoordinaten: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Drehen Sie unsere Punkte, indem Sie sie nach Bedarf mit einer der oben genannten Rotationszahlen multiplizieren.

  4. Ra-transformiere die Punkte von den Rotationskoordinaten zurück zu ihren "nativen": ( r , s ) -> (Boden ( r / 2), s , r mod 2), wobei "mod" wie oben definiert ist.

  5. Transformieren Sie die Punkte wieder in ihre ursprüngliche Position, indem Sie sie zum Rotationszentrum ( x , y , z ) hinzufügen und normalisieren.


Eine einfache Version unserer "Triplex" -Nummern, die auf f in C ++ basieren, würde folgendermaßen aussehen:

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
quelle