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


12

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

Я працюю над проектом, де світ складається з великої кількості кубів (я б дав вам число, але це світи, визначені користувачем). Мій тест - це близько (48 х 32 х 48) блоків.

В основному ці блоки нічого не роблять самі по собі. Вони просто сидять там.

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

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

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

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

Однак я все ще збираюся з депресивними 2 кадрів в секунду.

Хтось має якісь ідеї щодо того, як я міг би зменшити це відставання?

До речі, я використовую XNA (C #), і так, це 3d.


Ви маєте на увазі подібність до Minecraft? Тобто, воксель?
Качка комуніста

4
Ви заглянули в октриси? en.wikipedia.org/wiki/Octree
bummzack

1
Ви спробували профілювати свою гру? Це може відображати деякі ключові сфери, де проводиться найбільше часу. Це може бути не те, що ти думаєш.
deceleratedcaviar

2
Замість того, щоб намалювати всі 6 граней кожного куба, ви могли намалювати лише обличчя, які ні з чим не контактують
Девід Ешмор

1
@David: Так, або він може спочатку перестати робити один дзвінок за куб, а потім піклуватися про окремі багатокутники.
Ольховський

Відповіді:


20

Схоже, ти хочеш дізнатися про Дерева!

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

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

Тепер, щоб визначити цю зону, нам потрібно віднести простір координат нашого гравця до координатного простору карти куба; тобто нам потрібно зіставити положення програвача з плаваючою комою на дискретний індекс багатовимірного масиву кубів (наприклад, нотація може бути world[31][31][31], тобто точна середина для багатовимірного масиву 64 * 64 * 64).

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

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

Питання: як ми це реалізуємо?

Для світу 64 * 64 * 64 уявіть, що він розбився на 8 * 8 * 8 зон . Це означає, що у вашому світі у вас буде 8 зон на вісь (X, Y, Z). Кожна з цих зон міститиме 8 кубів, які легко отримати за допомогою цього нового спрощеного індексу.

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

Тепер зменшіть масштаб із цього світу зон і розташуйте зони у великих суперзонах ; де кожна суперзона містить 2 * 2 * 2 регулярних зони . Оскільки ваш світ наразі містить 512 (8 * 8 * 8) зон , ми можемо розділити 8 * 8 * 8 зон на 64 (4 * 4 * 4) суперзони , розділивши 8 зон на 2 зони на суперзону . Застосовуючи ту саму логіку зверху, це дозволило б порушити максимальні ітерації від 512 до 8, щоб знайти суперзону ; а потім максимум 64, щоб знайти зону, що триває(всього макс. 72)! Ви можете бачити, як це економить уже багато ітерацій (262144: 72).

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

Наведена нижче діаграма повинна допомогти вам уявити концепцію. (зображення з Вікіпедії: жовтені ): Жовтень

Відмова:

В ідеальному режимі, як описано вище, де ваш світ вокселів вже розміщений у багатовимірному масиві фіксованого розміру, ви можете просто запитати позицію гравця, а потім індексувати навколишні блоки із вартістю O (1)! (Див. Пояснення Олховського) Але це стає складніше, коли ти починаєш враховувати, що твій світ рідко має фіксований розмір у воксельній грі; і може знадобитися ваша структура даних, щоб мати можливість завантажувати цілі суперзони з HDD в пам'ять. На відміну від багатовимірного масиву фіксованого розміру, дерева легко дозволяють це без зайвого часу, витраченого на комбінаційні алгоритми.


Я б сказав, що я це отримую, але, на жаль, не знаю. Колонки зіткнення блоків не рухаються, лише коробка зіткнення гравця. І в мене зараз є метод, який (не перебираючи всі блоки) повертає всі блоки, які знаходяться в радіусі 5 блоків гравця. Вибачте за клопоту, але будь-яке уточнення? До речі, для спрощення ви могли б припустити, що світ 64 x 64 x 64?
Джоель

І я все ще наближаюся до 5 кадрів в секунду :(
Джоель,

Я переформулював свою відповідь, повідомте мені, чи допомогло це.
уповільненийкавіар

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

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

13

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

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

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

Ви також можете поставити у свій код маркери подій PIX , які відображатимуться як кольорові регіони в показах PIX. Не існує офіційного інтерфейсу C # для цих функцій, але ця тема показує, як можна зробити інтерфейс C # самостійно.


2
+1, повністю згоден. Ви не повинні вивчати алгоритми, ви повинні вивчати профайл коду. Я здогадуюсь, що це нічого спільного з тим, що ви перебираєте блоки (це не так багато), це те, що ви робите з кожним блоком. Зрештою, так, вам потрібно мати кращий метод, ніж груба сила, але якщо ви не можете впоратися з простою ітерацією на кубах 48x32x48, вам потрібно переосмислити, що ви робите з кожним кубом, а не як ви займаєтеся циклами.
Тім Холт

4
@Tim: Якщо його плеєр не є достатньо великим, щоб він займав простір 48x32x48, він не повинен перебирати ніде поблизу стільки кубів. Якщо він перебирає 73000 кубів на кадр, то я можу вам сказати, не роблячи профілювання, що це варто йому виправити, якщо не з іншої причини, ніж навчитися уникати виконання десятків тисяч разів більше ітерацій, ніж необхідні. Це не те, що я б назвав мікро- чи передчасною оптимізацією.
Ольховський

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

Randomman159: Тоді вам потрібно лише протестувати проти навколишніх 27 кубів, щоб знайти зіткнення. Дивіться мою відповідь.
Ольховський

6

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

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

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

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

Як зазначали інші, вам потрібно профілювати свій код, щоб побачити, що насправді уповільнює вас.

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


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

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

1

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

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


-1

У мене була проблема з моїм воксельним двигуном.

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

BlockIndex = (x * WorldWidth * WorldHeight) + (z * WorldHeight) + y;

Тоді, якщо ви хочете побачити, чи існує блок:

Blocks[BlockIndex].Type > -1;

Або ви визначаєте, чи існує блок.


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