Ефективне виявлення зіткнень на основі плитки для багатьох квадратів?


9

на даний момент я працюю над власною грою на основі плитки (думаю, Terraria, але менш фантастично (я думаю, це слово? Вибачте, якщо це не так)).

У будь-якому випадку, в даний час у мене працює система виявлення зіткнень (навіть для кутових випадків!), Що було для мене великим кроком. Є щось надзвичайно втішне в тому, як бачити спрайт не проходить через блок. Але тоді у мене виникла ідея зробити орієнтир. Погана ідея.

1000 квадратів, без проблем. 10 000 квадратів, на 3 символи, було на кшталт відсталим. 100 000 квадратів (дійсно величезна карта), для 3 символів було неможливо.

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

Ось мій алгоритм поки що, сміливо критикуйте.

foreach (Block in level)
{
    if (distance from block to player > a specified amount)
        ignore this block;
    else
    {
        get the intersection depth between the two bounding boxes
        if (depth of intersection != Zero-vector)
        {
            check y size vs x size
            resolve on smallest axis
        }
    }
}

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

Я думаю, можливо, використовуйте подвійний масив блоків (0,0) для (mapWidth, mapHeight) замість списку, обчислюючи небезпечну зону залежно від позиції людини, наприклад, якщо позиція гравця знаходиться на (10, 20) він буде виглядати від (0, 10) до (20, 30) тощо.

Будь-які думки та міркування є приголомшливими, дякую.


1
І ласкаво просимо в stackexchange! :-) Не забудьте прочитати FAQ, якщо ви не знаєте, як працює вся система забезпечення якості та репутації.
Девід Гувейя

Безумовно, ці плитки мають розмір більше 16 на 16 пікселів, а 1920 - 1080 - це 8100 плиток. Безумовно, ви знаєте, де знаходяться рухомі об'єкти, і ви можете перевірити лише плитки на сітці, які, можливо, знаходяться в межах (якщо одна - 160 * 160, а центр - у плитці (12,12), вам потрібно лише перевірити між плитками (6 , 6) та (18,18) для загальної кількості ~ 150 можливих плиток.). Напевно плитки під силою тяжіння тільки падають, і тому потрібно лише шукати наступну плитку під нею.
DampeS8N

Ви вважаєте, 16x16 занадто мало? Мені було б важко змінити розмір плитки, оскільки все, на що посилається плитка / висота, є статичною постійною. Все, що я мав би зробити, це збільшити їх у Paint.NET, що приємно, оскільки це додає більше деталей.
Росс

Ви б не хотіли поділитися кодом зіткнення? : /
ashes999

Відповіді:


7

Так, ти правильно думаєш. Ви повинні використовувати 2D масив плиток, оскільки це дозволяє індексувати плитки за позицією.

Block[,] level = new Block[width, height];

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

int leftTile = (int)Math.Floor((float)characterBounds.Left / tileWidth);
int rightTile = (int)Math.Ceiling(((float)characterBounds.Right / tileWidth)) - 1;
int topTile = (int)Math.Floor((float)characterBounds.Top / tileHeight);
int bottomTile = (int)Math.Ceiling(((float)characterBounds.Bottom / tileHeight)) - 1;

for (int y = topTile; y <= bottomTile; ++y)
{
    for (int x = leftTile; x <= rightTile; ++x)
    {
        // Handle collisions with the tile level[x,y] just like you were doing!
    }
}

Перевірте зразок, якщо у вас все ще виникають проблеми.


Це дуже приємний маленький алгоритм, я навіть не чув про зразок Платформера (я повинен був, але я претендую на незнання). Дякую!
Росс

@Ross Дійсно? Ви здивуєтеся, наскільки подібне ваше рішення до зразка. :-) Мінус частини списку, все інше майже однаково (перетинати обмежувальні поля, отримати глибину перетину, вирішувати на найменшій осі).
Девід Гувейя

1
О, чоловіче, я просто подивився на це. >. <Бажаю, я знав це 2 дні тому !! Ну, я новачок у XNA, але я поглибився двовимірною графікою (просто OpenGL, не дуже багато ігрового програмування). Я думаю, я повинен перевірити більше ресурсів, перш ніж перейти до кодування.
Росс

1

Гадаю, моя відповідь була б вашою відповіддю! ;-)

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

Можливо, перевірте підручник щодо зіткнень на riemers.net, якщо ви ще цього не зробили.


Я чув про Рімера, але ніколи не замислювався, дякую!
Росс

1

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

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

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

Тепер, якщо ви не хочете навіть дивитися на плитки з екрану, ви можете зробити щось на кшталт

public bool CheckCollision(myPosition) {
    if(quadNodes.Count > 0) {
        // This is not a leaf, keep checking
        foreach(Quad node in quadNodes) {
            if(node.Position is insideViewport && nearPlayer)
                // Continue recursion
            }
        }
    }
    else {
        // This is a leaf, do checks
        foreach(Tile tile in tileList) {
            if(collision)
                return true;
        }
        return false;
    }
}

Гм, я чув про Octrees в 3D-виявленні зіткнень, але ніколи не бачив передової структури даних, яка використовується для двовимірного виявлення зіткнень. Дуже дякую!
Росс

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

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

1
Ось, що я збирався запропонувати :) Сітку потрібно використовувати для обробки зіткнень з місцевістю, тоді як квадратик може бути використаний для обробки міжоб'єктних зіткнень.
Девід Гувейя

Правда. Зараз кожен обмежувальний ящик має 2 ^ розміри потужності. Це значно полегшує мене для виявлення зіткнень. Сітка зараз відповідає моїм потребам.
Росс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.