Карта з 20 мільйонними плитками змушує гру закінчуватися пам'яттю, як я її уникаю?


11

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

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

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

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


8
Завантажуйте лише області навколо гравців.
MichaelHouse

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

Що робить метод завантаження? Поштовий індекс, ви використовуєте конвеєр вмісту? Чи завантажує він текстури в пам'ять або просто метадані? Які метадані? Типи вмісту XNA, як правило, посилаються на керовані матеріали DirectX, тому керованих об'єктів дуже мало, але на стороні некерованого може бути безліч речей, яких ви не бачите, якщо ви також не використовуєте DirectX-профілер. stackoverflow.com/questions/3050188 / ...
Oskar Duveborn

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

3
Чому ви думаєте, що маєте вільну пам’ять? У вас працює 32-бітний процес у 64-бітній ОС?
Dalin Seivewright

Відповіді:


58

додаток виходить з ладу, коли він досягає 1,5 Гбіт.

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

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

Візьмемо для прикладу Terraria. Найменший у світі Terraria займає 4200x1200 плитки, що становить 5 мільйонів плиток. Тепер, скільки пам'яті потрібно, щоб представити цей світ?

Ну, у кожної плитки є шар переднього плану, фоновий шар (фонові стіни), «шар дроту», куди ходять дроти, і «меблевий шар», куди йдуть предмети меблів. Скільки пам’яті займає кожна плитка? Знову ж таки, ми говоримо просто концептуально, а не візуально.

Плитку переднього плану легко можна зберігати в непідписаному шрифті. Існує не більше 65536 типів плитки переднього плану, тому немає сенсу використовувати більше пам'яті, ніж ця. Фонові плитки можуть легко знаходитися в неподписаному байті, оскільки існує менше 256 різних типів фонових плиток. Шар дроту суто двійковий: або плитка має дріт у ній, або її немає. Так це один біт на плитку. І шар меблів знову може бути байтом без підпису, залежно від кількості можливих різних предметів меблів.

Загальний об'єм пам'яті на плитку: 2 байти + 1 байт + 1 біт + 1 байт: 4 байти + 1 біт. Таким чином, загальний розмір для невеликої карти Terraria становить 20790000 байт, або ~ 20 МБ. (зауважте: ці розрахунки базуються на Terraria 1.1. З тих пір гра значно розширилася, але навіть сучасна Terraria могла вміститися в межах 8 байтів за місце розташування плитки, або ~ 40 Мб. Досі цілком терпимо).

Ви ніколи не повинні зберігати це представлення як масиви класів C #. Вони повинні бути масивами цілих чисел чи чимось подібним. AC # struct також буде працювати.

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

Що вам потрібно зробити, це перетворити видиму частину вашої концептуальної карти у фактичну плитку спрайту XNA спрайту. Ви не повинні намагатися перетворити всю річ відразу . Кожна плитка, яку ви зберігаєте, повинна бути просто покажчиком, який говорить про те, що "Я тип плитки X", де X - ціле число. Ви використовуєте цей цілий індекс, щоб отримати спрайт, який ви використовуєте для його відображення. І ви використовуєте спрайтові листи XNA, щоб зробити це швидше, ніж просто малювати окремі квадратики.

Тепер видиму область плиток потрібно розбити на різні шматки, так що ви не будуєте постійно спрайтові листи, коли камера рухається. Отже, у вас можуть бути 64x64 шматки світу як аркуші спрайту. Незалежно від того, звідки в поточному положенні камери гравця видно 64x64 шматки світу, - це шматки, які ви малюєте. Будь-які інші шматки навіть не мають спрайтових аркушів; якщо шматок впаде з екрана, ви викидаєте цей аркуш (зверніть увагу: ви насправді не видаляєте його; ви тримаєте його навколо і поважаєте його за новий шматок, який може стати видимим пізніше).

Мені хотілося б, щоб вся карта була оброблена принаймні на серверній програмі (і по можливості клієнту).

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


4
+1 Відмінна відповідь. Необхідно проголосувати більше, ніж за мою.
MichaelHouse

22

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

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

Ви можете знайти більше подібних ідей тут:

Як вони це зробили: мільйони плиток у Terraria

"Зонування" районів на великій карті плитки, а також підземелля


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

6
Що містить кожна плитка? Скільки пам'яті використовується на плитку? Навіть по 10 байтів кожен у вас складає лише 190 мегабайт. Сервер не повинен завантажувати текстуру або будь-яку іншу інформацію, яка не потрібна. Якщо вам потрібне моделювання на 20 мільйонів плиток, вам потрібно зняти якомога більше цих плиток, щоб сформувати набір моделювання, який містить лише інформацію, необхідну для ваших ланцюгових реакцій.
MichaelHouse

5

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

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

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

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


3

Ефективним способом, який я використовував у грі XNA, було:

  • використовуйте spritesheets, а не зображення для кожної плитки / об'єкта, а просто завантажте те, що вам потрібно на цій карті;
  • розділіть карту на більш дрібні частини, скажіть, куски карт розміром 500 х 200 плиток, якщо ваша карта в 4 рази більша за цей розмір, або щось, що ви можете вирішити;
  • завантажте цей шматок у пам'ять і намалюйте лише видимі частини карти (скажімо, видимі плитки плюс 1 плитка для кожного напрямку руху гравця) у екрані екрану;
  • очистити вікно перегляду та скопіювати пікселі з буфера (це називається подвійним буфером і згладжує мовімент);
  • коли ти рухаєшся, відстежуй кути карти та завантажуй наступний шматок, коли він наблизиться до нього та додаєш до поточного;
  • як тільки гравець буде повністю на цій новій карті, ви можете вивантажити останню.

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

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

Вам доведеться щоразу циклічати лише відповідні частини цієї карти та робити лише те, що потрібно. Таким чином ви значно поліпшите продуктивність.

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

Порада тут полягає в тому, що я жодним чином не є експертом у розробниках ігор, і моя гра була зроблена для остаточного проекту мого випускного (який у мене був найкращий бал), тож я, можливо, не так прав у кожній точці, але я досліджував Інтернет та веб-сайти, такі як http://www.gamasutra.com та XNA-сайт творців XNA Creators.xna.com (в цей час http://create.msdn.com ), щоб зібрати знання та навички, і це працювало для мене чудово .


2

Або

  • придбайте більше пам’яті
  • представляють ваші структури даних більш компактно
  • не зберігайте всі дані в пам'яті одразу.

У мене 8 Gb таран на win7 64bit, і додаток виходить з ладу, коли він досягає 1,5 Гбіт. Тому насправді немає сенсу купувати більше барана, якщо я чогось не пропускаю.
user1306322

1
@ user1306322: Якщо у вас є 20 мільйонів плиток, і це займає близько 1,5 ГБ пам'яті, це означає, що ваші плитки мають приблизно 80 байт на плитку . Що саме ви зберігаєте в цих речах?
Нікол Болас

@NicolBolas, як я вже сказав, кілька входів, байтів і тексту2d. Крім того, вони не беруть 1,5 Гб, програма виходить з ладу, коли досягає цієї вартості пам'яті.
user1306322

2
@ user1306322: Це ще набагато більше, ніж потрібно. Наскільки великий texture2d? Чи є кожна плитка унікальною, чи вони діляться текстурами? Також де ви це сказали? Ви, звичайно, не ставили це у своєму питанні, де саме повинна бути інформація. Поясніть свої конкретні обставини, будь ласка.
Нікол Болас

@ user1306322: Чесно кажучи, я не дуже розумію, чому мою відповідь було оскаржено. Я перерахував три способи вирішення ситуацій, що знаходяться поза пам'яттю, - звичайно, це не означає, що всі вони обов'язково застосовуються. Але я все одно вважаю їх правильними. Я, мабуть, повинен був написати "розширити вашу пам'ять", а не "купити", щоб включити корпус віртуальних машин. Мене заперечують, бо моя відповідь була стислою замість багатослівної? Я думаю, що вони досить чіткі, щоб зрозуміти їх без подальшого пояснення ?!
Томас

1

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

У відповідь на ваше оновлення ви не можете виділити масиви на Int32.MaxValue розміром. Ви повинні розділити його на шматки. Ви можете навіть інкапсулювати його в клас обгортки, який відкриває фасад, схожий на масив:

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