Як я обертаю структуру шестикутної плитки на шестикутній сітці?


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це шістнадцятковий, який я хочу обертати, але він не працює:

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


1
в чому саме проблема? як реалізувати той чи деякі погані результати?
Ali1S232

Ви прив'язуєте обертання до 6 країв рожевого шестикутника чи кути повороту довільні? Також, який із рожевих шестикутників у правій структурі ви обертаєте?
Keeblebrox

Можливо, просто обертати окремі плитки, але це призводить до питання, що відбувається з плитками, які вже є, і це було б добре знати загалом, перш ніж я можу спробувати дати відповідь.
Джеймс

Вибачте за помилку. Я кажу про ліву частину зображення. У мене були погані результати, колись якісь шестигранники знаходяться в неправильних місцях. Рожева шестигранна головна, а яскраво-сині шестигранні - це дитячі. Припустимо, головний шестигранник (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 vs 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

як ви бачите тут:

Приклад обертання шістнадцяткової для -2,1,1

потім конвертувати -1,2,-1назад:

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

Отже (X = -2, Y = 1) обертається на 60 ° прямо в (X = -2, Y = -1).


4

Давайте спочатку визначимо нове число. Не хвилюйтесь, це легко.

  • f : f × f = -3

Або, простіше кажучи: f = √3 × i , при цьому я є уявною одиницею . При цьому обертання на 60 градусів за годинниковою стрілкою таке саме, як множення на 1/2 × (1 - f ) , а обертання на 60 градусів проти годинникової стрілки таке ж, як множення на 1/2 × (1 + f ) . Якщо це звучить дивно, пам’ятайте, що множення на складне число те саме, що обертання в площині 2D. Ми просто трохи «збиваємо» складні числа в уявному напрямку (на √3), щоб не мати справу з квадратними коренями ... або з не цілими числами.

Ми також можемо записати точку (a, b) у вигляді + b × f .

Це дозволяє нам обертати будь-яку точку в площині; наприклад, точка (2,0) = 2 + 0 × f обертається до (1, -1), потім до (-1, -1), (-2,0), (-1,1), ( 1,1) і, нарешті, повернемось до (2,0), просто помноживши його.

Звичайно, нам потрібен спосіб перевести ці точки від наших координат до тих, у яких ми робимо обертання, а потім знову повернутись. Для цього потрібен ще один біт інформації: Якщо точка, яку ми робимо обертанням, знаходиться вліво або вправо від вертикальної лінії. Для простоти ми заявляємо, що має значення "коливання" w 0, якщо воно ліворуч від нього (наприклад, центр обертання [0,0] у ваших двох нижньому зображенні), а також 1, якщо справа з нього. Це розширює наші вихідні точки тривимірними; ( x , y , w ), причому "w" після нормалізації буде або 0, або 1. Функцією нормалізації є:

NORM: ( x , y , w ) -> ( x + floor ( 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 ) -> (пол ( 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));
        }
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.