Как вращать структуру шестиугольных плиток на шестиугольной сетке?

10

Моя 2D-изометрическая игра использует гексагональную сетку. Как показано на рисунке ниже, как я могу повернуть голубые шестиугольные структуры на 60 градусов вокруг розовых шестиугольников?

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

РЕДАКТИРОВАТЬ:

Главный гекс (0,0). Другие гексы - это дети, их количество фиксировано. Я собираюсь определить только одну позицию (в данном случае ее справа) и вычислить другие направления, если это необходимо (левый-нижний, правый-нижний, правый-верхний, левый-верхний и левый). Другие гексы определены как: Package.Add (-1,0), Package.Add (-2,0) и так далее.

введите описание изображения здесь

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

В этом коде Numberэто основной гекс и Pointэто гекс, который я хочу повернуть, но он не работает:

введите описание изображения здесь

ruzsoo
источник
1
в чем именно проблема? как реализовать это или какие-то плохие результаты?
Ali1S232
Вы привязываете повороты к 6 краям розового шестиугольника или углы поворота произвольны? Кроме того, какой из розовых шестиугольников в правой структуре вы вращаете вокруг?
Киблброкс
Может быть, проще повернуть отдельные плитки, но это приводит к вопросу о том, что происходит с плитками, которые уже есть, и это было бы полезно узнать в целом, прежде чем я попытаюсь дать ответ.
Джеймс
Извини за ошибку. Я говорю о левой части изображения. У меня были плохие результаты, когда-нибудь гексы были в неправильных местах. Розовый гекс - главный, а ярко-синие - детские. Предположим, что основной гекс равен (5,5), затем я определяю дочерний гекс (-1,0), поэтому ребенок находится слева от розового и так далее. Я хочу знать, как повернуть этот дочерний гекс на 60 градусов (тогда он будет слева от розового цвета). проще: я работаю над сборкой системы в моей стратегической игре. Часто в стратегических играх вы можете повернуть здание, прежде чем его разместить. Я собираюсь вычислить гексы, которые нужно построить.
ruzsoo
Должен ли набор выбранных гексов каждый раз быть одинаковым? То есть вы, например, специально размещаете 3 объекта на гексах по обе стороны от розового гекса? Или вы просто хотите нарисовать линию заданной длины и решить, какие гексы лучше всего пересекают ее, независимо от того, сколько это будет? Вы хотите сделать это с фиксированным числом гексов или произвольным числом?
Тим Холт

Ответы:

11

Как мартин сойка отмечает , вращение проще, если вы конвертируете в другую систему координат, выполняете вращение, а затем конвертируете обратно.

Я использую другую систему координат, чем Мартин, помеченную x,y,z. В этой системе нет колебаний, и это полезно для множества шестнадцатеричных алгоритмов. В этой системе вы можете вращать гекс вокруг 0,0,0, «вращая» координаты и переключая их знаки: x,y,zпревращается в -y,-z,-xодну сторону и -z,-x,-yдругую сторону. У меня есть диаграмма на этой странице .

(Я сожалею о x / y / z против X / Y, но я использую x / y / z на моем сайте, а вы используете X / Y в своем коде, поэтому в этом ответе дело имеет значение! Поэтому я собираюсь использовать xx,yy,zzв качестве имен переменных, приведенных ниже, чтобы их было легче различить.)

Преобразуйте ваши X,Yкоординаты в x,y,zформат:

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

Выполните поворот на 60 ° в одну или другую сторону:

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

Конвертировать x,y,zобратно в ваш X,Y:

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

Например, если вы начинаете с (X = -2, Y = 1) и хотите повернуть на 60 ° вправо, вы должны преобразовать:

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

затем поверните на -2,1,160 ° вправо с помощью:

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

как вы видите здесь:

Пример вращения Hex для -2,1,1

затем преобразовать -1,2,-1обратно:

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

Итак, (X = -2, Y = 1) поворачивается на 60 ° прямо в (X = -2, Y = -1).

amitp
источник
4

Давайте сначала определим новый номер. Не беспокойтесь, это легко.

  • f : f × f = -3

Или, проще говоря: f = √3 × i , где i - мнимая единица . При этом поворот на 60 градусов по часовой стрелке - это то же самое, что умножение на 1/2 × (1 - f ) , а поворот на 60 градусов против часовой стрелки - на умножение на 1/2 × (1 + f ) . Если это звучит странно, помните, что умножение на комплексное число совпадает с вращением в 2D-плоскости. Мы просто "раздавливаем" комплексные числа в воображаемом направлении немного (на √3), чтобы не иметь дело с квадратными корнями ... или нецелыми числами, если на то пошло.

Мы также можем записать точку (a, b) как a + b × f .

Это позволяет нам вращать любую точку на плоскости; например, точка (2,0) = 2 + 0 × f поворачивается к (1, -1), затем к (-1, -1), (-2,0), (-1,1), ( 1,1) и, наконец, вернуться к (2,0), просто умножив его.

Конечно, нам нужен способ перевести эти точки из наших координат в те, в которых мы делаем повороты, и затем обратно. Для этого нужен еще один бит информации: если точка, вокруг которой мы вращаемся, находится «влево» или «вправо» от вертикальной линии. Для простоты мы объявляем, что оно имеет значение «колебания» w, равное 0, если оно находится слева от него (как центр вращения [0,0] на двух ваших нижних рисунках), и 1, если оно находится справа этого Это расширяет наши оригинальные точки, чтобы быть трехмерным; ( х , у , ш ), где "w" равно 0 или 1 после нормализации. Функция нормализации:

НОРМА: ( x , y , w ) -> ( x + этаж ( w / 2), y , w mod 2), с заданной операцией «mod», которая возвращает только положительные значения или ноль.

Наш алгоритм теперь выглядит следующим образом:

  1. Преобразуйте наши точки ( a , b , c ) в их положения относительно центра вращения ( x , y , w ), вычисляя ( a - x , b - y , c - w ), а затем нормализуя результат. Это ставит центр вращения в (0,0,0), очевидно.

  2. Преобразуйте наши точки из их «нативных» координат в комплексные вращательные: ( a , b , c ) -> (2 × a + c , b ) = 2 × a + c + b × f

  3. Поверните наши точки, умножив их на одно из вращательных чисел выше, по мере необходимости.

  4. Ра-преобразовать точки обратно из координат вращения в их "родные" координаты: ( r , s ) -> (floor ( r / 2), s , r mod 2), с "mod", определенным, как указано выше.

  5. Повторно трансформируйте точки обратно в исходное положение, добавив их в центр вращения ( x , y , z ) и нормализуя.


Простая версия наших «триплексных» чисел на основе f в C ++ будет выглядеть так:

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));
        }
};
Мартин Сойка
источник