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


19

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

Приклад зображення: відсортовано за екраном y

Стіна 2 - один піксель нижче стіни 1, тому вона намальована після стіни 1 і закінчується зверху.

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

Приклад зображення: відсортовано по ізометричній y

Як це зробити правильно?


3
Здається, ви натрапили на проблеми, поширені в Алгоритмі художника. "Загальним" рішенням є використання z-буфера =). Деякі обхідні шляхи включають використання об'єктного центру в якості ключа сортування замість кутового.
Jari Komppa

Відповіді:


12

Ізометричні ігри функціонально 3D, тому внутрішньо вам слід зберігати тривимірні координати для кожної сутності в грі. Фактичні координати, які ви вибираєте, є довільними, але, скажімо, X і Y - дві наземні осі, а Z - від землі вгору.

Потім рендерінг повинен спроектувати його в 2D, щоб намалювати речі на екрані. "Ізометрична" - одна з таких проекцій. Проектування від 3D до 2D ізометрично досить просто. Скажімо, що вісь X йде вліво-вліво-вправо і вниз по одному пікселю на кожні два горизонтальні пікселі. Так само вісь Y йде зверху праворуч донизу ліворуч. Вісь Z йде прямо вгору. Щоб конвертувати з 3D в 2D тоді просто:

function projectIso(x, y, z) {
    return {
        x: x - y,
        y: (x / 2) + (y / 2) - z
    };
}

Тепер до вашого оригінального питання, сортування. Тепер, коли ми працюємо з нашими об’єктами безпосередньо в 3D, сортування стає набагато простішим. У нашому координатному просторі найдальший спрайт має найнижчі координати x, y та z (тобто всі три осі вказують на екран). Тож ви просто сортуєте їх за сумою цих:

function nearness(obj) {
    return obj.x + obj.y + obj.z;
}

function closer(a, b) {
    if (nearness(a) > nearness(b)) {
        return "a";
    } else {
        return "b";
    }
}

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


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

4

Якщо припустити, що ваші спрайти займають набори плиток, які є прямокутниками (якщо вони займають довільні множини, то ви взагалі не можете правильно намалювати), проблема полягає в тому, що між елементами немає загального відношення порядку, тому ви не можете сортувати вони використовують сортування, яке призведе до порівняння O (nlogn).

Зверніть увагу, що для будь-яких двох об'єктів A і B перед B (A <- B) потрібно намалювати A, B перед A (B <- A) або їх можна намалювати в будь-якому порядку. Вони утворюють частковий порядок. Якщо ви намалюєте собі кілька прикладів з 3 перекриваються об’єктами, ви можете помітити, що навіть якщо 1-й і 3-й об'єкти можуть не перетинатися, таким чином, не маючи прямої залежності, їх порядок малювання залежить від 2-го об'єкта, який знаходиться між ними - залежно від того, як розмістивши його, ви отримаєте різні замовлення на малювання. Підсумок - традиційні сорти тут не працюють.

Одним із варіантів є використання порівняння (згадане Дані) та порівняння кожного об'єкта один з одним для визначення їх залежностей та формування графіка залежності (який буде DAG). Потім зробіть топологічне сортування на графіку, щоб визначити порядок малювання. Якщо об’єктів не занадто багато, це може бути досить швидким (це O(n^2)).

Іншим рішенням є використання квадратичного дерева (для врівноваження - псевдо ) та збереження у ньому прямокутників усіх об’єктів.

Потім перегляньте всі об'єкти X і використовуйте чотирьох дерев, щоб перевірити, чи немає об'єктів Y в смужці над об'єктом X, що починається з лівого лівого кінця і закінчується краєм правого кута об'єкта X - для всіх таких Y, Y < - X. Як це, вам все одно доведеться формувати графік і сортувати топологічно.

Але ви можете цього уникнути. Ви використовуєте перелік об'єктів Q та таблицю об'єктів T. Ви ітератуєте всі видимі слоти від менших до більших значень на осі x (один рядок), переходячи рядок за рядком на вісь y. Якщо в цьому слоті є нижній кут об'єкта, виконайте описану вище процедуру для визначення залежностей. Якщо об’єкт X залежить від якогось іншого об'єкта Y, який частково знаходиться над ним (Y <- X), і кожен такий Y вже знаходиться в Q, додайте X до Q. Якщо є деякий Y, якого немає в Q, додайте X до T і позначаємо, що Y <- X. Кожен раз, коли ви додаєте об'єкт до Q, ви видаляєте залежності об'єктів, що знаходяться в T. Якщо всі залежності вилучені, об'єкт з T переміщується в Q.

Ми припускаємо, що спрайти об'єктів не визирають зі своїх прорізів на нижній, лівій або правій частині (лише вгорі, як дерева на малюнку). Це повинно покращити продуктивність для великої кількості об’єктів. Такий підхід знову буде O(n^2), але лише в гіршому випадку, який включає об'єкти дивного розміру та / або дивні конфігурації об'єктів. У більшості випадків це так O(n * logn * sqrt(n)). Знаючи висоту ваших спрайтів, ви можете усунути sqrt(n), тому що вам не потрібно перевіряти всю смужку вище. Залежно від кількості об'єктів на екрані, ви можете спробувати замінити квадрокореневе масив із зазначенням, які слоти прийняті (має сенс, якщо об’єктів багато).

Нарешті, не соромтеся ознайомитись із цим вихідним кодом щодо деяких ідей: https://github.com/axel22/sages/blob/master/src/gui/scala/name/brijest/sages/gui/Canvas.scala


2

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

Вам, мабуть, слід сортувати за екраном Y за плиткою і сказати, що все складніше - це "проблема дизайну". Наприклад, якщо ви створюєте вміст, просто скажіть своїм дизайнерам алгоритм сортування та підсуньте wall2 на 2 пікселі, щоб вирішити проблему. Ось так нам довелося це виправити в ізометричній грі, над якою я працював. Це може включати взяття «довгих» предметів і розбивання їх на шматки розміру плитки.

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


2

Ідеальне ізометричне сортування важко. Але для першого підходу, щоб правильно сортувати предмети, слід використовувати більш складну функцію порівняння з кожним об'єктом, що перекривається. Ця функція повинна перевірити цю умову: Об'єкт "a", що перекривається, знаходиться поза "b", якщо:

(a.posX + a.sizeX <= b.posX) або (a.posY + a.sizeY <= b.posY) або (a.posZ + a.sizeZ <= b.posZ)

Звичайно, це перша ідея для наївного ізометричного втілення. Для більш складного сценарію (якщо ви хочете обертання перегляду, положення пікселів на осі z тощо), вам потрібно буде перевірити більше умов.


+1, якщо у вас плитки з різною шириною / висотою, подивіться функцію порівняння в цій відповіді.
mucaho

2

Я використовую дуже просту формулу, яка добре працює з моїм маленьким ізометричним двигуном у OpenGL:

Кожен із вас об'єктів (дерева, плитка для підлоги, символи, ...) має на екрані положення X та Y. Потрібно ввімкнути тестування заглиблень та знайти хороше значення Z для кожного значення. Ви можете просто:

z = x + y + objectIndex

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



1

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

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

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

Простіший підхід - розділити всі спрайти на три групи: діагоналі вздовж осі x, діагоналі вздовж осі y та плоскі. Якщо обидва об'єкти мають діагоналі вздовж однієї осі, сортуйте їх на основі іншої осі.


0

Вам потрібно призначити унікальні ідентифікатори всім об'єктам одного типу. Потім ви сортуєте всі об’єкти за їхніми позиціями та малюєте групи об’єктів у порядку їх ідентифікаторів. Таким чином, об'єкт 1 у групі A ніколи не перевищує об'єкт 2 у групі A тощо.


0

Це насправді не відповідь, а лише коментар та підсумок голосування, який я хотів би дати на цю відповідь axel22 тут /gamedev//a/8181/112940

У мене не вистачає репутації, щоб голосувати і не коментувати інші відповіді, але другий абзац його відповіді, мабуть, найважливіше, про що слід знати, намагаючись сортувати сутності в ізометричній грі, не покладаючись на "сучасний" 3D такі методи, як Z-буфер.

У своєму двигуні я хотів робити речі "old-school", чисті 2D. І я витратив багато часу, розбиваючи мозок, намагаючись з'ясувати, чому мій заклик "сортувати" (у моєму випадку це c ++ std :: sort) не працює належним чином у певних конфігураціях карт.

Лише коли я зрозумів, що це ситуація "часткового порядку", я зміг її вирішити.

Поки всі робочі рішення, які я знайшов в Інтернеті, використовували якесь топологічне сортування, щоб правильно вирішити проблему. Топологічне сортування, здається, є шляхом.

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