Алгоритм поширення етикетки візуально привабливим та інтуїтивним способом


24

Коротка версія

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

Розширена версія

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

Два транспортні засоби з їх етикетками

Тепер, оскільки транспортні засоби могли літати на різній висоті, їх значки можуть перекриватися. Однак я хотів би, щоб їх етикетки ніколи не перекривались (або етикетка з транспортного засобу "A" перекривалась значком транспортного засобу "B").

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

  B - label
A -----------label
  C - label

де було б краще (= ярлик ближче до транспортного засобу) дістати:

          B - label
label - A
          C - label

EDIT: Також слід враховувати, що поруч із корпусом транспортних засобів, що перекриваються, можуть бути інші конфігурації, в яких етикетки транспортних засобів можуть перекриватися (приклади мистецтва ASCII показують, наприклад, три дуже близькі транспортні засоби, на яких етикетка Aперекриває піктограму Bі C).

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

Для чого це варто, ось дві ідеї, про які я думав:

Слот-ізоляція простору етикетки

У цьому сценарії я б розділив весь екран на "прорізи" для міток. Тоді на кожному транспортному засобі завжди розміщуватиметься ярлик, розміщений у найближчому порожньому (порожній = немає інших спрайтів у цьому місці.

Спіральний пошук

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

try 0°, 10px
try 10°, 10px
try 20°, 10px
...
try 350°, 10px
try 0°, 20px
try 10°, 20px
...

1
Скільки літаків може перекриватися за один раз?
wangburger

1
@wangburger - Ніколи не думав, що це буде доречно (хотілося б дізнатися більше про твою думку), але відповідь: це залежить від ігрової стратегії гравця. Технічно у світі може бути 24 транспортних засоби, що перекриваються, але реальна цифра в більшості ігрових умов - 3-4.
mac

3
Хіба не більш заплутаним є рухливі мітки відносно площини, ніж перекриття, а статичні протягом розумного часу?
Майк Семдер

3
Можливо, вас зацікавить en.wikipedia.org/wiki/… - це вирішити непросту проблему. Не сподівайтесь знайти ідеальне рішення.
Блекі

2
GraphViz - це набір інструментів для розміщення графіків візуально приємним способом, з тенденцією уникати перекриттів міток. Хоча це може бути непридатним для використання безпосередньо, можливо, ви зможете отримати деяку інформацію з їх документації або вихідного коду про те, які алгоритми вони використовують для компонування своїх графіків. Здається, вони мають як енергетичні моделі, так і пружинні моделі.
Ларс Віклунд

Відповіді:


14

По суті це питання подібне до проблеми уникнення зіткнення. Так, літаки можуть літати на різній висоті, але їх мітки знаходяться на одній "висоті".

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

Якщо ви подивитеся на поведінку Flocking Behavior , ви хочете реалізувати перше "правило" flocking: відштовхування короткого дальності. Однак замість того, щоб "спрямовувати" в напрямку, що знаходиться від найближчих сусідів, ви використовуєте вектор "далеко" як місце розташування для вашої мітки.

Наприклад:

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

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

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

Тож, використовуючи ваш приклад вище, я думаю, що це призведе до такого типу:

            - label
           / 
          B 
label - A
          C 
           \
            - label

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

          - label       label -
          |                   |
          B                   B
  label - A                   A - label
          C                   C
          |                   |
          - label       label -

Усі три мітки можуть перевертати флоп праворуч наліво, залежно від того, як визначені ваші куточки етикетки. В основному, вам потрібно буде стежити за кутами навколо кола, де ваші мітки можуть перемикати, з якого кута вони намальовані: 0, 90, 180, 270.

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

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

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


1
Насправді мій приклад навіть не враховував площини, які знаходяться безпосередньо один над одним, оскільки їх координати x і z точно збігаються (але якщо ви використовуєте поплавці, це, швидше за все, не відбудеться). Я наводив приклади літаків, розташованих поруч. Крім того, ви могли, як я вже говорив, думати про чорні точки на зображенні, наведеному вище, як про інші мітки.
MichaelHouse

1
О, схоже, ви видалили свій коментар?
MichaelHouse

1
Що я мав на увазі з попереднім [погано сформульованим і таким чином тепер видаленим] коментарем, це те, що у вас може бути сценарій, коли ярлик "захоплений / оточений" іншими нерухливими (тобто транспортними засобами) спрайтами. У цьому випадку мітка, можливо, повинна «вистрибнути» поза оточуючим колом, але мені не зовсім зрозуміло, як це має відбуватися. BTW: Я спочатку хотів, що зеленою крапкою на вашій картині було кілька літаків, складених один на інший, але після вашого коментаря я зрозумів, що я помиляюся ... вибачте!).
мак

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

9

Подумавши, я нарешті вирішив застосувати метод пошуку спіралі, який я коротко описав у первісному запитанні.

Обґрунтування полягає в тому , що метод Byte56 потребує спеціального лікування для певних умов, тоді як пошук спіралі ні, і він кодує дуже компактно . Крім того, підступний пошук підкреслює пошук більш близького місця до транспортного засобу для розміщення етикетки, що IMO є головним фактором для того, щоб зробити карту читабельною.

Однак, будь ласка, продовжуйте підтримувати його відповідь, оскільки це не тільки корисно, але й дуже добре написано!

Ось скріншот результату, досягнутого за допомогою спірального коду:

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

І ось код, який, хоча і не є самостійним, - дає уявлення про те, наскільки простою є реалізація:

def place_tags(self):
    for tag in self.tags:
        start_angle = tag.angle
        while not tag.place() or is_colliding(tag):  #See note n.1
            tag.angle = (tag.angle + angle_step) % 360
            if tag.angle == start_angle:
                tag.radius += radius_step
        tag.connector.update()                       #See note n.2

Примітка 1 - tag.place()повертає значення True, якщо тег знаходиться повністю на видимій частині екрана / радара. Отже, цей рядок звучить як "продовжуйте циклічно, якщо тег знаходиться поза радаром або він перекриває щось інше ..."

Примітка 2 - tag.connector.updateце метод, який малює лінію, що з'єднує піктограму літака до мітки / тегу з текстовою інформацією.


Молодці, це дуже компактно. Дякую за похвалу. Також дякую за повідомлення про те, що ви закінчили, що завжди корисно для людей, які шукають відповіді пізніше. З екрану екрану схоже, що це працює дуже добре! Не забудьте прийняти свою відповідь, адже це саме те, з чим ви закінчились.
MichaelHouse

@ Byte56 - Дякую за "ретро-хвалу";) Чекаю на вибір відповіді як прийняту, тому що я все ще хотів би також зашифрувати ваше рішення і порівняти результат. Для одного я підозрюю, що ваше рішення може призвести до більш тривалого, але й швидшого виконання коду. Крім того, я хотів би побачити, як вони порівнюють у дуже забитих повітряних просторах ... так що є ще ймовірність, що я можу випустити код з вашим алгоритмом. Слідкуйте за цим простором! ;)
mac

@ Byte56 - Я намагався реалізувати ваш алгоритм. Це працювало чудово та швидко при низькій щільності, але як тільки простір заполонилося, у мене виникли проблеми з прямим реалізацією, яка б керувала конкретними ситуаціями, в яких мітка повинна "перестрибувати" блок інших міток або потрапляти в пастку не- рухомі (тобто площини значок) спрайти. Я відзначаю цю відповідь як обрану тоді, але ще раз: велике спасибі за час та внесок! :)
мак
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.