Comment faire pivoter une structure de tuiles hexagonales sur une grille hexagonale?

10

Mon jeu isométrique 2D utilise une carte quadrillée hexagonale. En référence à l'image ci-dessous, comment faire pivoter les structures hexagonales bleu clair de 60 degrés autour des hexagones roses?

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

ÉDITER:

L'hex principal est (0,0). Les autres hex sont des enfants, le nombre d'entre eux est fixe. Je vais définir une seule position (dans ce cas, sa droite) et calculer d'autres directions si nécessaire (en bas à gauche, en bas à droite, en haut à droite, en haut à gauche et à gauche). D'autres hexagones sont définis comme: Package.Add (-1,0), Package.Add (-2,0) et ainsi de suite.

entrez la description de l'image ici

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

Dans ce code Numberest l'hexagone principal et Pointl'hexagone que je veux faire pivoter, mais cela ne fonctionne pas:

entrez la description de l'image ici

ruzsoo
la source
1
Quel est le problème exactement? comment mettre en œuvre cela ou de mauvais résultats?
Ali1S232
Faites-vous des rotations sur les 6 bords de l'hexagone rose ou les angles de rotation sont-ils arbitraires? De plus, parmi les hexagones roses de la structure de droite, tournez-vous?
Keeblebrox
Il est peut-être plus facile de faire pivoter les tuiles individuelles, mais cela conduit à se demander ce qui arrive aux tuiles qui sont déjà là et ce serait bon de savoir en général avant de pouvoir essayer de donner une réponse.
James
Désolé de mon erreur. Je parle de la partie gauche de l'image. J'ai eu de mauvais résultats, jamais certains hexagones sont au mauvais endroit. L'hexagone rose est principal et les hexagones bleu vif sont des enfants. Supposons que l'hex principal est (5,5), puis je définis un hex enfant (-1,0) de sorte que l'enfant soit du côté gauche du rose et ainsi de suite. Je veux savoir comment faire tourner cet hexagone enfant de 60 degrés (alors ce sera en haut à gauche du rose). plus facile: je travaille sur le système de construction dans mon jeu de stratégie. Souvent, dans les jeux de stratégie, vous pouvez faire pivoter le bâtiment avant de le placer. Je vais calculer les hexs qui doivent être construits.
ruzsoo
L'ensemble d'hex sélectionnés doit-il être exactement le même nombre à chaque fois? C'est-à-dire, par exemple, placez-vous spécifiquement 3 objets sur les hexs de chaque côté de l'hex rose? Ou voulez-vous simplement tracer une ligne d'une longueur donnée et décider quels hexs la coupent le mieux, quel que soit le nombre? Voulez-vous le faire avec un nombre fixe d'hexagones ou un nombre arbitraire?
Tim Holt le

Réponses:

11

Comme le note Martin Sojka , les rotations sont plus simples si vous convertissez dans un système de coordonnées différent, effectuez la rotation, puis reconvertissez.

J'utilise un système de coordonnées différent de celui de Martin, étiqueté x,y,z. Il n'y a pas d'oscillation dans ce système, et il est utile pour de nombreux algorithmes hexadécimaux. Dans ce système, vous pouvez faire pivoter l'hexagone 0,0,0en «tournant» les coordonnées et en inversant leurs signes: x,y,zse transforme dans -y,-z,-xun sens et dans -z,-x,-yl'autre. J'ai un schéma sur cette page .

(Je suis désolé pour x / y / z vs X / Y mais j'utilise x / y / z sur mon site et vous utilisez X / Y dans votre code, donc dans cette réponse, le cas est important! Je vais donc utiliser xx,yy,zzcomme les noms de variables ci-dessous pour essayer de le rendre plus facile à distinguer.)

Convertissez vos X,Ycoordonnées au x,y,zformat:

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

Effectuez une rotation de 60 ° dans un sens ou dans l'autre:

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

Convertissez le x,y,zdos en votre X,Y:

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

Par exemple, si vous commencez par (X = -2, Y = 1) et souhaitez faire une rotation de 60 ° vers la droite, vous convertiriez:

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

puis tournez de -2,1,160 ° vers la droite avec:

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

comme vous le voyez ici:

Exemple de rotation hexagonale pour -2,1,1

puis reconvertissez -1,2,-1:

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

Donc (X = -2, Y = 1) pivote de 60 ° à droite dans (X = -2, Y = -1).

amitp
la source
4

Définissons d'abord un nouveau numéro. Pas de soucis, c'est facile.

  • f : f × f = -3

Ou, pour le dire simplement: f = √3 × i , avec i étant l' unité imaginaire . Avec cela, une rotation de 60 degrés dans le sens horaire est la même que la multiplication par 1/2 × (1 - f ) , et une rotation de 60 degrés dans le sens antihoraire la même que la multiplication par 1/2 × (1 + f ) . Si cela vous semble étrange, n'oubliez pas que la multiplication par un nombre complexe équivaut à la rotation dans le plan 2D. On "écrase" un peu les nombres complexes dans la direction imaginaire (par √3) pour ne pas avoir à traiter avec des racines carrées ... ou des non-entiers, d'ailleurs.

On peut aussi écrire le point (a, b) comme a + b × f .

Cela nous permet de faire pivoter n'importe quel point dans le plan; par exemple, le point (2,0) = 2 + 0 × f tourne vers (1, -1), puis vers (-1, -1), (-2,0), (-1,1), ( 1,1) et enfin de retour à (2,0), simplement en le multipliant.

Bien sûr, nous avons besoin d'un moyen de traduire ces points de nos coordonnées en ceux dans lesquels nous effectuons les rotations, puis à nouveau. Pour cela, une autre information est nécessaire: si le point sur lequel nous faisons la rotation est à "gauche" ou à "droite" de la ligne verticale. Par souci de simplicité, nous déclarons qu'il a une valeur "d'oscillation" w de 0 s'il se trouve à gauche de celui-ci (comme le centre de la rotation [0,0] dans vos deux images du bas), et de 1 s'il se trouve à droite de celui-ci. Cela étend nos points d'origine à trois dimensions; ( x , y , w ), "w" étant soit 0 soit 1 après normalisation. La fonction de normalisation est:

NORM: ( x , y , w ) -> ( x + étage ( w / 2), y , w mod 2), avec l'opération "mod" définie de telle sorte qu'elle ne renvoie que des valeurs positives ou nulles.

Notre algorithme se présente maintenant comme suit:

  1. Transformez nos points ( a , b , c ) en leurs positions par rapport au centre de rotation ( x , y , w ) en calculant ( a - x , b - y , c - w ), puis en normalisant le résultat. Cela met évidemment le centre de rotation à (0,0,0).

  2. Transformer nos points de leurs coordonnées "natives" en coordonnées complexes en rotation: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Faites pivoter nos points en les multipliant par l'un des nombres de rotation ci-dessus, si nécessaire.

  4. Transformez en Ra les points des coordonnées de rotation à leurs coordonnées "natives": ( r , s ) -> (étage ( r / 2), s , r mod 2), avec "mod" défini comme ci-dessus.

  5. Transformez les points à leur position d'origine en les ajoutant au centre de rotation ( x , y , z ) et en normalisant.


Une version simple de nos nombres "triplex" basés sur f en C ++ ressemblerait à ceci:

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
la source