Як постійно ефективно знаходити всі сутності в радіусі?


14

У мене дуже велика кількість утворень (одиниць). На кожному кроці кожна одиниця повинна знати положення всіх одиниць біля неї (відстань менше, ніж задана константа R ). Всі одиниці рухаються безперервно. Це в 3D.

В середньому буде близько 1% від загальної кількості одиниць біля будь-якої іншої одиниці із заданими обмеженнями.

Як я можу це зробити ефективно, без грубої сили?


7
Вам потрібна якась система просторових перегородок: en.wikipedia.org/wiki/Space_partitioning
Тетрад

Відповіді:


15

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

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

Нарешті, не ігноруйте простоту рішення Grid. Багато людей ігнорують, що простої сітки іноді може бути достатньо (і навіть ефективніше) для їх проблем, а натомість переходять до більш складного рішення.

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

Скажімо, ваш світ коливався від (-1000, -1000) до (1000, 1000) у площині XZ. Ви можете, наприклад, розділити його на 10x10 сітку, наприклад:

var grid = new List<Entity>[10, 10];

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

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

Редагувати Знайдено це на іншому форумі, і це може допомогти вам у прийнятті рішення:

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

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


Дякую за детальну відповідь. Так, здається, просте рішення Grid для мене досить добре.
квітня

0

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

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


0

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

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

По-перше: двовимірний затискаючий короб.

// returns true if the circle supplied is completely OUTSIDE the bounding box, rectClip
bool canTrivialRejectCircle(Vertex2D& vCentre, WorldUnit radius, Rect& rectClip) {
  if (vCentre.x + radius < rectClip.l ||
    vCentre.x - radius > rectClip.r ||
    vCentre.y + radius < rectClip.b ||
    vCentre.y - radius > rectClip.t)
    return true;
  else
    return false;
}

У порівнянні з чимось подібним (у 3D):

BOOL bSphereTest(CObject3D* obj1, CObject3D* obj2 )
{
  D3DVECTOR relPos = obj1->prPosition - obj2->prPosition;
  float dist = relPos.x * relPos.x + relPos.y * relPos.y + relPos.z * relPos.z;
  float minDist = obj1->fRadius + obj2->fRadius;
  return dist <= minDist * minDist;
}.

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

Ця стаття підтримує поле для тривіального відхилення. http://www.h3xed.com/programming/bounding-box-vs-bounding-circle-collision-detection-performance-as3

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

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

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


1
ОП спеціально заявила, що хоче уникнути грубого підходу, саме це ви описуєте у своєму першому абзаці. Крім того, як ви вважаєте, що чек з обмежувальним вікном дешевший, ніж перевірка обмежувальної сфери ?! Це просто неправильно.
нотлеш

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

Посилання на GDnet розірвано, але тест канонічної сфери дуже простий, дуже дешевий і не розгалужується:inside = (dot(p-p0, p-p0) <= r*r)
Ларс Віклунд

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

1
@Ciaran Чесно кажучи, ця стаття здається дуже поганою. Перш за все, він не робить тести з реалістичними даними, а скоріше використовує однакові значення. Не те, що ви зіткнетесь у реальному сценарії. І ні, згідно зі статтею ВВ відбувається швидше лише тоді, коли немає зіткнення (наприклад, перевірка виходить з ладу при першій ifзаяві). Також не дуже реалістично. Але цілком чесно, якщо ви починаєте оптимізувати такі речі, то ви точно починаєте не в тому місці.
bummzack

0

Я маю зробити це відповіддю, тому що я не маю балів коментувати чи виступати. Для 99% людей, які задають це питання, обмежувальний ящик - це рішення, як описав Ciaran. Складеною мовою він відкине 100 000 непотрібних одиниць у мить ока. Є багато накладних витрат, пов'язаних з рішеннями, що не стосуються грубої сили; з меншими числами (скажімо, менше 1000) вони будуть дорожчі за час обробки, ніж перевірка грубої сили. І вони займуть набагато більше часу на програмування.

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

Також зауважу, що перевірка обмежувальної сфери потребує множення там, де цього не має обмежувальна рамка. Множення за своєю природою займає в кілька разів більше часу, ніж додавання та порівняння. Має бути деяка комбінація мови, ОС та обладнання, де перевірка сфери буде швидшою, ніж перевірка поля, але в більшості місць і разів перевірка поля повинна бути швидшою, навіть якщо сфера відхиляє кілька невідповідних одиниць поле приймає. (І там, де сфера швидша, новий випуск компілятора / інтерпретатора / оптимізатора дуже ймовірно, що це змінить.)


Хоча у вашій відповіді немає нічого поганого, ви не відповідаєте на питання. Було спеціально запропоновано підхід "non bruteforce". Крім того, вам здається, що ви повторюєте те, що вже писав Ciaran, і ми мали тривалу дискусію щодо коментарів щодо AABB vs. Різниця у виконанні просто не має значення. Краще вибирайте обмежувальний об'єм, який підходить більшості ваших кандидатів на зіткнення, оскільки це зменшить кількість фактичних вузькофазних випробувань .. що матиме більший вплив на загальну продуктивність.
bummzack
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.