Як швидко обчислити зору зору у двовимірній грі?


24

У мене є матриця плиток, на деяких із цих плиток є предмети. Я хочу обчислити, які плитки видно гравцеві, а які ні, і мені це потрібно зробити досить ефективно (щоб це було досить швидко обчислити, навіть якщо у мене є великі матриці (100x100) і багато об'єктів).

Я намагався зробити це за лінійним алгоритмом Брезена , але це було повільно. Крім того, це дало мені деякі помилки:

----XXX-        ----X**-     ----XXX-
-@------        -@------     -@------
----XXX-        ----X**-     ----XXX-
(raw version)   (Besenham)   (correct, since tunnel walls are 
                              still visible at distance)

(@ is the player, X is obstacle, * is invisible, - is visible)

Я впевнений, що це можна зробити - адже у нас є NetHack, Zangband, і всі вони якось вирішили цю проблему :)

Який алгоритм ви можете порадити для цього?


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


1
Ну, моя помилка, NetHack не возився з оглядом :)
Rogach

Деякі старі ідеї можна знайти на веб- сайті fadden.com/tech/fast-los.html , хоча це підтягується до тих часів, коли процесори були досить повільними, а обчислення з плаваючою точкою було найкраще уникати.
вівторок

Відповіді:


10

Ваше визначення видимого таке:

плитка видно, коли принаймні частина (наприклад, кут) плитки може бути з'єднана з центром плитки гравця прямою лінією, яка не перетинає жодну перешкоду

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

  1. Вкажіть рівень точності, який ви хочете надати алгоритму. Це буде кількість променів, які ви будете відстежувати.
  2. Розділіть повне коло 360 градусів на вибрану точність, щоб знати, скільки градусів обертається між кожним променем.
  3. Починаючи з 0 градусів і збільшуючи на величину, визначену на кроці 2, створіть промінь із початком у центрі плитки гравця та напрям, визначений поточним кутом.
  4. Для кожного променя, починаючи з плитки гравця, рухайтеся по напрямку променя, поки не потрапите на плитку перешкод. Додайте цю плитку до списку видимих ​​плиток та перейдіть до наступного променя. Ви також можете додати максимальну відстань, щоб "відмовитися", якщо зіткнення не знайдено.

Ось малюнок, що показує 3 приклади променя. Плитки більш темного кольору - це "результат" кожного променя, тобто там, де сталося зіткнення. Вам потрібно буде повторити це по колу:

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

Налаштуйте максимальну відстань і кількість променів для виконання. Занадто мало, і ви будете сумувати за плитками, занадто багато, і ваша продуктивність постраждає. Крім того, чим далі повинні проїжджати промені, тим більша буде "помилка" і тим більше точності вам знадобиться.

Редагувати

Перевірте наступний посібник з радіомовлення, зокрема крок 3 та крок 4, щоб допомогти вам реалізувати біт перетину алгоритму:

http://www.permadi.com/tutorial/raycast/rayc7.html


Чи повинен я просто "ходити" по кожному промені на певній відстані (скажімо, 0,3 бала) або мені потрібно запускати щось на зразок алгоритму Бесенхема на кожному промені?
Рогач

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

1
Алгоритм хороший, але для коректної роботи з довгими 1-плитними тунелями знадобиться величезна кількість променів.
HolyBlackCat

@HolyBlackCat - це буде лише в тому випадку, якщо ви направляєте промені під рівними кутами в усі сторони. Але ви можете уникнути надсилання більшості цих променів і лише кидати їх на кінці рядка у вашій сцені. Ось гарне пояснення: redblobgames.com/articles/visibility
Рогач

8

Я б краще кидав тіньові промені, а не лінії зору.

Скажімо, це ваша область перегляду (потенційно видима область)

######################
#####.............####
###................###
##..................##
#....................#
#....................#
#..........@.........#
#....................#
#....................#
##..................##
###................###
#####.............####
######################

Блоки не відображаються під час. видно

Поставимо деяку перешкоду X:

######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXXX...........#
##..................##
###....X...........###
#####.............####
######################

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

######################
#####.............####
###................###
##.....X.....XXX....##
#......X.......X.....#
#...X.XX.............#
#...X......@.........#
#...X..........X.....#
#...XXXXX*...........#
##......##..........##
###....*#..........###
#####.###.........####
######################

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

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

Ви можете використовувати своєрідний алгоритм "Військово-морські битви", щоб перевірити блок Xs відразу (в основному шукайте суміжний X, який знаходиться в напрямку, який може зробити тіньовий конус ширшим)

[EDIT]

Для правильного відтінку тіні потрібні два промені, а оскільки плитка прямокутна, багато припущень можна зробити, використовуючи наявні симетрії.

Координати променя можна обчислити за допомогою простого перегородки навколо плитки перешкод:

Приклад розділення простору

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

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

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

вибрати найближчу перешкоду

Якщо жовта плитка є перешкодою, плитка стає новою червоною плиткою.

Тепер розглянемо верхній край конуса:

кандидатські плитки

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

Зелена плитка є кандидатом лише в тому випадку, якщо спостерігач знаходиться над помаранчевою лінією, яка наступна:

розширена перевірка

Те саме означає і для іншого променя, і для інших позицій спостерігача щодо червоної перешкоди.

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


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

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

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

@DavidGouveia - які великі проблеми?
Рогач

@DavidGouveia - Я вже намагався підходити з тіньовими "шишками", і це було дуже неефективно. Що стосується точності променів видимості - ~ 5500 променів достатньо, щоб побачити на 20 плиток стіни в кожен бік, якщо ви стоїте прямо біля неї, а відстань, на якій видно лише одну плитку, набагато більше. А що стосується пропускання деяких плиток на більшій відстані - ну, не всі мають зору префекта, так?
Рогач

8

Проблему, яку ви намагаєтеся вирішити, іноді називають полем зору, коротко кажучи FOV. Як ви згадали зловмисників як приклади, вам слід поглянути на те, що вікі RogueBasin можуть сказати про цю тему (є навіть посилання на реалізації): http://www.roguebasin.com/index.php?title=Field_of_Vision

Існує досить багато різних алгоритмів з різними плюсами та мінусами - дуже зручне порівняння доступне також у RogueBasin: http://www.roguebasin.com/index.php?title=Comparative_study_of_field_of_view_algorithms_for_2D_grid_based_worlds


Дійсно хороший та повний підсумок!
Рогач

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

Посилання у відповідь тепер переходить на домашню сторінку сайту - roguebasin.com/index.php?title=Category:FOV виявляється розумною відповідністю.
вівторок


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