Зіткнення на основі чотирьох дерев / сітки - введення логіки в дію


13

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

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

У мене є кілька можливих методів в голові, але я не впевнений, що найкраще

Загальний тест на зіткнення - немає оптимізації

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }

зберігати сусідів (метод 1) Але що робити, якщо ми хочемо оптимізувати зіткнення лише для перевірки об'єктів, які знаходяться поруч. Чи все-таки ми пробігаємося через усі об’єкти чи створюємо масив з близькими об’єктами, щоб перевірити їх?

var objects:Array = new Array();
    var neighbours:Array = new Array();

    for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){

               neighbours.push(objectA, objectB);
              }
        }

    }

    //somewhere else
    for(i:int = 0; i < neighbours.length; i++){
        //only check neighbours

        for(j:int = i + 1; j < neighbours.length; j++){

            if(objectA.collidesWith(objectB){

               //handle collision logic
              }
        }
    }

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

for(var i:int = 0; i < objects.length; i++){
        //find object A
        var objectA = objects[i];

        for(var j:int = i + 1; j < objects.length; j++){
            //find object B
            var objectB = objects[j];

            if(objectA.isNear(objectB){
               //they are near - check collision!
               if(objectA.collidesWith(objectB){

               //handle collision logic
              }
            }
        }

    }

Зберігання об’єктів у даних даних плиток (метод 3) Використання системи на основі плитки дозволяє використовувати інший варіант; Зберігайте об’єкти, які знаходяться на певній плитці, у самих даних плиток. Перевірте, на якій плитці знаходиться об'єкт оточуючих плиток, які містять об'єкти, з якими він міг зіткнутися:

var ObjectA;

for(var i:int = 0; i < 4; i ++){
//check 4 surrounding tiles from object A

   if(Object.currentTile + surroundingTile[i] CONTAINS collidable object){
   //check collision!
    if(objectA.collidesWith(surroundingTile.object){

    //handle collision logic
     }

}
}

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

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

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

Або більш ефективно знайти сусідів, таким чином, не потрібно перебирати стільки предметів, щоб перевірити зіткнення?

Продовжуйте змінювати дані на кожній плитці також дуже інтенсивно, тому я не впевнений, чи це гарна ідея.

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

Прошу вибачення за довгий пост, завжди маючи проблеми з поясненням себе!


що це за мова? Javascript?
Кайл С

2
Actioncript 3, але мова є досить непотрібною. Просто хочу дізнатися, який найкращий спосіб впоратись із цією структурою :)
omgnoseat

Відповіді:


8

Потрібно зменшити кількість фактичних перевірок на зіткнення. Так очевидно, я знаю. Отже, докладно зупинимось на цьому:

Ваш перший алгоритм перевірки зіткнення без будь-якої оптимізації: скільки перевірок буде зроблено за один пробіг? Якщо n - кількість об'єктів, це буде навколо n * (n-1) перевірок. Для багатьох об’єктів (> 100) це буде жахливо повільно.

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

Вам потрібно розділити свій ігровий простір. Скажімо, ваш ігровий простір шириною 400x400 пікселів. Ви можете розділити це на 4 підпростори розміром 200x200 і перевірити для кожного об'єкта, до якого з підпросторів він належить (один об'єкт може знаходитись у більш ніж одному підпросторі). Тоді вам потрібно буде лише перевірити, чи об’єкти в кожному підпросторі стикаються з іншим об’єктом у тому ж підпросторі.

Таким чином, час виконання буде складати: n для складання списку 4 підпробірів + (n / 4) * ((n-1) / 4), що набагато краще, ніж попередні витрати на виконання. Це можна скоротити, зменшивши пробіли менше (наприклад, 50x50).

Отже наш алгоритм виглядає приблизно так:

for each (object in objects)
  check into which subspace the object belongs

for each (subspace in subspaces)
  for each (object in subspace)
    check object for collision with other objects in same subspace

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

Ми можемо зробити останній крок для подальшої оптимізації цього за допомогою квадрату. Нехай k - кількість проміжків. Для побудови списків об'єктів пробілів ми робимо k * n перевірки, що призводить до безлічі перевірок, якщо ваш ігровий світ стає більшим.

Щоб зменшити цю вартість, ми використовуємо квадратик. Квадрат - це ще один спосіб розділити наш ігровий простір. Замість того, щоб розділити наш простір 400x400 на 64 підпростори 50x50 та перевірити для кожного об'єкта, у якому з 64 підпросторів він зараз є, ігровий простір розділиться на 4 підпростори, наполовину розміру ігрового простору (200x200), які в свою чергу поділяються на менші підпростори (100х100), які, в свою чергу, знову діляться на 50х50 підпростори. Це наше квадри.

Тепер, якщо ми хочемо дізнатися, до якого підпростору 50x50 належить об’єкт, ми перевіримо, до якого з підпросторів 200x200 він належить. Потім ми піднімаємося на один рівень глибше в нашому квадраті і перевіряємо 4 4-х підпростори, які знаходяться всередині щойно знайденого підпростору 200х200. Це повторюється, поки ми не дізнаємося, до якого підпростору 50х50 належить об’єкт. То скільки зараз перевірок було потрібно? 4 для кожного рівня квадрату (Пам'ятайте, об'єкт може бути на краю двох підпросторів). Тому нам потрібні 4 * 3 перевірки, щоб призначити об’єкт правильному підпростору 50x50. Набагато краще, ніж 64 чеки.

Отже, нашому алгоритму квадратури потрібно 4 * 3 * n перевірки для складання списків підпростору, а потім щось на зразок k * (n / k) перевіряє, щоб перевірити зіткнення кожного об'єкта.


Це дуже чіткий і зрозумілий awnser, дякую багато! Як обробляється, на якому підпросторі знаходиться об’єкт? Чи підпростор обробляється як об’єкт, де об'єкт на ньому зберігається як масиви і перевіряється один проти одного? Або ви просто перевіряєте, для якого підпростору це знаходиться, перевіряючи X і Y всередині основного циклу? Здається, що весь час створювати та змінювати масиви є досить неефективним. Тому я хочу бути впевненим, що я використовую відповідний метод :) Я також можу побачити проблему, яка виникає, коли об'єкти різняться за розміром. Я думаю, що найменший підпростір повинен бути приблизно таким же великим, як і найбільший об’єкт?
omgnoseat

Я радий допомогти :) Підпростір буде об’єктом, який підтримує 4 підпростори, які він містить. Найменший підпростір відрізняється тим, що містить не більше підпросторів, а список ваших об'єктів. Враховуючи вартість оновлення: дерево підпростору будується один раз при запуску гри. У кожному циклі основного циклу списки об'єктів у найменших підпросторах очищаються та заповнюються знову. Тому потрібно лише додавати / видаляти список. Жодних масивів не потрібно змінювати. Найменший підпростір повинен бути достатньо великим, щоб містити кілька ігрових об’єктів. Наскільки велика, залежить від вашої гри, і її можна буде переглядати пізніше.
Стівен

Дякуємо, забув, що ми також будемо перевіряти зіткнення між фактичними об’єктами в найменшому підпросторі, має сенс, що він повинен мати можливість утримувати кілька об'єктів зараз. Я почав тестувати, і це добре виходить. Найбільша проблема, яку я маю, виявляє, до якого підпростору належать об'єкти. Я продовжую перебирати через підпростори значень X і Y, це відповідний метод?
omgnoseat

Як ви обробляєте об'єкти, що поширюються на більш ніж один підпростір (тобто місце розташування на межі або поблизу)?
mghicks

Просте: я ні. Об'єкти можуть бути частиною декількох підпросторів, якщо вони знаходяться на межі кожного підпростору. Таким чином, один об’єкт може бути частиною до 4 підпросторів. Але це меншість.
Стівен
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.