Малювання ізометричних ігрових світів


178

Який правильний спосіб намалювати ізометричну плитку у 2D грі?

Я читав посилання (наприклад, на цю ), які пропонують відображати плитки таким чином, що буде зигзагувати кожен стовпчик у 2D-представленні масиву карти. Я думаю, що їх слід малювати більше алмазним способом, де те, що витягується на екран, більше стосується того, як виглядатиме 2D масив, просто трохи повертається.

Чи є переваги чи недоліки будь-якого методу?

Відповіді:


504

Оновлення: виправлений алгоритм візуалізації карти, додано більше ілюстрацій, змінено форматування.

Можливо, перевагою техніки «зигзаг» для відображення плиток на екрані можна сказати, що плитки xта yкоординати розташовані на вертикальній та горизонтальній осях.

Підхід "малювання в ромбі":

Намалювавши ізометричну карту за допомогою "малювання в ромбі", що, на мою думку, стосується просто рендеринга карти за допомогою вкладеної forпетлі над двовимірним масивом, наприклад на цьому прикладі:

tile_map[][] = [[...],...]

for (cellY = 0; cellY < tile_map.size; cellY++):
    for (cellX = 0; cellX < tile_map[cellY].size cellX++):
        draw(
            tile_map[cellX][cellY],
            screenX = (cellX * tile_width  / 2) + (cellY * tile_width  / 2)
            screenY = (cellY * tile_height / 2) - (cellX * tile_height / 2)
        )

Перевага:

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

Недолік:

Один недолік цього підходу полягає в тому, що xі yкоординати плитки на карті буде збільшуватися в діагональних ліній, які могли б зробити його більш важко візуально відобразити розташування на екрані на карті , представленої в вигляді масиву:

Зображення карти плитки

Однак реалізація вищевказаного коду має бути хиткою - наказ відтворення призведе до того, що плитки, які повинні бути за певними плитками, будуть намальовані зверху на плитках спереду:

Отримане зображення від неправильного порядку відтворення

Щоб виправити цю проблему, порядок внутрішнього forциклу необхідно змінити - починаючи з найвищого значення та переходячи до нижчого значення:

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    for (j = tile_map[i].size; j >= 0; j--):  // Changed loop condition here.
        draw(
            tile_map[i][j],
            x = (j * tile_width / 2) + (i * tile_width / 2)
            y = (i * tile_height / 2) - (j * tile_height / 2)
        )

За допомогою наведеного вище виправлення відображення карти слід виправити:

Отримане зображення від правильного порядку візуалізації

"Зігзаг" підхід:

Перевага:

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

Зігзаговий підхід до візуалізації здається компактним

Недолік:

Не намагаючись реалізувати техніку zig-zag, недоліком може бути те, що писати код візуалізації трохи складніше, тому що він не може бути записаний так просто, як вкладений forцикл над кожним елементом масиву:

tile_map[][] = [[...],...]

for (i = 0; i < tile_map.size; i++):
    if i is odd:
        offset_x = tile_width / 2
    else:
        offset_x = 0

    for (j = 0; j < tile_map[i].size; j++):
        draw(
            tile_map[i][j],
            x = (j * tile_width) + offset_x,
            y = i * tile_height / 2
        )

Крім того, може бути дещо складно спробувати визначити координату плитки через поетапний характер порядку виведення:

Координати на візуалізації замовлення

Примітка. Ілюстрації, що містяться у цій відповіді, були створені з реалізацією Java коду подання плитки, представленого із таким intмасивом як карта:

tileMap = new int[][] {
    {0, 1, 2, 3},
    {3, 2, 1, 0},
    {0, 0, 1, 1},
    {2, 2, 3, 3}
};

Зображення плитки:

  • tileImage[0] -> Коробка з коробкою всередині.
  • tileImage[1] -> Чорний ящик.
  • tileImage[2] -> Біла коробка.
  • tileImage[3] -> Коробка з високим сірим предметом у ній.

Примітка про ширину та висоту плитки

Змінні tile_widthта tile_heightвикористовувані у наведених вище прикладах коду стосуються ширини та висоти основної плитки на зображенні, що представляє плитку:

Зображення, що показує ширину та висоту плитки

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


136
ви навіть малювали картини. Це зусилля.
zaratustra

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

3
А як з декількома шарами висоти? Чи можу я намалювати їх усміхненими, починаючи з найнижчого рівня та продовжуючи до досягнення найвищого рівня?
NagyI

2
@DomenicDatti: Дякую за добрі слова :)
coobird

2
Це круто. Я просто використовував свій алмазний підхід , а також, в разі , якщо хто - то повинен отримати сітки COORDS від положення екрану, я зробив це: j = (2 * x - 4 * y) / tilewidth * 0.5; i = (p.x * 2 / tilewidth) - j;.
Kyr Dunenkoff

10

У будь-якому випадку робота буде виконана. Я припускаю, що під зигзагом ви маєте на увазі щось подібне: (цифри - це порядок відображення)

..  ..  01  ..  ..
  ..  06  02  ..
..  11  07  03  ..
  16  12  08  04
21  17  13  09  05
  22  18  14  10
..  23  19  15  ..
  ..  24  20  ..
..  ..  25  ..  ..

І під діамантом ви маєте на увазі:

..  ..  ..  ..  ..
  01  02  03  04
..  05  06  07  ..
  08  09  10  11
..  12  13  14  ..
  15  16  17  18
..  19  20  21  ..
  22  23  24  25
..  ..  ..  ..  ..

Перший метод потребує більшої кількості відображених плиток, щоб намалювати весь екран, але ви можете легко перевірити межі та пропустити будь-яку плитку повністю поза екраном. Обидва способи потребують певної кількості розчавлення, щоб з'ясувати, яке місце розташування плитки 01. Зрештою, обидва способи приблизно рівні за рівнем математики, необхідними для певного рівня ефективності.


14
Я насправді мав на увазі навпаки. Форма алмазу (який залишає краї карти гладкими) та метод зигзагу, залишаючи кромку країв
Benny Hallett

1

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

...1...
..234..
.56789.
..abc..
...d...

1

Відповідь Coobird правильна, повна. Однак я поєднав його підказки з тими з іншого сайту, щоб створити код, який працює в моєму додатку (iOS / Objective-C), яким я хотів поділитися з усіма, хто приходить сюди, шукаючи щось таке. Якщо вам подобається / голосуйте за цю відповідь, зробіть те саме для оригіналів; все, що я робив, було «стояти на плечах гігантів».

Щодо порядку сортування, моя техніка - це модифікований алгоритм художника: кожен об'єкт має (а) висоту основи (я називаю "рівень") і (б) X / Y для "основи" або "стопи" зображення (приклади: основа аватара знаходиться біля його ніг; основа дерева знаходиться в його коренях; основа літака - це зображення в центрі тощо). Тоді я просто сортую найнижчий до найвищого рівня, а потім найнижчий (найвищий на екрані) до найвищого рівня- Y, то найнижча (зліва-найбільше) до найвищої основи-X. Це робить плитку так, як можна було б очікувати.

Код для перетворення екрана (точки) в плитку (комірку) і назад:

typedef struct ASIntCell {  // like CGPoint, but with int-s vice float-s
    int x;
    int y;
} ASIntCell;

// Cell-math helper here:
//      http://gamedevelopment.tutsplus.com/tutorials/creating-isometric-worlds-a-primer-for-game-developers--gamedev-6511
// Although we had to rotate the coordinates because...
// X increases NE (not SE)
// Y increases SE (not SW)
+ (ASIntCell) cellForPoint: (CGPoint) point
{
    const float halfHeight = rfcRowHeight / 2.;

    ASIntCell cell;
    cell.x = ((point.x / rfcColWidth) - ((point.y - halfHeight) / rfcRowHeight));
    cell.y = ((point.x / rfcColWidth) + ((point.y + halfHeight) / rfcRowHeight));

    return cell;
}


// Cell-math helper here:
//      http://stackoverflow.com/questions/892811/drawing-isometric-game-worlds/893063
// X increases NE,
// Y increases SE
+ (CGPoint) centerForCell: (ASIntCell) cell
{
    CGPoint result;

    result.x = (cell.x * rfcColWidth  / 2) + (cell.y * rfcColWidth  / 2);
    result.y = (cell.y * rfcRowHeight / 2) - (cell.x * rfcRowHeight / 2);

    return result;
}

1

Ви можете використовувати евклідову відстань від найвищої точки та найближчої до глядача, за винятком того, що це не зовсім правильно. Це призводить до сферичного порядку сортування. Ви можете випрямити це, дивлячись здалеку. Далі кривизна вирівнюється. Тому просто додайте, скажімо, 1000 до кожного з компонентів x, y і z, щоб дати x ', y' і z '. Сортування на x '* x' + y '* y' + z '* z'.


0

Справжня проблема полягає в тому, коли вам потрібно намалювати плитку / спрайт, що перетинається / охоплює дві або більше інших плиток.

Після 2 (важких) місяців особистого аналізу проблеми я нарешті знайшов та застосував "правильний малюнок візуалізації" для моєї нової гри cocos2d-js. Рішення полягає у картографуванні для кожної плитки (сприйнятливої), що спрайти "спереду, ззаду, зверху і ззаду". Зробивши це, ви можете намалювати їх, дотримуючись "рекурсивної логіки".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.