2D ізометрична: координати екрана до плитки


9

Я пишу ізометричну 2D гру і мені важко зрозуміти, на якій плитці знаходиться курсор. Ось малюнок:

де xs і y - координатні екрани (пікселі), xt і yt - координати плитки, W і H - ширина плитки і висота плитки в пікселях відповідно. Моє позначення координат (y, x), що може заплутати, вибачте за це.

Найкраще, що я міг зрозуміти поки що, це:

int xtemp = xs / (W / 2);
int ytemp = ys / (H / 2);
int xt = (xs - ys) / 2;
int yt = ytemp + xt;

Це здається майже правильним, але дає мені дуже неточний результат, ускладнює вибір певних плиток, а іноді він вибирає плитку поруч із тією, на яку я намагаюся натиснути. Я не розумію, чому я хотів би, якщо хтось міг допомогти мені зрозуміти логіку цього.

Дякую!

Відповіді:


2

Для точної міри ми можемо врахувати наступне:

Давайте спочатку розглянемо, як перетворити координати з ізометричного простору, визначеного векторами i та j (як в isometricMap [i, j]) або як yt і xt на екрані, до екранного простору, визначеного x і y екрана. Давайте припустимо, що ваш простір екрана вирівняний за початком з ізометричним простором заради простоти.

Один із способів зробити перетворення - спершу зробити обертання, а потім масштабувати вісь y або x. Щоб отримати необхідні значення, щоб відповідати вашим yt та xt, я не можу тут просто придумати. Ви можете створити матрицю, щоб зробити це чи ні, а потім використовувати зворотну матрицю, але зворотна операція - це в основному те, що ви хочете.

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

Я думаю, є й інші способи, але це здається мені найбільш правильним зараз.


argh Я так багато разів переглядав цю посаду, і я думаю, що я не можу отримати свою точку так акуратно, як мені б хотілося в будь-якому випадку. Мені треба поспати.
Тоні

1
Дякую, матриці, безумовно, найкраще рішення тут. У мене щось майже зараз працює!
Асик

4

У мене була така сама проблема для гри, яку я писав. Я думаю, що ця проблема буде відрізнятися залежно від того, як саме ви реалізували вашу ізометричну систему, але я поясню, як я вирішив проблему.

Я вперше розпочав свою функцію tile_to_screen. (Я припускаю, що саме так ви розміщуєте плитки в потрібному місці в першу чергу.) Ця функція має рівняння для обчислення screen_x та screen_y. Моя виглядала так (пітон):

def map_to_screen(self, point):
    x = (SCREEN_WIDTH + (point.y - point.x) * TILE_WIDTH) / 2
    y = (SCREEN_HEIGHT + (point.y + point.x) * TILE_HEIGHT) / 2
    return (x, y)

Я взяв ці два рівняння і склав їх у систему лінійних рівнянь. Розв’яжіть цю систему рівнянь будь-яким обраним вами методом. (Я використовував метод rref. Також деякі графічні калькулятори можуть вирішити цю проблему.)

Кінцеві рівняння виглядали приблизно так:

# constants for quick calculating (only process once)
DOUBLED_TILE_AREA = 2 * TILE_HEIGHT * TILE_WIDTH
S2M_CONST_X = -SCREEN_HEIGHT * TILE_WIDTH + SCREEN_WIDTH * TILE_HEIGHT
S2M_CONST_Y = -SCREEN_HEIGHT * TILE_WIDTH - SCREEN_WIDTH * TILE_HEIGHT

def screen_to_map(self, point):
    # the "+ TILE_HEIGHT/2" adjusts for the render offset since I
    # anchor my sprites from the center of the tile
    point = (point.x * TILE_HEIGHT, (point.y + TILE_HEIGHT/2) * TILE_WIDTH)
    x = (2 * (point.y - point.x) + self.S2M_CONST_X) / self.DOUBLED_TILE_AREA
    y = (2 * (point.x + point.y) + self.S2M_CONST_Y) / self.DOUBLED_TILE_AREA
    return (x, y)

Як бачите, це не так просто, як початкове рівняння. Але це добре працює для створеної мною гри. Слава богу за лінійну алгебру!

Оновлення

Після написання простого класу Point з різними операторами я спростив цю відповідь на наступне:

# constants for quickly calculating screen_to_iso
TILE_AREA = TILE_HEIGHT * TILE_WIDTH
S2I_CONST_X = -SCREEN_CENTER.y * TILE_WIDTH + SCREEN_CENTER.x * TILE_HEIGHT
S2I_CONST_Y = -SCREEN_CENTER.y * TILE_WIDTH - SCREEN_CENTER.x * TILE_HEIGHT

def screen_to_iso(p):
    ''' Converts a screen point (px) into a level point (tile) '''
    # the "y + TILE_HEIGHT/2" is because we anchor tiles by center, not bottom
    p = Point(p.x * TILE_HEIGHT, (p.y + TILE_HEIGHT/2) * TILE_WIDTH)
    return Point(int((p.y - p.x + S2I_CONST_X) / TILE_AREA),
                 int((p.y + p.x + S2I_CONST_Y) / TILE_AREA))

def iso_to_screen(p):
    ''' Converts a level point (tile) into a screen point (px) '''
    return SCREEN_CENTER + Point((p.y - p.x) * TILE_WIDTH / 2,
                                 (p.y + p.x) * TILE_HEIGHT / 2)

Так, також повинна працювати система з двох лінійних рівнянь. Зважаючи на те, що у нас є два вектори, які не паралельні, ви повинні мати можливість дістати будь-яку точку на площині, використовуючи одиничні вектори yt та xt. Хоча я думаю, що ваша реалізація трохи звужена, і я не збираюся заважати її підтверджувати.
Тоні

2

Ви використовуєте хорошу систему координат. Якщо ви використовуєте ступінчасті стовпці, речі стають набагато складнішими.

Один із способів задуматися над цією проблемою полягає в тому, що у вас є функція перетворити (xt, yt) в (xs, ys). Я піду за відповіддю Тана і зателефоную map_to_screen.

Ви хочете обернути цю функцію. Ми можемо це назвати screen_to_map. Обертання функцій мають такі властивості:

map_to_screen(screen_to_map(xs, ys)) == (xs, ys)
screen_to_map(map_to_screen(xt, yt)) == (xt, yt)

Ці дві речі - це тестування, коли ви написали обидві функції. Як ви пишете зворотне? Не всі функції мають обертання, але в цьому випадку:

  1. Якщо ви записали це як обертання з подальшим перекладом, то зворотним є зворотний переклад (негативний dx, dy), за яким слід обертання (негативний кут).
  2. Якщо ви написали це як матричне множення, то оберненою є матриця, обернена множиною.
  3. Якщо ви написали це як алгебраїчні рівняння, що визначають (xs, ys) у виразі (xt, yt), то обернення знаходить, розв’язуючи ці рівняння для (xt, yt) заданих (xs, ys).

Переконайтесь, що функція інверсія + оригіналу повертає відповідь, з якого ви почали. Thane здає обидва тести, якщо ви + TILE_HEIGHT/2виймаєте компенсацію візуалізації. Коли я розв’язував алгебру, я придумав:

x = (2*xs - SCREEN_WIDTH) / TILE_WIDTH
y = (2*ys - SCREEN_HEIGHT) / TILE_HEIGHT
yt =  (y + x) / 2
xt =  (y - x) / 2

який я вважаю таким же, як і Тайн screen_to_map.

Функція перетворить координати миші в поплавці; використовувати floorдля перетворення їх у цілі координати плитки.


1
Дякую! Я в кінцевому підсумку використовував матрицю перетворення, так що запис зворотного є тривіальним, тобто це просто Matrix.Invert (). Плюс це призводить до більш декларативного стилю кодування (Matrix.Translate () * Matrix.Scale () * Matrix.Rotate (), а не купи рівнянь). Можливо, це дещо повільніше, але це не повинно бути проблемою.
Асик
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.